diff --git a/Engine/Binaries/ThirdParty/VisualStudio/VisualStudioSetupSDK.tps b/Engine/Binaries/ThirdParty/VisualStudio/VisualStudioSetupSDK.tps new file mode 100644 index 000000000000..7b0a13bba9c2 --- /dev/null +++ b/Engine/Binaries/ThirdParty/VisualStudio/VisualStudioSetupSDK.tps @@ -0,0 +1,11 @@ + + + Visual Studio Setup SDK + UE4/Dev-Build/Engine/Plugins/Developer/VisualStudioSourceCodeAccess/Source/ThirdParty/VisualStudioSetup/ + We use this to allow the editor to open the correct version of Visual Studio when you create a source file. + https://visualstudio.microsoft.com/license-terms/vs-setup-sdk/ + + P4 + + None + \ No newline at end of file diff --git a/Engine/Build/Android/Java/src/com/epicgames/ue4/SplashActivity.java b/Engine/Build/Android/Java/src/com/epicgames/ue4/SplashActivity.java index 397b666a8dcf..deb4f09e294f 100644 --- a/Engine/Build/Android/Java/src/com/epicgames/ue4/SplashActivity.java +++ b/Engine/Build/Android/Java/src/com/epicgames/ue4/SplashActivity.java @@ -77,8 +77,24 @@ public class SplashActivity extends Activity } } - // for now only allow on one device manufacturer - fix up later - if (!android.os.Build.MANUFACTURER.equals("HUAWEI")) + + // allow certain models for now to use full area around cutout + boolean BlockDisplayCutout = true; + if (android.os.Build.MANUFACTURER.equals("HUAWEI")) + { + BlockDisplayCutout = false; + } + else if (android.os.Build.MANUFACTURER.equals("samsung")) + { + String model = android.os.Build.MODEL; + if (model.startsWith("SM-G970") || model.startsWith("SM-G973") || model.startsWith("SM-G975") || + model.startsWith("SC-03L") || model.startsWith("SCV41") || model.startsWith("SC-04L") || model.startsWith("SCV42")) + { + BlockDisplayCutout = false; + } + } + + if (BlockDisplayCutout) { UseDisplayCutout = false; } diff --git a/Engine/Build/Commit.gitdeps.xml b/Engine/Build/Commit.gitdeps.xml index 509e8e630cfe..1d35b4a9ab84 100644 --- a/Engine/Build/Commit.gitdeps.xml +++ b/Engine/Build/Commit.gitdeps.xml @@ -9,7 +9,7 @@ - + @@ -2684,159 +2684,159 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -5937,7 +5937,7 @@ - + @@ -5989,7 +5989,7 @@ - + @@ -6069,20 +6069,20 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -6094,10 +6094,10 @@ - + - + @@ -8055,8 +8055,8 @@ - - + + @@ -18395,83 +18395,83 @@ - + - + - + - - - - + + + + - + - + - - - - + + + + - - - - + + + + - - + + - + - - - - + + + + - - + + - + - + - - - - + + + + - - - + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - + + - + - + @@ -18482,171 +18482,171 @@ - - - - + + + + - - - - + + + + - - + + - + - - + + - + - - + + - + - - - - + + + + - - + + - + - - - - + + + + - - - - + + + + - - + + - + - - - - + + + + - - + + - + - - - - + + + + - + - + - - + + - + - + - - + + - + - - + + - + - - - - + + + + - - + + - + - - - - + + + + - - - + + + - - - - + + + + - - + + - + - + - - - - + + + + - - + + - + - - + + - + - - - - + + + + - - - + + + - - - - + + + + - - - - + + + + - - - - + + + + - + - - - - + + + + - + @@ -28809,7 +28809,7 @@ - + @@ -30882,7 +30882,7 @@ - + @@ -33171,7 +33171,7 @@ - + @@ -34876,8 +34876,8 @@ - - + + @@ -35393,7 +35393,7 @@ - + @@ -35574,11 +35574,11 @@ - - - - - + + + + + @@ -36780,8 +36780,8 @@ - - + + @@ -37047,8 +37047,8 @@ - - + + @@ -37068,8 +37068,8 @@ - - + + @@ -37775,7 +37775,7 @@ - + @@ -37787,7 +37787,7 @@ - + @@ -38274,114 +38274,114 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -38450,57 +38450,57 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -38528,33 +38528,33 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -38582,297 +38582,297 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -39454,12 +39454,12 @@ - - - - - - + + + + + + @@ -39529,7 +39529,7 @@ - + @@ -39549,9 +39549,9 @@ - - - + + + @@ -41053,7 +41053,6 @@ - @@ -41067,6 +41066,7 @@ + @@ -41084,7 +41084,6 @@ - @@ -41109,7 +41108,6 @@ - @@ -41137,7 +41135,6 @@ - @@ -41146,15 +41143,15 @@ - - + + @@ -41164,9 +41161,8 @@ - + - @@ -41206,7 +41202,6 @@ - @@ -41218,6 +41213,7 @@ + @@ -41234,7 +41230,7 @@ - + @@ -41269,7 +41265,7 @@ - + @@ -41278,13 +41274,11 @@ - - @@ -41336,6 +41330,7 @@ + @@ -41347,7 +41342,6 @@ - @@ -41392,7 +41386,7 @@ - + @@ -41404,29 +41398,26 @@ - - - + - - + @@ -41439,10 +41430,10 @@ + - @@ -41453,9 +41444,11 @@ + + @@ -41465,20 +41458,21 @@ - - + - + + + @@ -41498,20 +41492,20 @@ - - + - + + @@ -41533,6 +41527,7 @@ + @@ -41550,7 +41545,7 @@ - + @@ -41567,7 +41562,7 @@ - + @@ -41608,13 +41603,12 @@ + - - - + @@ -41631,8 +41625,8 @@ + - @@ -41650,11 +41644,10 @@ - + - @@ -41682,6 +41675,7 @@ + @@ -41695,7 +41689,7 @@ - + @@ -41704,6 +41698,7 @@ + @@ -41727,7 +41722,7 @@ - + @@ -41750,7 +41745,7 @@ - + @@ -41758,13 +41753,12 @@ - + - - + @@ -41787,7 +41781,6 @@ - @@ -41844,13 +41837,13 @@ - + @@ -41861,13 +41854,14 @@ - - + - + + + @@ -41889,10 +41883,11 @@ + - + @@ -41916,7 +41911,6 @@ - @@ -41927,7 +41921,7 @@ - + @@ -41970,7 +41964,6 @@ - @@ -42006,7 +41999,7 @@ - + @@ -42035,7 +42028,6 @@ - @@ -42060,12 +42052,10 @@ - - @@ -42115,7 +42105,7 @@ - + @@ -42130,7 +42120,7 @@ - + @@ -42140,7 +42130,6 @@ - @@ -42160,7 +42149,7 @@ - + @@ -42168,12 +42157,13 @@ - + + @@ -42201,22 +42191,24 @@ + - + + - + @@ -42257,6 +42249,7 @@ + @@ -42265,17 +42258,17 @@ - + + - - + @@ -42285,6 +42278,7 @@ + @@ -42297,6 +42291,7 @@ + @@ -42312,16 +42307,14 @@ - + - - @@ -42337,8 +42330,6 @@ - - @@ -42357,7 +42348,6 @@ - @@ -42372,6 +42362,7 @@ + @@ -42414,13 +42405,11 @@ - + - - @@ -42445,6 +42434,7 @@ + @@ -42456,6 +42446,7 @@ + @@ -42467,6 +42458,7 @@ + @@ -42491,7 +42483,7 @@ - + @@ -42504,7 +42496,6 @@ - @@ -42517,6 +42508,7 @@ + @@ -42533,6 +42525,7 @@ + @@ -42545,7 +42538,6 @@ - @@ -42566,7 +42558,7 @@ - + @@ -42583,7 +42575,6 @@ - @@ -42596,7 +42587,6 @@ - @@ -42616,7 +42606,7 @@ - + @@ -42624,13 +42614,13 @@ - + - + @@ -42650,6 +42640,7 @@ + @@ -42661,6 +42652,7 @@ + @@ -42690,7 +42682,6 @@ - @@ -42709,8 +42700,8 @@ - + @@ -42721,6 +42712,7 @@ + @@ -42739,7 +42731,6 @@ - @@ -42748,11 +42739,9 @@ - - @@ -42763,11 +42752,12 @@ - + + @@ -42782,10 +42772,11 @@ - + + @@ -42797,7 +42788,7 @@ - + @@ -42811,7 +42802,6 @@ - @@ -42831,7 +42821,6 @@ - @@ -42873,7 +42862,6 @@ - @@ -42901,7 +42889,7 @@ - + @@ -42964,11 +42952,11 @@ - + - + @@ -42976,10 +42964,10 @@ + - @@ -42993,7 +42981,6 @@ - @@ -43031,6 +43018,7 @@ + @@ -43055,6 +43043,7 @@ + @@ -43082,7 +43071,7 @@ - + @@ -43091,7 +43080,6 @@ - @@ -43108,7 +43096,6 @@ - @@ -43144,7 +43131,7 @@ - + @@ -43152,13 +43139,12 @@ - - + @@ -43169,6 +43155,7 @@ + @@ -43177,7 +43164,7 @@ - + @@ -43209,6 +43196,7 @@ + @@ -43216,10 +43204,11 @@ - + + @@ -43232,7 +43221,6 @@ - @@ -43258,7 +43246,6 @@ - @@ -43280,7 +43267,6 @@ - @@ -43288,7 +43274,7 @@ - + @@ -43319,6 +43305,7 @@ + @@ -43326,7 +43313,7 @@ - + @@ -43345,7 +43332,7 @@ - + @@ -43362,7 +43349,6 @@ - @@ -43393,11 +43379,10 @@ - - + @@ -43408,16 +43393,18 @@ - - + + + + @@ -43452,7 +43439,7 @@ - + @@ -43469,9 +43456,9 @@ - + - + @@ -43480,12 +43467,12 @@ + - @@ -43515,7 +43502,7 @@ - + @@ -43544,7 +43531,7 @@ - + @@ -43571,13 +43558,13 @@ + - @@ -43619,7 +43606,7 @@ - + @@ -43644,17 +43631,18 @@ + - + - + @@ -43670,6 +43658,7 @@ + @@ -43681,6 +43670,7 @@ + @@ -43692,6 +43682,7 @@ + @@ -43708,6 +43699,7 @@ + @@ -43719,6 +43711,7 @@ + @@ -43773,6 +43766,7 @@ + @@ -43790,7 +43784,6 @@ - @@ -43799,6 +43792,7 @@ + @@ -43816,9 +43810,9 @@ - + @@ -43828,6 +43822,7 @@ + @@ -43846,7 +43841,6 @@ - @@ -43859,7 +43853,6 @@ - @@ -43868,7 +43861,6 @@ - @@ -43876,6 +43868,7 @@ + @@ -43897,17 +43890,16 @@ - - + - + @@ -43922,7 +43914,7 @@ - + @@ -43930,7 +43922,7 @@ - + @@ -43948,6 +43940,7 @@ + @@ -43957,10 +43950,10 @@ + - @@ -44008,9 +44001,9 @@ - + - + @@ -44035,15 +44028,15 @@ + - + - @@ -44056,7 +44049,6 @@ - @@ -44084,7 +44076,6 @@ - @@ -44098,7 +44089,7 @@ - + @@ -44119,6 +44110,7 @@ + @@ -44149,6 +44141,7 @@ + @@ -44171,7 +44164,6 @@ - @@ -44193,17 +44185,16 @@ - - + @@ -44216,6 +44207,7 @@ + @@ -44243,11 +44235,11 @@ - + @@ -44278,13 +44270,15 @@ + + - + @@ -44302,16 +44296,15 @@ - + - - + @@ -44326,6 +44319,7 @@ + @@ -44336,7 +44330,9 @@ + + @@ -44344,6 +44340,7 @@ + @@ -44365,13 +44362,14 @@ + - + @@ -44388,10 +44386,11 @@ + - + @@ -44405,14 +44404,14 @@ - + - + @@ -44471,7 +44470,7 @@ - + @@ -44510,10 +44509,9 @@ - - + @@ -44524,13 +44522,13 @@ + - @@ -44544,14 +44542,13 @@ - - + @@ -44577,6 +44574,7 @@ + @@ -44597,6 +44595,7 @@ + @@ -44637,6 +44636,7 @@ + @@ -44663,7 +44663,7 @@ - + @@ -44674,6 +44674,7 @@ + @@ -44697,9 +44698,8 @@ - + - @@ -44735,7 +44735,6 @@ - @@ -44757,6 +44756,7 @@ + @@ -44771,7 +44771,7 @@ - + @@ -44791,7 +44791,6 @@ - @@ -44820,7 +44819,7 @@ - + @@ -44828,6 +44827,7 @@ + @@ -44855,20 +44855,18 @@ - - + - @@ -44883,6 +44881,7 @@ + @@ -44901,7 +44900,6 @@ - @@ -44911,9 +44909,8 @@ - + - @@ -44938,8 +44935,9 @@ + - + @@ -44957,6 +44955,7 @@ + @@ -44970,7 +44969,6 @@ - @@ -44978,14 +44976,14 @@ - + - + @@ -45003,10 +45001,11 @@ + + - @@ -45022,7 +45021,7 @@ - + @@ -45039,7 +45038,6 @@ - @@ -45055,7 +45053,9 @@ + + @@ -45064,27 +45064,25 @@ - - + - - + + - + - @@ -45104,15 +45102,16 @@ + - + @@ -45130,12 +45129,11 @@ - + - @@ -45197,7 +45195,6 @@ - @@ -45222,7 +45219,7 @@ - + @@ -45243,7 +45240,7 @@ - + @@ -45256,7 +45253,6 @@ - @@ -45265,7 +45261,6 @@ - @@ -45276,6 +45271,7 @@ + @@ -45291,10 +45287,10 @@ - + @@ -45325,11 +45321,9 @@ - - - + @@ -45339,7 +45333,7 @@ - + @@ -45374,7 +45368,6 @@ - @@ -45391,15 +45384,15 @@ + - - + @@ -45411,15 +45404,15 @@ - - + + @@ -45427,7 +45420,7 @@ - + @@ -45443,6 +45436,7 @@ + @@ -45464,10 +45458,10 @@ - + @@ -45518,13 +45512,12 @@ - + - @@ -45539,6 +45532,7 @@ + @@ -45561,12 +45555,13 @@ - - + + + @@ -45576,8 +45571,7 @@ - - + @@ -45602,10 +45596,10 @@ - + - + @@ -45617,6 +45611,7 @@ + @@ -45624,12 +45619,12 @@ - + @@ -45674,7 +45669,6 @@ - @@ -45689,6 +45683,7 @@ + @@ -45696,14 +45691,14 @@ - + - + - + @@ -45729,7 +45724,7 @@ - + @@ -45757,10 +45752,9 @@ - - + @@ -45785,7 +45779,6 @@ - @@ -45804,6 +45797,7 @@ + @@ -45820,7 +45814,6 @@ - @@ -45853,7 +45846,6 @@ - @@ -45866,11 +45858,13 @@ + + @@ -45883,6 +45877,7 @@ + @@ -45891,7 +45886,6 @@ - @@ -45908,7 +45902,7 @@ - + @@ -45924,15 +45918,16 @@ - + + - + @@ -45951,6 +45946,7 @@ + @@ -45967,7 +45963,6 @@ - @@ -45980,7 +45975,7 @@ - + @@ -46006,9 +46001,8 @@ - - - + + @@ -46019,7 +46013,7 @@ - + @@ -46043,7 +46037,6 @@ - @@ -46060,11 +46053,11 @@ - + - + @@ -46074,12 +46067,12 @@ - + - + @@ -46098,30 +46091,26 @@ - + - - - - @@ -46132,11 +46121,11 @@ + - @@ -46145,7 +46134,6 @@ - @@ -46215,7 +46203,6 @@ - @@ -46232,10 +46219,9 @@ - + - @@ -46251,15 +46237,16 @@ - - + + + - + @@ -46279,6 +46266,7 @@ + @@ -46302,18 +46290,18 @@ - + - + @@ -46321,7 +46309,7 @@ - + @@ -46333,11 +46321,12 @@ + - + @@ -46376,11 +46365,11 @@ + - + - @@ -46397,6 +46386,7 @@ + @@ -46404,6 +46394,7 @@ + @@ -46412,7 +46403,6 @@ - @@ -46439,9 +46429,11 @@ + + @@ -46460,11 +46452,15 @@ + + + + @@ -46473,6 +46469,7 @@ + @@ -46518,6 +46515,7 @@ + @@ -46533,10 +46531,13 @@ + + + @@ -46550,11 +46551,13 @@ + + @@ -46583,7 +46586,7 @@ - + @@ -46617,8 +46620,6 @@ - - @@ -46643,6 +46644,7 @@ + @@ -46651,7 +46653,7 @@ - + @@ -46659,6 +46661,7 @@ + @@ -46677,6 +46680,7 @@ + @@ -46684,17 +46688,17 @@ + - - + @@ -46703,6 +46707,7 @@ + @@ -46715,11 +46720,10 @@ - - + @@ -46737,7 +46741,7 @@ - + @@ -46765,7 +46769,6 @@ - @@ -46778,12 +46781,11 @@ - - + @@ -46841,7 +46843,6 @@ - @@ -46860,6 +46861,7 @@ + @@ -46879,7 +46881,6 @@ - @@ -46920,7 +46921,6 @@ - @@ -46950,6 +46950,7 @@ + @@ -46958,10 +46959,8 @@ - - @@ -46971,11 +46970,10 @@ - - + @@ -46989,13 +46987,14 @@ + - + @@ -47010,6 +47009,7 @@ + @@ -47047,7 +47047,6 @@ - @@ -47055,10 +47054,9 @@ - + - @@ -47070,6 +47068,7 @@ + @@ -47086,12 +47085,12 @@ + - @@ -47110,6 +47109,7 @@ + @@ -47129,7 +47129,7 @@ - + @@ -47164,12 +47164,12 @@ + - @@ -47210,7 +47210,6 @@ - @@ -47226,13 +47225,13 @@ + - @@ -47241,7 +47240,7 @@ - + @@ -47262,6 +47261,7 @@ + @@ -47276,10 +47276,9 @@ - - + @@ -47321,13 +47320,13 @@ - - + + @@ -47336,6 +47335,7 @@ + @@ -47364,12 +47364,10 @@ - - @@ -47396,13 +47394,11 @@ - - @@ -47424,6 +47420,7 @@ + @@ -47445,6 +47442,7 @@ + @@ -47465,10 +47463,10 @@ - + - + @@ -47484,8 +47482,9 @@ + - + @@ -47509,12 +47508,14 @@ + + @@ -47529,6 +47530,7 @@ + @@ -47544,7 +47546,6 @@ - @@ -47552,7 +47553,6 @@ - @@ -47572,9 +47572,8 @@ - - + @@ -47592,7 +47591,6 @@ - @@ -47600,7 +47598,6 @@ - @@ -47610,7 +47607,7 @@ - + @@ -47622,11 +47619,9 @@ - - @@ -47647,9 +47642,7 @@ - - @@ -47661,7 +47654,6 @@ - @@ -47699,7 +47691,7 @@ - + @@ -47719,6 +47711,7 @@ + @@ -47756,7 +47749,8 @@ - + + @@ -47779,14 +47773,16 @@ - + + + @@ -47807,7 +47803,6 @@ - @@ -47822,12 +47817,11 @@ - + - @@ -47847,6 +47841,7 @@ + @@ -47866,6 +47861,7 @@ + @@ -47873,7 +47869,6 @@ - @@ -47916,6 +47911,7 @@ + @@ -47931,6 +47927,7 @@ + @@ -47945,7 +47942,7 @@ - + @@ -47976,13 +47973,11 @@ - - @@ -48009,7 +48004,6 @@ - @@ -48050,7 +48044,7 @@ - + @@ -48067,6 +48061,7 @@ + @@ -48079,8 +48074,10 @@ + + @@ -48108,7 +48105,6 @@ - @@ -48153,7 +48149,7 @@ - + @@ -48166,7 +48162,6 @@ - @@ -48175,7 +48170,6 @@ - @@ -48189,6 +48183,7 @@ + @@ -48196,6 +48191,7 @@ + @@ -48229,7 +48225,7 @@ - + @@ -48242,20 +48238,20 @@ - + - - + + @@ -48283,10 +48279,8 @@ - - @@ -48297,15 +48291,17 @@ + + - + @@ -48313,7 +48309,6 @@ - @@ -48324,13 +48319,13 @@ + - @@ -48355,7 +48350,7 @@ - + @@ -48382,7 +48377,7 @@ - + @@ -48421,13 +48416,13 @@ - - + + @@ -48449,20 +48444,19 @@ - + + - - @@ -48481,19 +48475,20 @@ - + + + - @@ -48503,6 +48498,7 @@ + @@ -48511,7 +48507,7 @@ - + @@ -48525,12 +48521,11 @@ - - + @@ -48550,10 +48545,10 @@ + - @@ -48565,6 +48560,7 @@ + @@ -48574,6 +48570,7 @@ + @@ -48598,7 +48595,6 @@ - @@ -48622,7 +48618,6 @@ - @@ -48639,8 +48634,8 @@ + - @@ -48657,7 +48652,6 @@ - @@ -48670,6 +48664,7 @@ + @@ -48687,7 +48682,7 @@ - + @@ -48712,8 +48707,7 @@ - - + @@ -48725,7 +48719,6 @@ - @@ -48741,6 +48734,7 @@ + @@ -48765,7 +48759,7 @@ - + @@ -48777,6 +48771,7 @@ + @@ -48818,15 +48813,14 @@ - + - - - + + @@ -48856,19 +48850,17 @@ - - + - + - @@ -48884,6 +48876,7 @@ + @@ -48898,7 +48891,6 @@ - @@ -48928,7 +48920,7 @@ - + @@ -48940,7 +48932,7 @@ - + @@ -48992,6 +48984,7 @@ + @@ -49011,7 +49004,6 @@ - @@ -49040,10 +49032,8 @@ - - @@ -49102,7 +49092,7 @@ - + @@ -49112,6 +49102,7 @@ + @@ -49121,6 +49112,7 @@ + @@ -49138,7 +49130,6 @@ - @@ -49178,8 +49169,8 @@ + - @@ -49189,17 +49180,15 @@ - - - + @@ -49211,6 +49200,7 @@ + @@ -49225,7 +49215,7 @@ - + @@ -49247,13 +49237,10 @@ - - - @@ -49261,7 +49248,7 @@ - + @@ -49281,7 +49268,6 @@ - @@ -49332,6 +49318,7 @@ + @@ -49341,10 +49328,11 @@ + - + @@ -49364,6 +49352,7 @@ + @@ -49372,7 +49361,7 @@ - + @@ -49382,17 +49371,16 @@ - + - + - @@ -49410,7 +49398,6 @@ - @@ -49458,17 +49445,15 @@ - - + - @@ -49490,6 +49475,7 @@ + @@ -49498,7 +49484,7 @@ - + @@ -49517,6 +49503,7 @@ + @@ -49540,7 +49527,6 @@ - @@ -49558,7 +49544,7 @@ - + @@ -49570,17 +49556,17 @@ - - + + @@ -49591,8 +49577,8 @@ - + @@ -49622,7 +49608,6 @@ - @@ -49638,7 +49623,6 @@ - @@ -49646,7 +49630,6 @@ - @@ -49660,6 +49643,7 @@ + @@ -49697,7 +49681,6 @@ - @@ -49707,14 +49690,12 @@ - - @@ -49749,10 +49730,10 @@ - + @@ -49772,22 +49753,21 @@ + - - + - @@ -49805,13 +49785,13 @@ - + @@ -49829,7 +49809,6 @@ - @@ -49847,6 +49826,7 @@ + @@ -49862,7 +49842,6 @@ - @@ -49911,7 +49890,6 @@ - @@ -49923,7 +49901,7 @@ - + @@ -49960,6 +49938,7 @@ + @@ -49983,10 +49962,9 @@ - - + @@ -49997,7 +49975,6 @@ - @@ -50042,7 +50019,7 @@ - + @@ -50097,7 +50074,6 @@ - @@ -50175,10 +50151,10 @@ - + - - + + @@ -50208,6 +50184,7 @@ + @@ -50231,7 +50208,8 @@ - + + @@ -50252,7 +50230,6 @@ - @@ -50278,18 +50255,19 @@ - - + + + @@ -50313,20 +50291,24 @@ + + + - + + @@ -50357,7 +50339,6 @@ - @@ -50392,8 +50373,10 @@ + + @@ -50438,14 +50421,15 @@ - + - + + @@ -50473,6 +50457,7 @@ + @@ -50502,12 +50487,12 @@ + - @@ -50524,16 +50509,16 @@ - + - + @@ -50554,12 +50539,11 @@ - + - @@ -50569,8 +50553,7 @@ - - + @@ -50616,6 +50599,7 @@ + @@ -50629,11 +50613,12 @@ - + + @@ -50641,7 +50626,7 @@ - + @@ -50654,7 +50639,6 @@ - @@ -50670,6 +50654,7 @@ + @@ -50678,6 +50663,7 @@ + @@ -50702,7 +50688,7 @@ - + @@ -50712,6 +50698,7 @@ + @@ -50735,7 +50722,6 @@ - @@ -50763,31 +50749,28 @@ - - + - - @@ -50804,7 +50787,7 @@ - + @@ -50812,7 +50795,6 @@ - @@ -50825,7 +50807,6 @@ - @@ -50881,7 +50862,6 @@ - @@ -50895,6 +50875,7 @@ + @@ -50907,7 +50888,8 @@ - + + @@ -50916,7 +50898,7 @@ - + @@ -50933,14 +50915,15 @@ - + - + + @@ -50958,7 +50941,7 @@ - + @@ -50966,15 +50949,16 @@ + - + - + @@ -51007,6 +50991,7 @@ + @@ -51057,6 +51042,7 @@ + @@ -51068,11 +51054,12 @@ - + + @@ -51084,18 +51071,17 @@ + - - - + @@ -51104,13 +51090,15 @@ + - + + @@ -51131,6 +51119,7 @@ + @@ -51175,7 +51164,8 @@ - + + @@ -51207,7 +51197,6 @@ - @@ -51216,12 +51205,12 @@ - + @@ -51233,6 +51222,7 @@ + @@ -51249,6 +51239,7 @@ + @@ -51261,11 +51252,10 @@ - + - @@ -51291,7 +51281,6 @@ - @@ -51300,7 +51289,6 @@ - @@ -51313,6 +51301,7 @@ + @@ -51338,6 +51327,7 @@ + @@ -51364,8 +51354,9 @@ + - + @@ -51373,14 +51364,12 @@ - - @@ -51392,6 +51381,7 @@ + @@ -51408,7 +51398,7 @@ - + @@ -51419,7 +51409,7 @@ - + @@ -51441,8 +51431,6 @@ - - @@ -51458,7 +51446,7 @@ - + @@ -51477,11 +51465,11 @@ + - @@ -51499,6 +51487,7 @@ + @@ -51516,10 +51505,10 @@ - + - + @@ -51531,6 +51520,7 @@ + @@ -51554,6 +51544,7 @@ + @@ -51564,8 +51555,8 @@ - + @@ -51584,7 +51575,7 @@ - + @@ -51593,6 +51584,7 @@ + @@ -51633,6 +51625,7 @@ + @@ -51668,6 +51661,7 @@ + @@ -51677,6 +51671,7 @@ + @@ -51686,6 +51681,7 @@ + @@ -51717,7 +51713,7 @@ - + @@ -51727,6 +51723,7 @@ + @@ -51742,7 +51739,7 @@ - + @@ -51764,7 +51761,6 @@ - @@ -51776,6 +51772,7 @@ + @@ -51785,6 +51782,7 @@ + @@ -51806,6 +51804,7 @@ + @@ -51813,7 +51812,7 @@ - + @@ -51823,13 +51822,11 @@ - - + - @@ -51845,9 +51842,7 @@ - - @@ -51859,7 +51854,6 @@ - @@ -51870,6 +51864,7 @@ + @@ -51878,6 +51873,7 @@ + @@ -51900,6 +51896,7 @@ + @@ -51908,7 +51905,7 @@ - + @@ -51924,6 +51921,7 @@ + @@ -51941,8 +51939,9 @@ + - + @@ -51950,11 +51949,10 @@ - - + @@ -51982,6 +51980,7 @@ + @@ -52001,6 +52000,7 @@ + @@ -52009,10 +52009,10 @@ - + @@ -52025,12 +52025,11 @@ - + - @@ -52049,7 +52048,8 @@ - + + @@ -52068,7 +52068,7 @@ - + @@ -52078,8 +52078,8 @@ - - + + @@ -52094,6 +52094,7 @@ + @@ -52109,7 +52110,6 @@ - @@ -52130,7 +52130,7 @@ - + @@ -52152,7 +52152,7 @@ - + @@ -52168,6 +52168,7 @@ + @@ -52210,7 +52211,6 @@ - @@ -52220,6 +52220,7 @@ + @@ -52236,7 +52237,7 @@ - + @@ -52259,6 +52260,7 @@ + @@ -52266,18 +52268,19 @@ + - - + + @@ -52294,19 +52297,22 @@ - + + + + @@ -52315,7 +52321,7 @@ - + @@ -52342,6 +52348,7 @@ + @@ -52353,19 +52360,20 @@ + + - - + @@ -52375,12 +52383,11 @@ - + - @@ -52392,7 +52399,7 @@ - + @@ -52401,12 +52408,12 @@ - + @@ -52426,15 +52433,16 @@ + + - @@ -52458,6 +52466,7 @@ + @@ -52499,13 +52508,13 @@ - + - + @@ -52519,7 +52528,7 @@ - + @@ -52551,7 +52560,7 @@ - + @@ -52568,6 +52577,7 @@ + @@ -52588,6 +52598,7 @@ + @@ -52601,12 +52612,15 @@ + + - + + @@ -52625,12 +52639,12 @@ + - @@ -52654,14 +52668,12 @@ - - @@ -52674,10 +52686,12 @@ + + @@ -52685,6 +52699,7 @@ + @@ -52700,7 +52715,7 @@ - + @@ -52716,7 +52731,6 @@ - @@ -52726,11 +52740,13 @@ - + + + @@ -52752,6 +52768,8 @@ + + @@ -52764,7 +52782,6 @@ - @@ -52793,11 +52810,14 @@ + + + @@ -52831,10 +52851,10 @@ - + @@ -52867,7 +52887,6 @@ - @@ -52884,6 +52903,7 @@ + @@ -52912,10 +52932,9 @@ - + - @@ -52932,7 +52951,6 @@ - @@ -52956,7 +52974,6 @@ - @@ -52976,7 +52993,7 @@ - + @@ -53009,12 +53026,12 @@ + - @@ -53025,6 +53042,7 @@ + @@ -53036,7 +53054,6 @@ - @@ -53052,6 +53069,7 @@ + @@ -53064,6 +53082,7 @@ + @@ -53073,7 +53092,7 @@ - + @@ -53119,7 +53138,8 @@ - + + @@ -53147,10 +53167,11 @@ + - + @@ -53187,7 +53208,7 @@ - + @@ -53198,7 +53219,6 @@ - @@ -53254,7 +53274,6 @@ - @@ -53285,7 +53304,6 @@ - @@ -53305,6 +53323,7 @@ + @@ -53318,7 +53337,7 @@ - + @@ -53343,14 +53362,13 @@ - + - @@ -53378,13 +53396,14 @@ + - + @@ -53418,6 +53437,7 @@ + @@ -53461,7 +53481,7 @@ - + @@ -53471,24 +53491,26 @@ - + - + + - + + @@ -53505,9 +53527,8 @@ - + - @@ -53525,7 +53546,7 @@ - + @@ -53534,7 +53555,6 @@ - @@ -53545,7 +53565,6 @@ - @@ -53555,7 +53574,6 @@ - @@ -53566,6 +53584,8 @@ + + @@ -53575,13 +53595,11 @@ - + - - - + @@ -53604,24 +53622,25 @@ + + - - - + + @@ -53630,7 +53649,6 @@ - @@ -53640,7 +53658,9 @@ + + @@ -53653,11 +53673,9 @@ - - @@ -53685,7 +53703,6 @@ - @@ -53713,12 +53730,14 @@ - + + + @@ -53728,7 +53747,7 @@ - + @@ -53778,11 +53797,12 @@ - + + @@ -53798,6 +53818,7 @@ + @@ -53807,12 +53828,12 @@ - + @@ -53833,12 +53854,13 @@ - + + @@ -53869,8 +53891,8 @@ - - + + @@ -53880,7 +53902,7 @@ - + @@ -53971,12 +53993,10 @@ - - @@ -53986,15 +54006,15 @@ - + - + @@ -54017,7 +54037,6 @@ - @@ -54041,11 +54060,14 @@ + + + @@ -54055,11 +54077,13 @@ + + @@ -54072,7 +54096,7 @@ - + @@ -54086,7 +54110,7 @@ - + @@ -54099,7 +54123,6 @@ - @@ -54108,7 +54131,7 @@ - + @@ -54122,7 +54145,6 @@ - @@ -54156,11 +54178,12 @@ - + + @@ -54171,8 +54194,6 @@ - - @@ -54181,7 +54202,6 @@ - @@ -54195,7 +54215,6 @@ - @@ -54221,6 +54240,7 @@ + @@ -54246,9 +54266,9 @@ + - @@ -54256,7 +54276,6 @@ - @@ -54266,6 +54285,7 @@ + @@ -54288,7 +54308,6 @@ - @@ -54309,9 +54328,9 @@ + - - + @@ -54340,9 +54359,9 @@ + - @@ -54359,7 +54378,9 @@ + + @@ -54380,13 +54401,12 @@ - + - @@ -54449,12 +54469,12 @@ - + @@ -54485,12 +54505,12 @@ - + @@ -54498,10 +54518,12 @@ + + - + @@ -54510,8 +54532,9 @@ + - + @@ -54522,7 +54545,6 @@ - @@ -54547,8 +54569,10 @@ + + @@ -54568,7 +54592,6 @@ - @@ -54613,6 +54636,7 @@ + @@ -54625,23 +54649,24 @@ - + - + + @@ -54670,8 +54695,9 @@ - + + @@ -54692,7 +54718,6 @@ - @@ -54723,13 +54748,12 @@ - + - @@ -54737,6 +54761,7 @@ + @@ -54750,7 +54775,6 @@ - @@ -54811,6 +54835,7 @@ + @@ -54825,7 +54850,6 @@ - @@ -54836,13 +54860,14 @@ - + + @@ -54854,7 +54879,6 @@ - @@ -54895,8 +54919,6 @@ - - @@ -54909,7 +54931,6 @@ - @@ -54942,6 +54963,7 @@ + @@ -54954,7 +54976,7 @@ - + @@ -54985,6 +55007,7 @@ + @@ -55003,7 +55026,7 @@ - + @@ -55011,7 +55034,7 @@ - + @@ -55030,9 +55053,7 @@ - - @@ -55051,11 +55072,11 @@ + - @@ -55077,12 +55098,10 @@ - - @@ -55090,7 +55109,7 @@ - + @@ -55102,6 +55121,7 @@ + @@ -55116,8 +55136,9 @@ + - + @@ -55150,6 +55171,7 @@ + @@ -55170,7 +55192,6 @@ - @@ -55181,11 +55202,12 @@ - + + @@ -55255,6 +55277,7 @@ + @@ -55292,6 +55315,7 @@ + @@ -55303,6 +55327,7 @@ + @@ -55310,8 +55335,6 @@ - - @@ -55346,15 +55369,14 @@ - + - @@ -55367,15 +55389,15 @@ + - + - @@ -55392,7 +55414,7 @@ - + @@ -55407,6 +55429,7 @@ + @@ -55438,6 +55461,7 @@ + @@ -55448,10 +55472,12 @@ + + @@ -55481,7 +55507,6 @@ - @@ -55528,23 +55553,22 @@ - + - - + - + @@ -55559,13 +55583,13 @@ + - - + @@ -55585,10 +55609,9 @@ - - + @@ -55631,6 +55654,7 @@ + @@ -55639,7 +55663,6 @@ - @@ -55651,12 +55674,10 @@ - - - + @@ -55668,7 +55689,7 @@ - + @@ -55721,7 +55742,6 @@ - @@ -55733,7 +55753,7 @@ - + @@ -55759,7 +55779,9 @@ + + @@ -55783,6 +55805,7 @@ + @@ -55817,6 +55840,7 @@ + @@ -55830,8 +55854,6 @@ - - @@ -55839,16 +55861,18 @@ - + + + @@ -55860,15 +55884,13 @@ - - - + @@ -55879,8 +55901,10 @@ + + @@ -55902,6 +55926,7 @@ + @@ -55929,7 +55954,7 @@ - + @@ -55938,9 +55963,8 @@ - - + @@ -55958,7 +55982,6 @@ - @@ -55997,6 +56020,7 @@ + @@ -56009,7 +56033,7 @@ - + @@ -56048,12 +56072,11 @@ - - + @@ -56075,25 +56098,22 @@ - - - - + - + - + @@ -56132,27 +56152,25 @@ - - + - + - + - @@ -56163,14 +56181,12 @@ - - @@ -56187,7 +56203,7 @@ - + @@ -56213,9 +56229,9 @@ - + - + @@ -56234,8 +56250,8 @@ - - + + @@ -56246,19 +56262,17 @@ + - - - @@ -56269,8 +56283,8 @@ - + @@ -56280,7 +56294,7 @@ - + @@ -56303,15 +56317,13 @@ - - - + @@ -56323,12 +56335,10 @@ - - @@ -56343,7 +56353,8 @@ - + + @@ -56355,6 +56366,7 @@ + @@ -56367,10 +56379,10 @@ + - @@ -56383,16 +56395,18 @@ + - + + @@ -56418,8 +56432,8 @@ - + @@ -56452,7 +56466,7 @@ - + @@ -56477,14 +56491,14 @@ - + + - @@ -56549,8 +56563,7 @@ - - + @@ -56582,7 +56595,6 @@ - @@ -56599,7 +56611,6 @@ - @@ -56680,7 +56691,7 @@ - + @@ -56712,7 +56723,6 @@ - @@ -56723,7 +56733,6 @@ - @@ -56735,16 +56744,19 @@ + + + - + @@ -56771,6 +56783,7 @@ + @@ -56819,6 +56832,7 @@ + @@ -56828,7 +56842,6 @@ - @@ -56836,7 +56849,7 @@ - + @@ -56860,14 +56873,13 @@ - - + @@ -56902,12 +56914,11 @@ - + - @@ -56971,6 +56982,7 @@ + @@ -56996,18 +57008,15 @@ - - - @@ -57044,6 +57053,7 @@ + @@ -57076,7 +57086,7 @@ - + @@ -57102,7 +57112,6 @@ - @@ -57116,7 +57125,6 @@ - @@ -57147,7 +57155,7 @@ - + @@ -57155,7 +57163,6 @@ - @@ -57164,16 +57171,15 @@ - - + @@ -57191,7 +57197,6 @@ - @@ -57218,6 +57223,7 @@ + @@ -57243,10 +57249,8 @@ - - @@ -57259,6 +57263,7 @@ + @@ -57282,7 +57287,7 @@ - + @@ -57290,9 +57295,9 @@ - + @@ -57321,7 +57326,6 @@ - @@ -57329,7 +57333,6 @@ - @@ -57349,9 +57352,10 @@ + - + @@ -57367,6 +57371,7 @@ + @@ -57379,7 +57384,6 @@ - @@ -57436,6 +57440,7 @@ + @@ -57450,8 +57455,7 @@ - - + @@ -57475,6 +57479,7 @@ + @@ -57487,18 +57492,15 @@ - - + - - - + @@ -57517,7 +57519,7 @@ - + @@ -57530,9 +57532,12 @@ + + + @@ -57547,19 +57552,20 @@ + - - + + @@ -57602,7 +57608,7 @@ - + @@ -57614,11 +57620,11 @@ - + @@ -57637,6 +57643,7 @@ + @@ -57660,10 +57667,12 @@ + + @@ -57682,6 +57691,7 @@ + @@ -57691,7 +57701,7 @@ - + @@ -57700,15 +57710,15 @@ + - + - @@ -57721,7 +57731,6 @@ - @@ -57729,9 +57738,8 @@ - + - @@ -57755,8 +57763,7 @@ - - + @@ -57764,8 +57771,7 @@ - - + @@ -57785,11 +57791,13 @@ + + @@ -57824,7 +57832,6 @@ - @@ -57852,6 +57859,7 @@ + @@ -57864,6 +57872,7 @@ + @@ -57879,8 +57888,7 @@ - - + @@ -57893,7 +57901,6 @@ - @@ -57912,6 +57919,7 @@ + @@ -57927,9 +57935,10 @@ - + + @@ -57951,7 +57960,6 @@ - @@ -57970,13 +57978,13 @@ - + @@ -57993,11 +58001,13 @@ - + + + @@ -58026,7 +58036,7 @@ - + @@ -58035,6 +58045,7 @@ + @@ -58044,6 +58055,7 @@ + @@ -58068,10 +58080,8 @@ - - @@ -58098,7 +58108,6 @@ - @@ -58110,7 +58119,6 @@ - @@ -58132,11 +58140,11 @@ - + @@ -58144,8 +58152,10 @@ + + @@ -58161,7 +58171,7 @@ - + @@ -58186,13 +58196,13 @@ - + - + @@ -58218,7 +58228,7 @@ - + @@ -58253,7 +58263,6 @@ - @@ -58264,17 +58273,18 @@ - + + @@ -58303,7 +58313,8 @@ - + + @@ -58313,14 +58324,13 @@ - - + @@ -58335,9 +58345,7 @@ - - @@ -58366,16 +58374,15 @@ - - + @@ -58392,7 +58399,7 @@ - + @@ -58417,7 +58424,7 @@ - + @@ -58425,6 +58432,7 @@ + @@ -58437,6 +58445,7 @@ + @@ -58457,7 +58466,7 @@ - + @@ -58499,8 +58508,10 @@ + + @@ -58521,6 +58532,7 @@ + @@ -58550,7 +58562,7 @@ - + @@ -58620,7 +58632,6 @@ - @@ -58632,7 +58643,7 @@ - + @@ -58643,7 +58654,6 @@ - @@ -58667,10 +58677,8 @@ - - @@ -58700,10 +58708,8 @@ - - @@ -58734,7 +58740,7 @@ - + @@ -58757,6 +58763,7 @@ + @@ -58801,33 +58808,32 @@ - - + - + - + - + - + - + @@ -58852,7 +58858,6 @@ - @@ -58862,6 +58867,7 @@ + @@ -58902,11 +58908,11 @@ + - - + @@ -58951,6 +58957,7 @@ + @@ -58976,7 +58983,6 @@ - @@ -58993,6 +58999,7 @@ + @@ -59011,6 +59018,7 @@ + @@ -59025,12 +59033,13 @@ + - + @@ -59059,6 +59068,7 @@ + @@ -59081,6 +59091,7 @@ + @@ -59092,7 +59103,8 @@ - + + @@ -59106,10 +59118,10 @@ + - @@ -59129,11 +59141,12 @@ + - - + + @@ -59146,7 +59159,6 @@ - @@ -59184,7 +59196,7 @@ - + @@ -59202,13 +59214,12 @@ - + - @@ -59228,10 +59239,10 @@ - + @@ -59241,6 +59252,7 @@ + @@ -59252,17 +59264,20 @@ + + + - + @@ -59274,7 +59289,7 @@ - + @@ -59305,7 +59320,6 @@ - @@ -59321,16 +59335,16 @@ - + - + @@ -59338,7 +59352,7 @@ - + @@ -59359,10 +59373,10 @@ - + - + @@ -59377,6 +59391,7 @@ + @@ -59412,20 +59427,19 @@ - + - + - @@ -59435,12 +59449,13 @@ - + + @@ -59448,7 +59463,6 @@ - @@ -59463,7 +59477,7 @@ - + @@ -59471,7 +59485,7 @@ - + @@ -59480,7 +59494,6 @@ - @@ -59496,19 +59509,16 @@ - - - + - @@ -59549,7 +59559,6 @@ - @@ -59563,7 +59572,6 @@ - @@ -59577,9 +59585,11 @@ + + @@ -59640,6 +59650,7 @@ + @@ -59650,10 +59661,9 @@ - + - @@ -59665,11 +59675,12 @@ - + + @@ -59679,9 +59690,7 @@ - - @@ -59690,6 +59699,7 @@ + @@ -59736,6 +59746,7 @@ + @@ -59748,12 +59759,15 @@ + + + @@ -59762,6 +59776,7 @@ + @@ -59788,7 +59803,6 @@ - @@ -59806,7 +59820,6 @@ - @@ -59816,7 +59829,6 @@ - @@ -59844,13 +59856,13 @@ + - @@ -59872,13 +59884,13 @@ - - + + @@ -59913,8 +59925,8 @@ + - @@ -59925,7 +59937,8 @@ - + + @@ -59950,7 +59963,7 @@ - + @@ -59959,14 +59972,16 @@ + - - + + + @@ -59979,6 +59994,7 @@ + @@ -59993,6 +60009,7 @@ + @@ -60008,14 +60025,12 @@ - - @@ -60039,12 +60054,14 @@ + - + + @@ -60064,7 +60081,6 @@ - @@ -60083,7 +60099,6 @@ - @@ -60108,6 +60123,7 @@ + @@ -60142,7 +60158,6 @@ - @@ -60151,6 +60166,7 @@ + @@ -60182,8 +60198,8 @@ + - @@ -60191,7 +60207,7 @@ - + @@ -60207,7 +60223,7 @@ - + @@ -60219,6 +60235,7 @@ + @@ -60284,16 +60301,16 @@ - + - + - + @@ -60302,7 +60319,6 @@ - @@ -60326,10 +60342,10 @@ - + @@ -60358,12 +60374,13 @@ - + - + + @@ -60392,7 +60409,7 @@ - + @@ -60407,16 +60424,16 @@ - + - + @@ -60435,10 +60452,10 @@ + - @@ -60471,13 +60488,13 @@ - + - + @@ -60499,10 +60516,11 @@ - + + - + @@ -60519,12 +60537,11 @@ - + - @@ -60557,7 +60574,7 @@ - + @@ -60572,7 +60589,6 @@ - @@ -60591,7 +60607,7 @@ - + @@ -60607,11 +60623,10 @@ - + - @@ -60646,6 +60661,7 @@ + @@ -60662,9 +60678,11 @@ + - + + @@ -60688,9 +60706,11 @@ + + @@ -60721,7 +60741,6 @@ - @@ -60733,7 +60752,7 @@ - + @@ -60743,7 +60762,7 @@ - + @@ -60757,6 +60776,7 @@ + @@ -60785,7 +60805,6 @@ - @@ -60797,7 +60816,7 @@ - + @@ -60828,15 +60847,14 @@ - + - @@ -60852,6 +60870,7 @@ + @@ -60859,13 +60878,11 @@ - - + - @@ -60904,12 +60921,12 @@ - + - + @@ -60937,7 +60954,6 @@ - @@ -60987,16 +61003,14 @@ - - - + @@ -61013,13 +61027,11 @@ - - - + @@ -61031,12 +61043,12 @@ - + - + @@ -61047,7 +61059,8 @@ - + + @@ -61068,7 +61081,6 @@ - @@ -61079,10 +61091,8 @@ - - @@ -61100,6 +61110,7 @@ + @@ -61113,6 +61124,7 @@ + @@ -61134,6 +61146,7 @@ + @@ -61143,7 +61156,6 @@ - @@ -61155,20 +61167,20 @@ - + - + + - @@ -61193,10 +61205,11 @@ - + + @@ -61206,6 +61219,7 @@ + @@ -61218,10 +61232,10 @@ + - @@ -61250,12 +61264,12 @@ + - @@ -61278,7 +61292,6 @@ - @@ -61306,7 +61319,7 @@ - + @@ -61318,14 +61331,14 @@ - + - + @@ -61334,7 +61347,7 @@ - + @@ -61343,7 +61356,6 @@ - @@ -61351,7 +61363,7 @@ - + @@ -61360,23 +61372,21 @@ - + - + - - + - @@ -61406,6 +61416,7 @@ + @@ -61446,7 +61457,7 @@ - + @@ -61463,6 +61474,7 @@ + @@ -61470,7 +61482,6 @@ - @@ -61497,7 +61508,7 @@ - + @@ -61506,8 +61517,9 @@ + - + @@ -61515,6 +61527,7 @@ + @@ -61553,6 +61566,7 @@ + @@ -61568,18 +61582,19 @@ + + - + - @@ -61596,6 +61611,7 @@ + @@ -61620,6 +61636,7 @@ + @@ -61649,7 +61666,6 @@ - @@ -61693,12 +61709,15 @@ + + + @@ -61727,9 +61746,9 @@ + - @@ -61746,25 +61765,25 @@ + - - - + - + - + + @@ -61785,6 +61804,7 @@ + @@ -61797,11 +61817,13 @@ + + + - @@ -61818,9 +61840,7 @@ - - @@ -61830,13 +61850,15 @@ + - + + @@ -61845,6 +61867,7 @@ + @@ -61867,7 +61890,6 @@ - @@ -61885,7 +61907,6 @@ - @@ -61898,6 +61919,7 @@ + @@ -61942,7 +61964,6 @@ - @@ -61987,7 +62008,6 @@ - @@ -62005,16 +62025,17 @@ - - + + - + + @@ -62043,7 +62064,7 @@ - + @@ -62054,7 +62075,7 @@ - + @@ -62067,7 +62088,8 @@ - + + @@ -62097,9 +62119,9 @@ - + @@ -62119,14 +62141,13 @@ - - + @@ -62143,7 +62164,6 @@ - @@ -62172,18 +62192,19 @@ + - + - + @@ -62201,7 +62222,6 @@ - @@ -62249,6 +62269,7 @@ + @@ -62267,6 +62288,7 @@ + @@ -62305,6 +62327,7 @@ + @@ -62342,7 +62365,7 @@ - + @@ -62353,6 +62376,7 @@ + @@ -62386,17 +62410,15 @@ - + - - @@ -62419,6 +62441,7 @@ + @@ -62469,7 +62492,6 @@ - @@ -62480,7 +62502,6 @@ - @@ -62516,7 +62537,6 @@ - @@ -62536,7 +62556,6 @@ - @@ -62552,13 +62571,11 @@ - - @@ -62568,18 +62585,18 @@ - + - + @@ -62591,6 +62608,7 @@ + @@ -62643,7 +62661,6 @@ - @@ -62652,6 +62669,7 @@ + @@ -62670,7 +62688,7 @@ - + @@ -62716,7 +62734,7 @@ - + @@ -62733,7 +62751,6 @@ - @@ -62742,6 +62759,7 @@ + @@ -62776,19 +62794,19 @@ - + + - - + @@ -62817,7 +62835,6 @@ - @@ -62831,7 +62848,6 @@ - @@ -62844,7 +62860,6 @@ - @@ -62869,6 +62884,7 @@ + @@ -62877,6 +62893,7 @@ + @@ -62889,9 +62906,8 @@ - - + @@ -62919,7 +62935,9 @@ + + @@ -62991,11 +63009,11 @@ - + - + @@ -63008,7 +63026,6 @@ - @@ -63017,28 +63034,29 @@ - + + + - - + @@ -63085,6 +63103,7 @@ + @@ -63110,23 +63129,20 @@ + - - - - + - @@ -63140,7 +63156,6 @@ - @@ -63151,15 +63166,15 @@ - + - + @@ -63207,6 +63222,7 @@ + @@ -63216,9 +63232,9 @@ + - - + @@ -63240,13 +63256,13 @@ - + + - @@ -63258,7 +63274,7 @@ - + @@ -63274,12 +63290,10 @@ - - @@ -63309,7 +63323,7 @@ - + @@ -63318,7 +63332,7 @@ - + @@ -63353,10 +63367,11 @@ - + + @@ -63380,6 +63395,7 @@ + @@ -63391,7 +63407,6 @@ - @@ -63401,7 +63416,6 @@ - @@ -63435,7 +63449,6 @@ - @@ -63456,26 +63469,22 @@ - + - - - + - - @@ -63484,14 +63493,13 @@ - - + @@ -63502,7 +63510,6 @@ - @@ -63512,9 +63519,7 @@ - - @@ -63554,7 +63559,6 @@ - @@ -63576,7 +63580,6 @@ - @@ -63588,10 +63591,11 @@ - + + @@ -63608,7 +63612,6 @@ - @@ -63651,6 +63654,7 @@ + @@ -63660,7 +63664,6 @@ - @@ -63710,12 +63713,11 @@ - - + @@ -63726,7 +63728,6 @@ - @@ -63796,7 +63797,7 @@ - + @@ -63809,10 +63810,10 @@ - + @@ -63821,22 +63822,21 @@ - - + - + - - + + @@ -63852,9 +63852,9 @@ - + - + @@ -63871,7 +63871,7 @@ - + @@ -63882,6 +63882,7 @@ + @@ -63892,6 +63893,7 @@ + @@ -63909,6 +63911,7 @@ + @@ -63920,6 +63923,7 @@ + @@ -63930,7 +63934,7 @@ - + @@ -63945,7 +63949,7 @@ - + @@ -63956,7 +63960,6 @@ - @@ -63968,20 +63971,17 @@ - - - - + @@ -64007,7 +64007,6 @@ - @@ -64016,7 +64015,6 @@ - @@ -64077,6 +64075,7 @@ + @@ -64097,6 +64096,7 @@ + @@ -64118,7 +64118,6 @@ - @@ -64127,8 +64126,8 @@ - + @@ -64164,14 +64163,14 @@ - + + - @@ -64188,6 +64187,7 @@ + @@ -64214,15 +64214,16 @@ + - + @@ -64234,6 +64235,7 @@ + @@ -64241,6 +64243,7 @@ + @@ -64258,14 +64261,12 @@ - - @@ -64281,7 +64282,6 @@ - @@ -64310,9 +64310,8 @@ - - + @@ -64321,7 +64320,6 @@ - @@ -64374,7 +64372,6 @@ - @@ -64383,9 +64380,9 @@ - + @@ -64406,8 +64403,7 @@ - - + @@ -64421,8 +64417,8 @@ + - @@ -64432,14 +64428,13 @@ - - + @@ -64493,14 +64488,13 @@ - + - @@ -64520,13 +64514,13 @@ - + + - @@ -64538,16 +64532,19 @@ + + + @@ -64564,10 +64561,9 @@ - - + @@ -64594,7 +64590,6 @@ - @@ -64605,24 +64600,25 @@ + + - + - - + @@ -64652,7 +64648,6 @@ - @@ -64664,6 +64659,7 @@ + @@ -64682,11 +64678,11 @@ - + - + @@ -64717,18 +64713,20 @@ + - + + @@ -64741,7 +64739,6 @@ - @@ -64789,16 +64786,19 @@ + - + + + @@ -64827,7 +64827,7 @@ - + @@ -64856,10 +64856,9 @@ - - + @@ -64870,7 +64869,6 @@ - @@ -64893,7 +64891,6 @@ - @@ -64903,11 +64900,11 @@ + - @@ -64918,16 +64915,17 @@ - + - + + @@ -64938,7 +64936,6 @@ - @@ -64972,15 +64969,14 @@ - - + @@ -64991,6 +64987,7 @@ + @@ -64999,6 +64996,7 @@ + @@ -65009,6 +65007,7 @@ + @@ -65017,8 +65016,10 @@ - + + + @@ -65034,7 +65035,7 @@ - + @@ -65053,6 +65054,7 @@ + @@ -65063,6 +65065,7 @@ + @@ -65083,13 +65086,12 @@ - - + @@ -65103,7 +65105,6 @@ - @@ -65132,14 +65133,14 @@ - + - + @@ -65152,11 +65153,11 @@ - + @@ -65164,8 +65165,8 @@ + - @@ -65185,6 +65186,7 @@ + @@ -65195,23 +65197,26 @@ + - + + - + + @@ -65237,7 +65242,6 @@ - @@ -65245,8 +65249,7 @@ - - + @@ -65276,8 +65279,8 @@ - + @@ -65308,12 +65311,11 @@ - + - @@ -65323,14 +65325,13 @@ - + - @@ -65350,7 +65351,7 @@ - + @@ -65359,7 +65360,6 @@ - @@ -65378,20 +65378,20 @@ - - - + + + @@ -65406,7 +65406,6 @@ - @@ -65424,6 +65423,7 @@ + @@ -65437,15 +65437,15 @@ + - - + @@ -65456,13 +65456,14 @@ - + + @@ -65482,7 +65483,6 @@ - @@ -65495,13 +65495,13 @@ + - @@ -65514,7 +65514,7 @@ - + @@ -65534,7 +65534,6 @@ - @@ -65550,10 +65549,8 @@ - - @@ -65575,6 +65572,7 @@ + @@ -65587,7 +65585,6 @@ - @@ -65599,6 +65596,7 @@ + @@ -65608,6 +65606,7 @@ + @@ -65616,10 +65615,11 @@ - + + @@ -65630,6 +65630,7 @@ + @@ -65652,6 +65653,7 @@ + @@ -65667,13 +65669,11 @@ - - @@ -65719,7 +65719,6 @@ - @@ -65742,39 +65741,41 @@ - - + + + + + + - - @@ -65805,7 +65806,7 @@ - + @@ -65813,7 +65814,6 @@ - @@ -65851,7 +65851,6 @@ - @@ -65862,7 +65861,6 @@ - @@ -65907,7 +65905,9 @@ + + @@ -65923,6 +65923,7 @@ + @@ -65964,7 +65965,7 @@ - + @@ -65993,7 +65994,6 @@ - @@ -66009,6 +66009,7 @@ + @@ -66029,7 +66030,7 @@ - + @@ -66039,28 +66040,29 @@ - + + - + @@ -66074,7 +66076,6 @@ - @@ -66089,6 +66090,7 @@ + @@ -66109,11 +66111,9 @@ - - @@ -66121,7 +66121,7 @@ - + @@ -66142,6 +66142,7 @@ + @@ -66150,7 +66151,8 @@ - + + @@ -66180,14 +66182,16 @@ + - + - + + @@ -66206,6 +66210,7 @@ + @@ -66213,6 +66218,7 @@ + @@ -66231,7 +66237,6 @@ - @@ -66273,7 +66278,6 @@ - @@ -66296,12 +66300,13 @@ + - + @@ -66317,9 +66322,9 @@ - + @@ -66340,7 +66345,7 @@ - + @@ -66356,10 +66361,12 @@ + - + + @@ -66384,6 +66391,7 @@ + @@ -66399,7 +66407,6 @@ - @@ -66410,11 +66417,11 @@ + - @@ -66438,7 +66445,6 @@ - @@ -66456,7 +66462,7 @@ - + @@ -66467,7 +66473,7 @@ - + @@ -66481,11 +66487,12 @@ + - + @@ -66528,12 +66535,14 @@ + + - + @@ -66553,7 +66562,6 @@ - @@ -66575,7 +66583,6 @@ - @@ -66603,7 +66610,6 @@ - @@ -66617,13 +66623,12 @@ - - + @@ -66655,6 +66660,7 @@ + @@ -66685,7 +66691,7 @@ - + @@ -66718,12 +66724,12 @@ + - @@ -66748,6 +66754,7 @@ + @@ -66774,12 +66781,11 @@ - - + @@ -66790,6 +66796,7 @@ + @@ -66809,10 +66816,8 @@ - - @@ -66837,7 +66842,6 @@ - @@ -66874,9 +66878,12 @@ + + + @@ -66884,9 +66891,7 @@ - - @@ -66894,7 +66899,6 @@ - @@ -66927,6 +66931,7 @@ + @@ -66934,8 +66939,9 @@ + - + @@ -66952,10 +66958,11 @@ - + + @@ -66979,7 +66986,6 @@ - @@ -66987,7 +66993,6 @@ - @@ -66998,11 +67003,10 @@ - - + @@ -67015,6 +67019,7 @@ + @@ -67023,7 +67028,7 @@ - + @@ -67041,6 +67046,7 @@ + @@ -67058,6 +67064,7 @@ + @@ -67072,7 +67079,7 @@ - + @@ -67083,7 +67090,6 @@ - @@ -67094,7 +67100,8 @@ - + + @@ -67126,9 +67133,10 @@ - + + @@ -67144,12 +67152,10 @@ - - @@ -67178,6 +67184,7 @@ + @@ -67199,11 +67206,11 @@ - + @@ -67219,6 +67226,7 @@ + @@ -67232,6 +67240,7 @@ + @@ -67241,6 +67250,7 @@ + @@ -67256,10 +67266,9 @@ - - + @@ -67278,7 +67287,6 @@ - @@ -67290,6 +67298,7 @@ + @@ -67319,10 +67328,10 @@ + - @@ -67330,7 +67339,6 @@ - @@ -67340,7 +67348,6 @@ - @@ -67356,10 +67363,8 @@ - - @@ -67378,7 +67383,6 @@ - @@ -67400,7 +67404,7 @@ - + @@ -67419,24 +67423,24 @@ - + - + + - - + @@ -67454,14 +67458,13 @@ - + - @@ -67481,7 +67484,7 @@ - + @@ -67489,10 +67492,9 @@ - + - @@ -67520,6 +67522,7 @@ + @@ -67527,7 +67530,6 @@ - @@ -67540,11 +67542,11 @@ - + @@ -67562,7 +67564,6 @@ - @@ -67587,7 +67588,7 @@ - + @@ -67616,7 +67617,6 @@ - @@ -67626,6 +67626,7 @@ + @@ -67638,7 +67639,6 @@ - @@ -67653,14 +67653,13 @@ - + - @@ -67668,7 +67667,7 @@ - + @@ -67708,6 +67707,8 @@ + + @@ -67721,10 +67722,11 @@ + - + @@ -67739,9 +67741,11 @@ + + @@ -67750,6 +67754,7 @@ + @@ -67761,11 +67766,13 @@ + + - + @@ -67776,15 +67783,16 @@ + - + - + @@ -67811,8 +67819,7 @@ - - + @@ -67840,13 +67847,13 @@ - + @@ -67868,15 +67875,15 @@ - + - + - + @@ -67904,7 +67911,7 @@ - + @@ -67924,15 +67931,16 @@ + + - - + @@ -67942,13 +67950,12 @@ - + - @@ -67957,12 +67964,14 @@ + + @@ -67990,6 +67999,7 @@ + @@ -68009,14 +68019,12 @@ - - @@ -68034,13 +68042,12 @@ - - + - + @@ -68058,8 +68065,9 @@ + - + @@ -68085,7 +68093,6 @@ - @@ -68107,12 +68114,13 @@ + - - + + @@ -68158,7 +68166,7 @@ - + @@ -68190,7 +68198,6 @@ - @@ -68203,7 +68210,7 @@ - + @@ -68224,23 +68231,23 @@ - + + + - + - - + - @@ -68248,23 +68255,21 @@ - - - + - + @@ -68302,15 +68307,15 @@ - - + + @@ -68323,6 +68328,7 @@ + @@ -68346,6 +68352,7 @@ + @@ -68366,6 +68373,7 @@ + @@ -68412,6 +68420,7 @@ + @@ -68419,12 +68428,14 @@ + + @@ -68457,7 +68468,6 @@ - @@ -68492,7 +68502,6 @@ - @@ -68500,6 +68509,7 @@ + @@ -68509,6 +68519,7 @@ + @@ -68531,13 +68542,13 @@ + - @@ -68547,7 +68558,6 @@ - @@ -68563,7 +68573,7 @@ - + @@ -68576,18 +68586,17 @@ - - + + - @@ -68602,7 +68611,6 @@ - @@ -68617,7 +68625,9 @@ + + @@ -68628,110 +68638,106 @@ - - + - - + - + + - + - - - - - + + + + - - + - + + + - - + + + - - - - + + - + + - - - @@ -68749,20 +68755,17 @@ - - - + - - + @@ -68777,60 +68780,51 @@ - + - - + - - + - + - - + + - - - - - - + - - @@ -68841,70 +68835,66 @@ - + - - - + + + - + + + - - + - - - + - - + - - + + + + - - - - + - @@ -68913,6 +68903,7 @@ + @@ -68920,25 +68911,23 @@ + - - - - + - - + + @@ -68947,6 +68936,7 @@ + @@ -68955,8 +68945,6 @@ - - @@ -68965,16 +68953,15 @@ + - - - + @@ -68984,54 +68971,54 @@ - - + - - + + + - - + + + - - + @@ -69040,116 +69027,107 @@ + + + - + + - + - - + - - + - + - - + - - - - - - - + - - + - - + + - + + - - - + + - - - + + + + - - - - @@ -69158,36 +69136,36 @@ + - - - - + - - - + + + + + + - + - @@ -69195,19 +69173,18 @@ + + - - - @@ -69217,9 +69194,9 @@ - + @@ -69230,67 +69207,64 @@ - - + - + + - + - + - - - - - + + + - + + - + - - + - @@ -69298,202 +69272,229 @@ + + + + + + + + + - + - + + + + + + - + + + + - - + + + - + + - + - - + - + + - - + + + + + - + + - - + + + + + - + - + - + + - + - - + + - - - + + - - - + + + + - + + - + + - - + - @@ -69502,24 +69503,25 @@ + + - - - + + @@ -69530,11 +69532,12 @@ - + + @@ -69544,44 +69547,44 @@ - - + + + - - - + + - - + - + + @@ -69594,50 +69597,51 @@ - + + - + + - + + - - - + - + - + @@ -69651,65 +69655,64 @@ + + + - + - - - + + - - + - - - + + - - - + + + + - - @@ -69717,24 +69720,22 @@ - - + - - + @@ -69742,22 +69743,24 @@ + - + + - + - + + - @@ -69765,8 +69768,9 @@ + - + @@ -69777,46 +69781,50 @@ - + + + - - + - - + + - + - + - + + + + @@ -69824,31 +69832,34 @@ - + + - + + + - + - + + + - - @@ -69858,18 +69869,14 @@ - - - - + + - - @@ -69880,30 +69887,28 @@ + - - + - - + + - + + - - - @@ -69913,25 +69918,25 @@ + + + + - + - - - - @@ -69939,8 +69944,8 @@ - + @@ -69952,11 +69957,9 @@ - - @@ -69969,16 +69972,21 @@ + + + + + - + + - @@ -69989,83 +69997,84 @@ + + - - + + - - - - - - + + + - - - + - + + + - + + + + + + + - - - - @@ -70077,12 +70086,13 @@ + - + + - @@ -70098,71 +70108,76 @@ - + - + - - + + + - - + - + + - - - + - + + + - + - + + + + + - - - + + + @@ -70170,11 +70185,14 @@ + + + @@ -70182,46 +70200,47 @@ - + + - + + - - - + + + - + + - - @@ -70230,80 +70249,80 @@ - - + - + - - - + - + - - - + + + + - + + - - + + + + - + - - + + - + - @@ -70311,23 +70330,28 @@ + - + + + + + - + @@ -70337,37 +70361,36 @@ + + + - + - - - - - + + - @@ -70375,19 +70398,17 @@ - + - + - - @@ -70396,21 +70417,16 @@ - + + - - - - - - @@ -70423,20 +70439,22 @@ + + - + + - @@ -70445,24 +70463,26 @@ + + + + - - @@ -70472,17 +70492,13 @@ - - - - + - @@ -70495,11 +70511,9 @@ - - + - - + @@ -70508,20 +70522,20 @@ - - + - + + - + @@ -70536,9 +70550,9 @@ + - @@ -70546,7 +70560,6 @@ - @@ -70555,36 +70568,36 @@ + + + - - + - - - - + + + - + + + - - - @@ -70592,20 +70605,17 @@ - - - - - + - + + - + @@ -70613,10 +70623,9 @@ + - - @@ -70624,41 +70633,45 @@ - - + + - - + + + + + + - + @@ -70668,26 +70681,22 @@ - - - - - + - + @@ -70697,38 +70706,48 @@ - - + + - - + + + + + + + + + + + - + + - + @@ -70736,23 +70755,22 @@ - - + + + - - @@ -70763,21 +70781,18 @@ + - - - - @@ -70786,20 +70801,22 @@ - + + - + + @@ -70807,13 +70824,14 @@ - + - + + @@ -70822,57 +70840,49 @@ - + - - - + + - - - - - - - + - - + + - + - @@ -70880,23 +70890,25 @@ + + + - + - - + @@ -70914,12 +70926,10 @@ - + - - @@ -70935,25 +70945,26 @@ + - + - + - + @@ -70964,34 +70975,36 @@ + - - - + + + - + + + - @@ -71001,24 +71014,25 @@ + + - - + + + - - - + @@ -71026,90 +71040,87 @@ - - - - + - + - - - - + - + + + - + + + - - - - - - + + + - + + + - - + + + - @@ -71119,127 +71130,121 @@ + - - - + + - - - + - + - - - - - + + + - - - - + + + + - - - + - + - - - + + - - + + + + + + - - - @@ -71251,27 +71256,21 @@ - + - + - - - - - - @@ -71287,45 +71286,42 @@ - - - - + + - - + - + + + - + - + - - @@ -71334,15 +71330,12 @@ + - - - - @@ -71350,14 +71343,13 @@ - + - @@ -71370,18 +71362,20 @@ + + - + - + @@ -71389,37 +71383,35 @@ - + - + - + - - - - + - + + - + + - @@ -71428,79 +71420,88 @@ - + + - - + + + + + - + + - - - + - - + - + + - - + + + + + + + - + + - + @@ -71508,49 +71509,50 @@ - - + - - + - - - + + - + + + + + + - - + - + + - - + @@ -71558,6 +71560,7 @@ + @@ -71570,16 +71573,17 @@ + - + + - @@ -71590,47 +71594,45 @@ - + - - + - + + - + - - - - - + + + @@ -71639,11 +71641,17 @@ + + - + + + + + @@ -71659,7 +71667,7 @@ - + @@ -71667,66 +71675,78 @@ + + + - + - + + + + + - + - + - + + + + - + + + + - diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json similarity index 85% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json index 6d596bc733c9..b03ded127a9b 100644 --- a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/Contents.json +++ b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json @@ -3,13 +3,13 @@ { "size" : "1280x768", "idiom" : "tv", - "filename" : "App Icon - Large.imagestack", + "filename" : "App Icon - App Store.imagestack", "role" : "primary-app-icon" }, { "size" : "400x240", "idiom" : "tv", - "filename" : "App Icon - Small.imagestack", + "filename" : "App Icon.imagestack", "role" : "primary-app-icon" }, { diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/Top Shelf Image Wide.imageset/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/Top Shelf Image Wide.imageset/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json diff --git a/Engine/Build/TVOS/Resources/Assets.xcassets/LaunchImage.launchimage/Contents.json b/Engine/Build/TVOS/Resources/Assets.xcassets/Launch Image.launchimage/Contents.json similarity index 100% rename from Engine/Build/TVOS/Resources/Assets.xcassets/LaunchImage.launchimage/Contents.json rename to Engine/Build/TVOS/Resources/Assets.xcassets/Launch Image.launchimage/Contents.json diff --git a/Engine/Config/Android/AndroidEngine.ini b/Engine/Config/Android/AndroidEngine.ini index d2b3ef709d3e..24e1d89e005c 100644 --- a/Engine/Config/Android/AndroidEngine.ini +++ b/Engine/Config/Android/AndroidEngine.ini @@ -4,6 +4,7 @@ DefaultBloomKernelTextureName=/Engine/EngineResources/DefaultTexture.DefaultText [PlatformCrypto] PlatformRequiresDataCrypto=True +PakSigningRequired=False [/Script/Engine.GarbageCollectionSettings] gc.MaxObjectsInGame=131072 diff --git a/Engine/Config/BaseEditor.ini b/Engine/Config/BaseEditor.ini index 72972d22079d..043cadc7a892 100644 --- a/Engine/Config/BaseEditor.ini +++ b/Engine/Config/BaseEditor.ini @@ -526,3 +526,7 @@ bValidateOnSave=true +DebugResolutions=(Width=720,Height=1280,Description="",Color=(R=0.000000,G=0.318547,B=0.806952,A=1.000000)) +DebugResolutions=(Width=1136,Height=640,Description="",Color=(R=0.000000,G=0.238398,B=0.658375,A=1.000000)) +DebugResolutions=(Width=640,Height=1136,Description="",Color=(R=0.000000,G=0.238398,B=0.658375,A=1.000000)) + +[/Script/UnrealEd.DiffAssetRegistriesCommandlet] +P4EngineBasePath="/Engine/Content/" +P4EngineAssetPath="/Engine/" diff --git a/Engine/Config/BaseEditorPerProjectUserSettings.ini b/Engine/Config/BaseEditorPerProjectUserSettings.ini index cb97aba70e38..06f98ecab92b 100644 --- a/Engine/Config/BaseEditorPerProjectUserSettings.ini +++ b/Engine/Config/BaseEditorPerProjectUserSettings.ini @@ -318,6 +318,7 @@ bUseAbsoluteTranslation=True bLevelStreamingVolumePrevis=False bUseUE3OrbitControls=False bUseDistanceScaledCameraSpeed=True +bOrbitCameraAroundSelection=True ;Scroll gesture direction ScrollGestureDirectionFor3DViewports=UseSystemSetting ScrollGestureDirectionForOrthoViewports=UseSystemSetting diff --git a/Engine/Config/BaseEngine.ini b/Engine/Config/BaseEngine.ini index d6dc208aedfe..f3f0275ef3f9 100644 --- a/Engine/Config/BaseEngine.ini +++ b/Engine/Config/BaseEngine.ini @@ -26,6 +26,10 @@ HttpConnectionTimeout=60 HttpReceiveTimeout=30 HttpSendTimeout=30 +[BackgroundHttp] +; How many days we will wait to delete any found BackgroundHTTP Temp Files that haven't been claimed. Default is 259200 seconds (3 days). -1 to disable. +BackgroundHttp.TempFileTimeOutSeconds=259200 + [WebSockets.LibWebSockets] ThreadStackSize=131072 ThreadTargetFrameTimeInSeconds=0.0333 @@ -1840,6 +1844,7 @@ bDevForArmV7S=False bShipForArmV7=False bShipForArm64=True bShipForArmV7S=False +bShipForBitcode=True bTreatRemoteAsSeparateController=False bAllowRemoteRotation=True bUseRemoteAsVirtualJoystick=True @@ -1869,6 +1874,7 @@ UseFastIntrinsics=False ForceFloats=False EnableMathOptimisations=True MaxShaderLanguageVersion=2 +bDisableMotionData=False bEnableAdvertisingIdentifier=True ; These are the defaults for Android settings, and they need to be in the .ini since UBT reads the .ini settings, without instantiating the class @@ -2221,6 +2227,8 @@ DefaultCompletionMode=RestoreState [PlatformCrypto] PlatformRequiresDataCrypto=False +PakSigningRequired=True +PakEncryptionRequired=True [/Script/AppleARKit.AppleARKitSettings] bEnableLiveLinkForFaceTracking=true diff --git a/Engine/Config/BaseGame.ini b/Engine/Config/BaseGame.ini index c85e0a5d89ce..de1a58606714 100644 --- a/Engine/Config/BaseGame.ini +++ b/Engine/Config/BaseGame.ini @@ -14,6 +14,8 @@ MAXNEARZEROVELOCITYSQUARED=9.0f CLIENTADJUSTUPDATECOST=180.0f MAXCLIENTUPDATEINTERVAL=0.25f MaxClientForcedUpdateDuration=1.0f +ServerForcedUpdateHitchThreshold=0.150f +ServerForcedUpdateHitchCooldown=0.100f MaxMoveDeltaTime=0.125f MaxClientSmoothingDeltaTime=0.50f ClientNetSendMoveDeltaTime=0.0166 diff --git a/Engine/Config/IOS/IOSEngine.ini b/Engine/Config/IOS/IOSEngine.ini index 6f4c6ee857c9..c857c2d24a79 100644 --- a/Engine/Config/IOS/IOSEngine.ini +++ b/Engine/Config/IOS/IOSEngine.ini @@ -7,6 +7,7 @@ gc.MaxObjectsInGame=131072 [PlatformCrypto] PlatformRequiresDataCrypto=True +PakSigningRequired=False [Audio] AudioDeviceModuleName=IOSAudio diff --git a/Engine/Extras/cl-filter/cl-filter.cpp b/Engine/Extras/cl-filter/cl-filter.cpp index 2fbbfa765db4..bbebf7f80ddc 100644 --- a/Engine/Extras/cl-filter/cl-filter.cpp +++ b/Engine/Extras/cl-filter/cl-filter.cpp @@ -99,9 +99,6 @@ int wmain(int ArgC, const wchar_t* ArgV[]) return -1; } - // Get the default console codepage - UINT CodePage = GetConsoleCP(); - // Pipe the output to stdout char Buffer[1024]; size_t BufferSize = 0; @@ -219,7 +216,7 @@ static std::string FindAndReplace(std::string Input, const std::string& FindStr, return Input; } -bool GetLocalizedIncludePrefix(const wchar_t* LibraryPath, HMODULE LibraryHandle, std::vector& Prefix) +bool GetLocalizedIncludePrefix(UINT CodePage, const wchar_t* LibraryPath, HMODULE LibraryHandle, std::vector& Prefix) { static const unsigned int ResourceId = 408; @@ -251,7 +248,7 @@ bool GetLocalizedIncludePrefix(const wchar_t* LibraryPath, HMODULE LibraryHandle Prefix.resize(Length + 1); // Get the multibyte text - int Result = WideCharToMultiByte(CP_ACP, 0, Text, (int)(TextEnd - Text), Prefix.data(), Length, NULL, NULL); + int Result = WideCharToMultiByte(CodePage, 0, Text, (int)(TextEnd - Text), Prefix.data(), Length, NULL, NULL); if (Result == 0) { wprintf(L"WARNING: unable to get MBCS string (input text '%s', library %s)", Text, LibraryPath); @@ -298,6 +295,9 @@ void GetLocalizedIncludePrefixes(const wchar_t* CompilerPath, std::vector Prefix; - if (GetLocalizedIncludePrefix(ResourceFile.c_str(), LibraryHandle, Prefix)) + if (GetLocalizedIncludePrefix(CodePage, ResourceFile.c_str(), LibraryHandle, Prefix)) { if (wcscmp(LocaleId.c_str(), L"1033") != 0) { diff --git a/Engine/Plugins/Developer/AnimationSharing/Source/AnimationSharing/Private/AnimationSharingManager.cpp b/Engine/Plugins/Developer/AnimationSharing/Source/AnimationSharing/Private/AnimationSharingManager.cpp index dfa397f1d2d9..f3deca4e727e 100644 --- a/Engine/Plugins/Developer/AnimationSharing/Source/AnimationSharing/Private/AnimationSharingManager.cpp +++ b/Engine/Plugins/Developer/AnimationSharing/Source/AnimationSharing/Private/AnimationSharingManager.cpp @@ -227,7 +227,7 @@ void UAnimationSharingManager::SetupPerSkeletonData(const FPerSkeletonAnimationS { UAnimSharingInstance* Data = NewObject(this); // Try and setup up instance using provided setup data - if (Data->Setup(this, SkeletonSetup, &ScalabilitySettings, Skeletons.Num() - 1)) + if (Data->Setup(this, SkeletonSetup, &ScalabilitySettings, Skeletons.Num())) { PerSkeletonData.Add(Data); Skeletons.Add(Skeleton); diff --git a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientDesktopPresenceActor.cpp b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientDesktopPresenceActor.cpp index 0f97772ed208..e1b52d4e5c96 100644 --- a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientDesktopPresenceActor.cpp +++ b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientDesktopPresenceActor.cpp @@ -54,9 +54,9 @@ void AConcertClientDesktopPresenceActor::SetPresenceColor(const FLinearColor& In TextMID->SetVectorParameterValue(ColorParamName, InColor); } -void AConcertClientDesktopPresenceActor::InitPresence(const class UConcertAssetContainer& InAssetContainer) +void AConcertClientDesktopPresenceActor::InitPresence(const class UConcertAssetContainer& InAssetContainer, FName DeviceType) { - Super::InitPresence(InAssetContainer); + Super::InitPresence(InAssetContainer, DeviceType); UMaterialInterface* PresenceMaterial = InAssetContainer.PresenceFadeMaterial; diff --git a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientDesktopPresenceActor.h b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientDesktopPresenceActor.h index cfab9c21fac8..16ba21d3e535 100644 --- a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientDesktopPresenceActor.h +++ b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientDesktopPresenceActor.h @@ -27,7 +27,7 @@ public: virtual void SetPresenceColor(const FLinearColor& InColor) override; /** AConcertClientPresenceActor Interface */ - virtual void InitPresence(const class UConcertAssetContainer& InAssetContainer) override; + virtual void InitPresence(const class UConcertAssetContainer& InAssetContainer, FName DeviceType) override; /** Handle presence update events */ virtual void HandleEvent(const FStructOnScope& InEvent) override; diff --git a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientPresenceActor.cpp b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientPresenceActor.cpp index 1fceb6a2099a..1960472b97c2 100644 --- a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientPresenceActor.cpp +++ b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientPresenceActor.cpp @@ -116,8 +116,9 @@ void AConcertClientPresenceActor::Tick(float DeltaSeconds) } } -void AConcertClientPresenceActor::InitPresence(const UConcertAssetContainer& InAssetContainer) +void AConcertClientPresenceActor::InitPresence(const UConcertAssetContainer& InAssetContainer, FName DeviceType) { + PresenceDeviceType = DeviceType; UStaticMesh* PresenceMesh = InAssetContainer.GenericDesktopMesh; if (PresenceMeshComponent->GetStaticMesh() == nullptr) diff --git a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientPresenceActor.h b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientPresenceActor.h index c29cbd8deefc..a014bf12e20e 100644 --- a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientPresenceActor.h +++ b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientPresenceActor.h @@ -49,13 +49,17 @@ public: virtual void HandleEvent(const FStructOnScope& InEvent); - virtual void InitPresence(const class UConcertAssetContainer& InAssetContainer); + virtual void InitPresence(const class UConcertAssetContainer& InAssetContainer, FName DeviceType); virtual bool ShouldTickIfViewportsOnly() const override; virtual void Tick(float DeltaSeconds) override; protected: + /* The device type that this presence represent (i.e Oculus, Vive, Desktop) */ + UPROPERTY(BlueprintReadOnly, Category = "Rendering", meta = (AllowPrivateAccess = "true")) + FName PresenceDeviceType; + /** The camera mesh component to show visually where the camera is placed */ UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Rendering", meta = (AllowPrivateAccess = "true")) class UStaticMeshComponent* PresenceMeshComponent; diff --git a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientPresenceManager.cpp b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientPresenceManager.cpp index a08838892496..57460563a511 100644 --- a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientPresenceManager.cpp +++ b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientPresenceManager.cpp @@ -34,6 +34,7 @@ #include "SLevelViewport.h" #include "ConcertAssetContainer.h" #include "IVREditorModule.h" +#include "VREditorMode.h" #include "Framework/Application/SlateApplication.h" #endif @@ -69,7 +70,7 @@ FConcertClientPresenceManager::FConcertClientPresenceManager(TSharedRefFindSessionClient(RemoteEndpointId, ClientSessionInfo); if (!PresenceState.PresenceActor.IsValid()) { - PresenceState.PresenceActor = CreatePresenceActor(ClientSessionInfo.ClientInfo, PresenceState.bInVR); + PresenceState.PresenceActor = CreatePresenceActor(ClientSessionInfo.ClientInfo, PresenceState.VRDevice); } if (PresenceState.PresenceActor.IsValid()) @@ -364,9 +365,9 @@ bool FConcertClientPresenceManager::ShouldProcessPresenceEvent(const FConcertSes return true; } -AConcertClientPresenceActor* FConcertClientPresenceManager::CreatePresenceActor(const FConcertClientInfo& InClientInfo, bool bClientInVR) +AConcertClientPresenceActor* FConcertClientPresenceManager::CreatePresenceActor(const FConcertClientInfo& InClientInfo, FName VRDevice) { - AConcertClientPresenceActor* PresenceActor = SpawnPresenceActor(InClientInfo, bClientInVR); + AConcertClientPresenceActor* PresenceActor = SpawnPresenceActor(InClientInfo, VRDevice); if (PresenceActor) { @@ -377,7 +378,7 @@ AConcertClientPresenceActor* FConcertClientPresenceManager::CreatePresenceActor( return PresenceActor; } -AConcertClientPresenceActor* FConcertClientPresenceManager::SpawnPresenceActor(const FConcertClientInfo& InClientInfo, bool bClientInVR) +AConcertClientPresenceActor* FConcertClientPresenceManager::SpawnPresenceActor(const FConcertClientInfo& InClientInfo, FName VRDevice) { check(AssetContainer); @@ -390,7 +391,7 @@ AConcertClientPresenceActor* FConcertClientPresenceManager::SpawnPresenceActor(c // @todo this is potentially slow and hitchy as clients connect. It might be better to preload all the presence actor types UClass* PresenceActorClass = nullptr; - if (bClientInVR) + if (!VRDevice.IsNone()) { PresenceActorClass = LoadObject(nullptr, *InClientInfo.VRAvatarActorClass); } @@ -401,7 +402,7 @@ AConcertClientPresenceActor* FConcertClientPresenceManager::SpawnPresenceActor(c if (!PresenceActorClass) { - UE_LOG(LogConcert, Warning, TEXT("Failed to load presence actor class '%s'. Presence will not be displayed"), bClientInVR ? *InClientInfo.VRAvatarActorClass : *InClientInfo.DesktopAvatarActorClass); + UE_LOG(LogConcert, Warning, TEXT("Failed to load presence actor class '%s'. Presence will not be displayed"), !VRDevice.IsNone() ? *InClientInfo.VRAvatarActorClass : *InClientInfo.DesktopAvatarActorClass); return nullptr; } @@ -413,6 +414,7 @@ AConcertClientPresenceActor* FConcertClientPresenceManager::SpawnPresenceActor(c ActorSpawnParameters.Name = MakeUniqueObjectName(World, PresenceActorClass, PresenceActorClass->GetFName()); // @todo how should spawned actors be named? ActorSpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; ActorSpawnParameters.ObjectFlags = EObjectFlags::RF_DuplicateTransient; + ActorSpawnParameters.bDeferConstruction = true; PresenceActor = World->SpawnActor(PresenceActorClass, ActorSpawnParameters); @@ -425,12 +427,16 @@ AConcertClientPresenceActor* FConcertClientPresenceManager::SpawnPresenceActor(c if (!PresenceActor) { - UE_LOG(LogConcert, Warning, TEXT("Failed to spawn presence actor of class '%s'. Presence will not be displayed"), bClientInVR ? *InClientInfo.VRAvatarActorClass : *InClientInfo.DesktopAvatarActorClass); + UE_LOG(LogConcert, Warning, TEXT("Failed to spawn presence actor of class '%s'. Presence will not be displayed"), !VRDevice.IsNone() ? *InClientInfo.VRAvatarActorClass : *InClientInfo.DesktopAvatarActorClass); return nullptr; } // Setup the asset container. - PresenceActor->InitPresence(*AssetContainer); + PresenceActor->InitPresence(*AssetContainer, VRDevice); + { + FEditorScriptExecutionGuard UCSGuard; + PresenceActor->FinishSpawning(FTransform(), true); + } return PresenceActor; } @@ -514,24 +520,25 @@ void FConcertClientPresenceManager::OnSessionClientChanged(IConcertClientSession void FConcertClientPresenceManager::OnVREditingModeEnter() { - bInVR = true; + UVREditorMode* VRMode = IVREditorModule::Get().GetVRMode(); + VRDeviceType = VRMode ? VRMode->GetHMDDeviceType() : FName(); UpdatePresenceMode(); } void FConcertClientPresenceManager::OnVREditingModeExit() { - bInVR = false; + VRDeviceType = FName(); UpdatePresenceMode(); } void FConcertClientPresenceManager::UpdatePresenceMode() { - if ((bInVR && CurrentAvatarActorClass != VRAvatarActorClass) || - (!bInVR && CurrentAvatarActorClass != DesktopAvatarActorClass)) + if ((!VRDeviceType.IsNone() && CurrentAvatarActorClass != VRAvatarActorClass) || + (VRDeviceType.IsNone() && CurrentAvatarActorClass != DesktopAvatarActorClass)) { // Mode will get recreated on next call to OnEndFrame CurrentAvatarMode.Reset(); - CurrentAvatarActorClass = bInVR ? VRAvatarActorClass : DesktopAvatarActorClass; + CurrentAvatarActorClass = !VRDeviceType.IsNone() ? VRAvatarActorClass : DesktopAvatarActorClass; SendPresenceInVREvent(); } } @@ -539,7 +546,7 @@ void FConcertClientPresenceManager::UpdatePresenceMode() void FConcertClientPresenceManager::SendPresenceInVREvent(const FGuid* InEndpointId) { FConcertClientPresenceInVREvent Event; - Event.bInVR = bInVR; + Event.VRDevice = VRDeviceType; if (InEndpointId) { @@ -553,13 +560,13 @@ void FConcertClientPresenceManager::SendPresenceInVREvent(const FGuid* InEndpoin void FConcertClientPresenceManager::HandleConcertClientPresenceInVREvent(const FConcertSessionContext& InSessionContext, const FConcertClientPresenceInVREvent& InEvent) { - UpdatePresenceAvatar(InSessionContext.SourceEndpointId, InEvent.bInVR); + UpdatePresenceAvatar(InSessionContext.SourceEndpointId, InEvent.VRDevice); } -void FConcertClientPresenceManager::UpdatePresenceAvatar(const FGuid& InEndpointId, bool bIsInVR) +void FConcertClientPresenceManager::UpdatePresenceAvatar(const FGuid& InEndpointId, FName VRDevice) { FConcertClientPresenceState& PresenceState = EnsurePresenceState(InEndpointId); - PresenceState.bInVR = bIsInVR; + PresenceState.VRDevice = VRDevice; if (PresenceState.PresenceActor.IsValid()) { @@ -725,7 +732,7 @@ FReply FConcertClientPresenceManager::OnJumpToPresenceClicked(FGuid InEndpointId FRotator OtherClientRotation(OtherClientState->Orientation.Rotator()); // Disregard pitch and roll when teleporting to a VR presence. - if (bInVR) + if (!VRDeviceType.IsNone()) { OtherClientRotation.Pitch = 0.0f; OtherClientRotation.Roll = 0.0f; diff --git a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientPresenceManager.h b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientPresenceManager.h index b4d925b812d3..f7c9de0f02bb 100644 --- a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientPresenceManager.h +++ b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientPresenceManager.h @@ -38,9 +38,10 @@ struct FConcertClientPresenceState { FConcertClientPresenceState() : bIsConnected(true) + , bVisible(true) , bInPIE(false) - , bInVR(false) - , bVisible(true) {} + , VRDevice(NAME_None) + {} /** State map */ TMap EventStateMap; @@ -51,14 +52,14 @@ struct FConcertClientPresenceState /** Whether client is connected */ bool bIsConnected; + /** Whether client is visible */ + bool bVisible; + /** Whether client is in PIE */ bool bInPIE; - /** Whether client is in VR editing mode */ - bool bInVR; - - /** Whether client is visible */ - bool bVisible; + /** Whether client is using a VRDevice */ + FName VRDevice; /** Presence actor */ TWeakObjectPtr PresenceActor; @@ -162,10 +163,10 @@ private: bool ShouldProcessPresenceEvent(const FConcertSessionContext& InSessionContext, const UStruct* InEventType, const FConcertClientPresenceEventBase& InEvent) const; /** Create a new presence actor */ - AConcertClientPresenceActor* CreatePresenceActor(const FConcertClientInfo& InClientInfo, bool bClientInVR); + AConcertClientPresenceActor* CreatePresenceActor(const FConcertClientInfo& InClientInfo, FName VRDevice); /** Spawn a presence actor */ - AConcertClientPresenceActor* SpawnPresenceActor(const FConcertClientInfo& InClientInfo, bool bClientInVR); + AConcertClientPresenceActor* SpawnPresenceActor(const FConcertClientInfo& InClientInfo, FName VRDevice); /** Clear presence */ void ClearPresenceActor(const FGuid& InEndpointId); @@ -198,7 +199,7 @@ private: void HandleConcertClientPresenceInVREvent(const FConcertSessionContext& InSessionContext, const FConcertClientPresenceInVREvent& InEvent); /** Updates presence avatar for remote client by invalidating current presence actor */ - void UpdatePresenceAvatar(const FGuid& InEndpointId, bool bIsInVR); + void UpdatePresenceAvatar(const FGuid& InEndpointId, FName VRDevice); /** Set presence PIE state */ void SetPresenceInPIE(const FGuid& InEndpointId, bool bInPIE); @@ -263,8 +264,8 @@ private: /** True if presence is currently enabled and should be shown (unless hidden by other settings) */ bool bIsPresenceEnabled; - /** True if in VR */ - bool bInVR; + /** NAME_None if not in VR */ + FName VRDeviceType; /** Avatar actor class */ UClass* CurrentAvatarActorClass; diff --git a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientVRPresenceActor.cpp b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientVRPresenceActor.cpp index b47826a9268a..4f7e8eb8939d 100644 --- a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientVRPresenceActor.cpp +++ b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientVRPresenceActor.cpp @@ -149,6 +149,7 @@ void AConcertClientVRPresenceActor::Tick(float DeltaSeconds) const FTransform RightControllerTransform(RightControllerOrientation, RightControllerPosition); RightControllerMeshComponent->SetWorldTransform(RightControllerTransform); + RightControllerMeshComponent->SetRelativeScale3D(FVector(1.0f, -1.0f, 1.0f)); } // Calculate laser @@ -167,12 +168,12 @@ void AConcertClientVRPresenceActor::Tick(float DeltaSeconds) } } -void AConcertClientVRPresenceActor::InitPresence(const class UConcertAssetContainer& InAssetContainer) +void AConcertClientVRPresenceActor::InitPresence(const class UConcertAssetContainer& InAssetContainer, FName DeviceType) { - Super::InitPresence(InAssetContainer); + Super::InitPresence(InAssetContainer, DeviceType); // To do, send data about these through the event. - UStaticMesh* ControllerMesh = InAssetContainer.VivePreControllerMesh; + UStaticMesh* ControllerMesh = PresenceDeviceType == FName(TEXT("OculusHMD")) ? InAssetContainer.OculusControllerMesh : InAssetContainer.VivePreControllerMesh; LeftControllerMeshComponent->SetStaticMesh(ControllerMesh); LeftControllerMeshComponent->SetMobility(EComponentMobility::Movable); diff --git a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientVRPresenceActor.h b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientVRPresenceActor.h index d590faa8031c..1d2d39bc2643 100644 --- a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientVRPresenceActor.h +++ b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncClient/Source/ConcertSyncClient/Private/ConcertClientVRPresenceActor.h @@ -19,7 +19,7 @@ class AConcertClientVRPresenceActor : public AConcertClientPresenceActor public: virtual void HandleEvent(const FStructOnScope& InEvent) override; - virtual void InitPresence(const class UConcertAssetContainer& InAssetContainer) override; + virtual void InitPresence(const class UConcertAssetContainer& InAssetContainer, FName DeviceType) override; virtual void SetPresenceColor(const FLinearColor& InColor) override; diff --git a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncCore/Source/ConcertSyncCore/Public/ConcertPresenceEvents.h b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncCore/Source/ConcertSyncCore/Public/ConcertPresenceEvents.h index a521d2ff802d..7c857bcee47a 100644 --- a/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncCore/Source/ConcertSyncCore/Public/ConcertPresenceEvents.h +++ b/Engine/Plugins/Developer/Concert/ConcertSync/ConcertSyncCore/Source/ConcertSyncCore/Public/ConcertPresenceEvents.h @@ -32,7 +32,7 @@ struct FConcertClientPresenceInVREvent GENERATED_BODY() UPROPERTY() - bool bInVR; + FName VRDevice; }; USTRUCT() diff --git a/Engine/Plugins/Editor/CryptoKeys/Source/CryptoKeys/Private/CryptoKeysHelpers.cpp b/Engine/Plugins/Editor/CryptoKeys/Source/CryptoKeys/Private/CryptoKeysHelpers.cpp index 602bec83ea8e..fa158b591808 100644 --- a/Engine/Plugins/Editor/CryptoKeys/Source/CryptoKeys/Private/CryptoKeysHelpers.cpp +++ b/Engine/Plugins/Editor/CryptoKeys/Source/CryptoKeys/Private/CryptoKeysHelpers.cpp @@ -26,12 +26,12 @@ namespace CryptoKeysHelpers return bResult; } - bool GenerateSigningKey(FString& OutPublicExponent, FString& OutPrivateExponent, FString& OutModulus) + bool GenerateSigningKey(FString& OutPublicExponent, FString& OutPrivateExponent, FString& OutModulus, int32 NumKeyBits) { bool bResult = false; TArray PublicExponent, PrivateExponent, Modulus; - if (CryptoKeysOpenSSL::GenerateNewSigningKey(PublicExponent, PrivateExponent, Modulus)) + if (CryptoKeysOpenSSL::GenerateNewSigningKey(PublicExponent, PrivateExponent, Modulus, NumKeyBits)) { OutPublicExponent = FBase64::Encode(PublicExponent); OutPrivateExponent = FBase64::Encode(PrivateExponent); diff --git a/Engine/Plugins/Editor/CryptoKeys/Source/CryptoKeys/Private/CryptoKeysHelpers.h b/Engine/Plugins/Editor/CryptoKeys/Source/CryptoKeys/Private/CryptoKeysHelpers.h index 11fe2671c797..a8b48f9f9fe1 100644 --- a/Engine/Plugins/Editor/CryptoKeys/Source/CryptoKeys/Private/CryptoKeysHelpers.h +++ b/Engine/Plugins/Editor/CryptoKeys/Source/CryptoKeys/Private/CryptoKeysHelpers.h @@ -2,6 +2,8 @@ #pragma once +#include "CoreTypes.h" + class FString; namespace CryptoKeysHelpers @@ -17,8 +19,9 @@ namespace CryptoKeysHelpers Generates a new RSA signing key @param OutPublicExponent - Assigned the base64 encoded representation of the RSA public exponent @param OutPrivateExponent - Assigned the base64 encoded representation of the RSA private exponent - @param OutModulus - Assigned the base64 encoded representation of the RSA modulus + @param OutModulus Assigned the base64 encoded representation of the RSA modulus + @param InNumKeyBits - How many bits to use for the RSA key @returns true if key generation succeeded, false otherwise */ - bool GenerateSigningKey(FString& OutPublicExponent, FString& OutPrivateExponent, FString& OutModulus); + bool GenerateSigningKey(FString& OutPublicExponent, FString& OutPrivateExponent, FString& OutModulus, int32 NumKeyBits = 4096); } \ No newline at end of file diff --git a/Engine/Plugins/Editor/CryptoKeys/Source/CryptoKeysOpenSSL/Private/CryptoKeysOpenSSL.cpp b/Engine/Plugins/Editor/CryptoKeys/Source/CryptoKeysOpenSSL/Private/CryptoKeysOpenSSL.cpp index c5eb98daf82f..046faa345ca2 100644 --- a/Engine/Plugins/Editor/CryptoKeys/Source/CryptoKeysOpenSSL/Private/CryptoKeysOpenSSL.cpp +++ b/Engine/Plugins/Editor/CryptoKeys/Source/CryptoKeysOpenSSL/Private/CryptoKeysOpenSSL.cpp @@ -9,15 +9,20 @@ #include #include #include +#include #if PLATFORM_WINDOWS #include "Windows/HideWindowsPlatformTypes.h" #endif -#include "Math/BigInt.h" - DEFINE_LOG_CATEGORY_STATIC(LogCryptoKeys, Log, All); +#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000L +#define USE_LEGACY_OPENSSL 1 +#else +#define USE_LEGACY_OPENSSL 0 +#endif + namespace CryptoKeysOpenSSL { bool GenerateNewEncryptionKey(TArray& OutKey) @@ -32,99 +37,42 @@ namespace CryptoKeysOpenSSL return bResult; } - bool TestKeys(FEncryptionKey& InPublicKey, FEncryptionKey& InPrivateKey) + void BigNumToArray(const BIGNUM* InNum, TArray& OutBytes, int32 InKeySize) { - UE_LOG(LogCryptoKeys, Display, TEXT("Testing signature keys.")); + int32 NumBytes = BN_num_bytes(InNum); + check(NumBytes <= InKeySize); + OutBytes.SetNumZeroed(NumBytes); - // Just some random values - static TEncryptionInt TestData[] = - { - 11, - 253, - 128, - 234, - 56, - 89, - 34, - 179, - 29, - 1024, - (int64)(MAX_int32), - (int64)(MAX_uint32)-1 - }; - - for (int32 TestIndex = 0; TestIndex < ARRAY_COUNT(TestData); ++TestIndex) - { - TEncryptionInt EncryptedData = FEncryption::ModularPow(TestData[TestIndex], InPrivateKey.Exponent, InPrivateKey.Modulus); - TEncryptionInt DecryptedData = FEncryption::ModularPow(EncryptedData, InPublicKey.Exponent, InPublicKey.Modulus); - if (TestData[TestIndex] != DecryptedData) - { - UE_LOG(LogCryptoKeys, Error, TEXT("Keys do not properly encrypt/decrypt data (failed test with %lld)"), TestData[TestIndex].ToInt()); - return false; - } - } - - UE_LOG(LogCryptoKeys, Display, TEXT("Signature keys check completed successfuly.")); - - return true; + BN_bn2bin(InNum, OutBytes.GetData()); + Algo::Reverse(OutBytes); } - bool GenerateNewSigningKey(TArray& OutPublicExponent, TArray& OutPrivateExponent, TArray& OutModulus) + bool GenerateNewSigningKey(TArray& OutPublicExponent, TArray& OutPrivateExponent, TArray& OutModulus, int32 InNumKeyBits) { - TArray NewP, NewQ; + int32 KeySize = InNumKeyBits; + int32 KeySizeInBytes = InNumKeyBits / 8; RSA* RSAKey = RSA_new(); BIGNUM* E = BN_new(); BN_set_word(E, RSA_F4); - RSA_generate_key_ex(RSAKey, 255, E, nullptr); + RSA_generate_key_ex(RSAKey, KeySize, E, nullptr); -#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000L - const BIGNUM* P = RSAKey->p; - const BIGNUM* Q = RSAKey->q; +#if USE_LEGACY_OPENSSL + const BIGNUM* PublicModulus = RSAKey->n; + const BIGNUM* PublicExponent = RSAKey->e; + const BIGNUM* PrivateExponent = RSAKey->d; #else - const BIGNUM* P = nullptr; - const BIGNUM* Q = nullptr; - // This does not increment the refcounts of P or Q - RSA_get0_factors(RSAKey, &P, &Q); + const BIGNUM* PublicModulus = RSA_get0_n(RSAKey); + const BIGNUM* PublicExponent = RSA_get0_e(RSAKey); + const BIGNUM* PrivateExponent = RSA_get0_d(RSAKey); #endif - uint32 NumBytes = BN_num_bytes(P); - - NewP.Empty(NumBytes); - NewP.AddUninitialized(NumBytes); - BN_bn2bin(P, NewP.GetData()); - P = nullptr; - - NumBytes = BN_num_bytes(Q); - - NewQ.Empty(NumBytes); - NewQ.AddUninitialized(NumBytes); - BN_bn2bin(Q, NewP.GetData()); - Q = nullptr; - + BigNumToArray(PublicModulus, OutModulus, KeySizeInBytes); + BigNumToArray(PublicExponent, OutPublicExponent, KeySizeInBytes); + BigNumToArray(PrivateExponent, OutPrivateExponent, KeySizeInBytes); + RSA_free(RSAKey); - NewP.AddZeroed(sizeof(TEncryptionInt) - NewP.Num()); - NewQ.AddZeroed(sizeof(TEncryptionInt) - NewQ.Num()); - - check(NewP.Num() == sizeof(TEncryptionInt)); - check(NewQ.Num() == sizeof(TEncryptionInt)); - - TEncryptionInt OurP((uint32*)&NewP[0]); - TEncryptionInt OurQ((uint32*)&NewQ[0]); - - FEncryptionKey PublicKey, PrivateKey; - FEncryption::GenerateKeyPair(OurP, OurQ, PublicKey, PrivateKey); - - TestKeys(PublicKey, PrivateKey); - - OutPublicExponent.AddUninitialized(sizeof(TEncryptionInt)); - OutPrivateExponent.AddUninitialized(sizeof(TEncryptionInt)); - OutModulus.AddUninitialized(sizeof(TEncryptionInt)); - FMemory::Memcpy(&OutPublicExponent[0], (const uint8*)PublicKey.Exponent.GetBits(), sizeof(TEncryptionInt)); - FMemory::Memcpy(&OutPrivateExponent[0], (const uint8*)PrivateKey.Exponent.GetBits(), sizeof(TEncryptionInt)); - FMemory::Memcpy(&OutModulus[0], (const uint8*)PublicKey.Modulus.GetBits(), sizeof(TEncryptionInt)); - return true; } } diff --git a/Engine/Plugins/Editor/CryptoKeys/Source/CryptoKeysOpenSSL/Public/CryptoKeysOpenSSL.h b/Engine/Plugins/Editor/CryptoKeys/Source/CryptoKeysOpenSSL/Public/CryptoKeysOpenSSL.h index d12b74aeeb99..4f1d562f3cf3 100644 --- a/Engine/Plugins/Editor/CryptoKeys/Source/CryptoKeysOpenSSL/Public/CryptoKeysOpenSSL.h +++ b/Engine/Plugins/Editor/CryptoKeys/Source/CryptoKeysOpenSSL/Public/CryptoKeysOpenSSL.h @@ -6,5 +6,5 @@ namespace CryptoKeysOpenSSL { bool CRYPTOKEYSOPENSSL_API GenerateNewEncryptionKey(TArray& OutKey); - bool CRYPTOKEYSOPENSSL_API GenerateNewSigningKey(TArray& OutPublicExponent, TArray& OutPrivateExponent, TArray& OutModulus); + bool CRYPTOKEYSOPENSSL_API GenerateNewSigningKey(TArray& OutPublicExponent, TArray& OutPrivateExponent, TArray& OutModulus, int32 InNumKeyBits = 2048); } \ No newline at end of file diff --git a/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Private/EditorLevelLibrary.cpp b/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Private/EditorLevelLibrary.cpp index 5080d1a9b6a5..e2137801cade 100644 --- a/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Private/EditorLevelLibrary.cpp +++ b/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Private/EditorLevelLibrary.cpp @@ -29,6 +29,8 @@ #include "MeshMergeModule.h" #include "ScopedTransaction.h" #include "UnrealEdGlobals.h" +#include "LevelEditor.h" +#include "ILevelViewport.h" #define LOCTEXT_NAMESPACE "EditorLevelLibrary" @@ -59,6 +61,21 @@ namespace InternalEditorLevelLibrary return GEditor ? GEditor->GetEditorWorldContext(false).World() : nullptr; } + UWorld* GetGameWorld() + { + if (GEditor) + { + if (FWorldContext* WorldContext = GEditor->GetPIEWorldContext()) + { + return WorldContext->World(); + } + + return nullptr; + } + + return GWorld; + } + template TArray GetAllLoadedObjects() { @@ -181,10 +198,94 @@ void UEditorLevelLibrary::SetSelectedLevelActors(const TArray& Ac { GEditor->SelectNone(true, true, false); } - - return; } +void UEditorLevelLibrary::PilotLevelActor(AActor* ActorToPilot) +{ + FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); + + TSharedPtr ActiveLevelViewport = LevelEditorModule.GetFirstActiveViewport(); + if (ActiveLevelViewport.IsValid()) + { + FLevelEditorViewportClient& LevelViewportClient = ActiveLevelViewport->GetLevelViewportClient(); + + LevelViewportClient.SetActorLock(ActorToPilot); + if (LevelViewportClient.IsPerspective() && LevelViewportClient.GetActiveActorLock().IsValid()) + { + LevelViewportClient.MoveCameraToLockedActor(); + } + } +} + +void UEditorLevelLibrary::EjectPilotLevelActor() +{ + FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); + + TSharedPtr ActiveLevelViewport = LevelEditorModule.GetFirstActiveViewport(); + if (ActiveLevelViewport.IsValid()) + { + FLevelEditorViewportClient& LevelViewportClient = ActiveLevelViewport->GetLevelViewportClient(); + + if (AActor* LockedActor = LevelViewportClient.GetActiveActorLock().Get()) + { + //// Check to see if the locked actor was previously overriding the camera settings + //if (CanGetCameraInformationFromActor(LockedActor)) + //{ + // // Reset the settings + // LevelViewportClient.ViewFOV = LevelViewportClient.FOVAngle; + //} + + LevelViewportClient.SetActorLock(nullptr); + + // remove roll and pitch from camera when unbinding from actors + GEditor->RemovePerspectiveViewRotation(true, true, false); + } + } +} + + +void UEditorLevelLibrary::EditorSetGameView(bool bGameView) +{ + FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); + + TSharedPtr ActiveLevelViewport = LevelEditorModule.GetFirstActiveViewport(); + if (ActiveLevelViewport.IsValid()) + { + if (ActiveLevelViewport->IsInGameView() != bGameView) + { + ActiveLevelViewport->ToggleGameView(); + } + } +} + +#if WITH_EDITOR + +void UEditorLevelLibrary::EditorPlaySimulate() +{ + FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); + + TSharedPtr ActiveLevelViewport = LevelEditorModule.GetFirstActiveViewport(); + if (ActiveLevelViewport.IsValid()) + { + const bool bSimulateInEditor = true; + GUnrealEd->RequestPlaySession(false, ActiveLevelViewport, bSimulateInEditor, NULL, NULL, -1, false); + } +} + +void UEditorLevelLibrary::EditorInvalidateViewports() +{ + FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); + + TSharedPtr ActiveLevelViewport = LevelEditorModule.GetFirstActiveViewport(); + if (ActiveLevelViewport.IsValid()) + { + FLevelEditorViewportClient& LevelViewportClient = ActiveLevelViewport->GetLevelViewportClient(); + LevelViewportClient.Invalidate(); + } +} + +#endif + namespace InternalEditorLevelLibrary { AActor* SpawnActor(const TCHAR* MessageName, UObject* ObjToUse, FVector Location, FRotator Rotation) @@ -329,6 +430,14 @@ UWorld* UEditorLevelLibrary::GetEditorWorld() return InternalEditorLevelLibrary::GetEditorWorld(); } +UWorld* UEditorLevelLibrary::GetGameWorld() +{ + TGuardValue UnattendedScriptGuard(GIsRunningUnattendedScript, true); + + return InternalEditorLevelLibrary::GetGameWorld(); +} + + /** * * Editor Scripting | Level diff --git a/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorLevelLibrary.h b/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorLevelLibrary.h index 638c9c3f4106..6f8c3e6a0786 100644 --- a/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorLevelLibrary.h +++ b/Engine/Plugins/Editor/EditorScriptingUtilities/Source/EditorScriptingUtilities/Public/EditorLevelLibrary.h @@ -113,6 +113,25 @@ public: UFUNCTION(BlueprintCallable, Category = "Editor Scripting | Level Utility") static void SetSelectedLevelActors(const TArray& ActorsToSelect); + UFUNCTION(BlueprintCallable, Category = "Editor Scripting | Level Utility", meta=(DevelopmentOnly)) + static void PilotLevelActor(AActor* ActorToPilot); + + UFUNCTION(BlueprintCallable, Category = "Editor Scripting | Level Utility", meta=(DevelopmentOnly)) + static void EjectPilotLevelActor(); + +#if WITH_EDITOR + + UFUNCTION(BlueprintCallable, Category = "Editor Scripting | Level Utility", meta = (DevelopmentOnly)) + static void EditorPlaySimulate(); + + UFUNCTION(BlueprintCallable, Category = "Editor Scripting | Level Utility", meta = (DevelopmentOnly)) + static void EditorInvalidateViewports(); + +#endif + + UFUNCTION(BlueprintCallable, Category = "Editor Scripting | Level Utility", meta = (DevelopmentOnly)) + static void EditorSetGameView(bool bGameView); + /** * Create an actor and place it in the world editor. The Actor can be created from a Factory, Archetype, Blueprint, Class or an Asset. * The actor will be created in the current level and will be selected. @@ -148,6 +167,9 @@ public: UFUNCTION(BlueprintCallable, Category = "Editor Scripting | Level Utility") static UWorld* GetEditorWorld(); + UFUNCTION(BlueprintCallable, Category = "Editor Scripting | Level Utility") + static UWorld* GetGameWorld(); + public: /** * Close the current Persistent Level (without saving it). Create a new blank Level and save it. Load the new created level. diff --git a/Engine/Plugins/Enterprise/DatasmithContent/Source/DatasmithContent/Private/ObjectTemplates/DatasmithLandscapeTemplate.cpp b/Engine/Plugins/Enterprise/DatasmithContent/Source/DatasmithContent/Private/ObjectTemplates/DatasmithLandscapeTemplate.cpp index cf9895b32dec..7ffbc762f67b 100644 --- a/Engine/Plugins/Enterprise/DatasmithContent/Source/DatasmithContent/Private/ObjectTemplates/DatasmithLandscapeTemplate.cpp +++ b/Engine/Plugins/Enterprise/DatasmithContent/Source/DatasmithContent/Private/ObjectTemplates/DatasmithLandscapeTemplate.cpp @@ -3,11 +3,12 @@ #include "ObjectTemplates/DatasmithLandscapeTemplate.h" #include "Landscape.h" +#include "ObjectTemplates/DatasmithActorTemplate.h" void UDatasmithLandscapeTemplate::Apply( UObject* Destination, bool bForce ) { #if WITH_EDITORONLY_DATA - ALandscape* Landscape = Cast< ALandscape >( Destination ); + ALandscape* Landscape = UDatasmithActorTemplate::GetActor< ALandscape >( Destination ); if( !Landscape ) { @@ -26,7 +27,7 @@ void UDatasmithLandscapeTemplate::Apply( UObject* Destination, bool bForce ) void UDatasmithLandscapeTemplate::Load( const UObject* Source ) { #if WITH_EDITORONLY_DATA - const ALandscape* Landscape = Cast< ALandscape >( Source ); + const ALandscape* Landscape = UDatasmithActorTemplate::GetActor< ALandscape >( Source ); if( !Landscape ) { diff --git a/Engine/Plugins/Enterprise/DatasmithContent/Source/DatasmithContent/Public/ObjectTemplates/DatasmithObjectTemplate.h b/Engine/Plugins/Enterprise/DatasmithContent/Source/DatasmithContent/Public/ObjectTemplates/DatasmithObjectTemplate.h index ef011b6573a8..e42156d7f456 100644 --- a/Engine/Plugins/Enterprise/DatasmithContent/Source/DatasmithContent/Public/ObjectTemplates/DatasmithObjectTemplate.h +++ b/Engine/Plugins/Enterprise/DatasmithContent/Source/DatasmithContent/Public/ObjectTemplates/DatasmithObjectTemplate.h @@ -58,7 +58,7 @@ public: #define DATASMITHOBJECTTEMPLATE_CONDITIONALSETSOFTOBJECTPTR(MemberName, Destination, PreviousTemplate) \ if ( !PreviousTemplate || Destination->MemberName == PreviousTemplate->MemberName.Get() ) \ { \ - Destination->MemberName = MemberName.Get(); \ + Destination->MemberName = MemberName.LoadSynchronous(); \ } struct DATASMITHCONTENT_API FDatasmithObjectTemplateUtils diff --git a/Engine/Plugins/Experimental/AlembicImporter/Source/AlembicImporter/Private/AlembicImportFactory.cpp b/Engine/Plugins/Experimental/AlembicImporter/Source/AlembicImporter/Private/AlembicImportFactory.cpp index 37d8d955f9f5..a4fff4087ee6 100644 --- a/Engine/Plugins/Experimental/AlembicImporter/Source/AlembicImporter/Private/AlembicImportFactory.cpp +++ b/Engine/Plugins/Experimental/AlembicImporter/Source/AlembicImporter/Private/AlembicImportFactory.cpp @@ -82,7 +82,7 @@ UObject* UAlembicImportFactory::FactoryCreateFile(UClass* InClass, UObject* InPa bOutOperationCanceled = false; - if (bShowOption) + if (!GIsRunningUnattendedScript && bShowOption) { TSharedPtr Options; ShowImportOptionsWindow(Options, UFactory::CurrentFilename, Importer); diff --git a/Engine/Plugins/Experimental/AutomationUtils/AutomationUtils.uplugin b/Engine/Plugins/Experimental/AutomationUtils/AutomationUtils.uplugin new file mode 100644 index 000000000000..1ca5bad99e1f --- /dev/null +++ b/Engine/Plugins/Experimental/AutomationUtils/AutomationUtils.uplugin @@ -0,0 +1,34 @@ +{ + "FileVersion": 1, + "Version": 1, + "VersionName": "0.1", + "FriendlyName": "Automation Utilities", + "Description": "Tools and Utilities for Automation purposes", + "Category": "Automation", + "CreatedBy": "Epic Games, Inc.", + "CreatedByURL": "http://epicgames.com", + "MarketplaceURL": "", + "SupportURL": "", + "EnabledByDefault": true, + "CanContainContent": true, + "IsBetaVersion": true, + "Installed": false, + "Modules": [ + { + "Name": "AutomationUtils", + "Type": "Runtime", + "LoadingPhase": "Default" + }, + { + "Name": "AutomationUtilsEditor", + "Type": "Editor", + "LoadingPhase": "Default" + } + ], + "Plugins": [ + { + "Name": "ScreenshotTools", + "Enabled": true + } + ] +} \ No newline at end of file diff --git a/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtils/AutomationUtils.Build.cs b/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtils/AutomationUtils.Build.cs new file mode 100644 index 000000000000..8748653f6983 --- /dev/null +++ b/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtils/AutomationUtils.Build.cs @@ -0,0 +1,31 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +namespace UnrealBuildTool.Rules +{ + public class AutomationUtils : ModuleRules + { + public AutomationUtils(ReadOnlyTargetRules Target) : base(Target) + { + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "CoreUObject", + "Engine", + } + ); + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "Core", + "CoreUObject", + "Engine", + "Json", + "RHI", + "RenderCore" + } + ); + } + } +} diff --git a/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtils/Private/AutomationUtils.cpp b/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtils/Private/AutomationUtils.cpp new file mode 100644 index 000000000000..3dfc012da466 --- /dev/null +++ b/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtils/Private/AutomationUtils.cpp @@ -0,0 +1,7 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +#include "Modules/ModuleManager.h" + +//Module Implementation Call +IMPLEMENT_MODULE(FDefaultModuleImpl, AutomationUtils) + diff --git a/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtils/Private/AutomationUtilsBlueprintLibrary.cpp b/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtils/Private/AutomationUtilsBlueprintLibrary.cpp new file mode 100644 index 000000000000..89615994f368 --- /dev/null +++ b/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtils/Private/AutomationUtilsBlueprintLibrary.cpp @@ -0,0 +1,289 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +#include "AutomationUtilsBlueprintLibrary.h" +#include "SceneViewExtension.h" +#include "SceneView.h" +#include "ShaderCompiler.h" +#include "ContentStreaming.h" +#include "ImageUtils.h" +#include "Misc/EngineVersion.h" +#include "HardwareInfo.h" +#include "EngineUtils.h" +#include "Materials/MaterialInstanceDynamic.h" +#include "MeshDrawShaderBindings.h" +#include "Scalability.h" +#include "Dom/JsonObject.h" +#include "Engine/Engine.h" +#include "Misc/FileHelper.h" +#include "Serialization/JsonWriter.h" +#include "Serialization/JsonSerializer.h" + + + + +//Private Helper Class Definitions +class FAutomationUtilsGameplayViewExtension : public FSceneViewExtensionBase +{ +public: + FAutomationUtilsGameplayViewExtension(const FAutoRegister& AutoRegister) + : FSceneViewExtensionBase(AutoRegister) + { + } + + void SetupViewFamily(FSceneViewFamily& InViewFamily) override + { + // Turn off common show flags for noisy sources of rendering. + FEngineShowFlags& ShowFlags = InViewFamily.EngineShowFlags; + ShowFlags.SetAntiAliasing(0); + ShowFlags.SetMotionBlur(0); + ShowFlags.SetTemporalAA(0); + ShowFlags.SetScreenSpaceReflections(0); + ShowFlags.SetScreenSpaceAO(0); + ShowFlags.SetDistanceFieldAO(0); + ShowFlags.SetContactShadows(0); + ShowFlags.SetEyeAdaptation(0); + ShowFlags.SetBloom(0); + + // Turn off time the ultimate source of noise. + InViewFamily.CurrentWorldTime = 0; + InViewFamily.CurrentRealTime = 0; + InViewFamily.DeltaWorldTime = 0; + } + + bool IsActiveThisFrame(class FViewport* InViewport) const + { + return true; + } + + void SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) override {} + void BeginRenderViewFamily(FSceneViewFamily& InViewFamily) override {} + void PreRenderViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneViewFamily& InViewFamily) override {} + void PreRenderView_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneView& InView) override {} + + /** We always want to go last. */ + virtual int32 GetPriority() const override { return MIN_int32; } +}; + + +class FAutomationUtilsGameplayAutomationScreenshotInstance +{ +public: + FAutomationUtilsGameplayAutomationScreenshotInstance(FString InScreenshotName, float MaxGlobalError, float MaxLocalError) + : ScreenshotName(InScreenshotName) + , MetadataJsonString(TEXT("{}")) + , DeterminedPath(TEXT("")) + , World(GWorld.GetReference()) + { + if (GEngine && GEngine->GameViewport) + { + //FlushRendering + FlushRenderingCommands(); + //hook to the screenshot delegate + GEngine->GameViewport->OnScreenshotCaptured().AddRaw(this, &FAutomationUtilsGameplayAutomationScreenshotInstance::HandleScreenshotData); + //And a removed-from-world delegate too, just in case + FWorldDelegates::LevelRemovedFromWorld.AddRaw(this, &FAutomationUtilsGameplayAutomationScreenshotInstance::WorldDestroyed); + + + + //Generate Json Metadata relevant to rendering device, quality settings, and comparison tolerances + TSharedPtr JsonObject = MakeShareable(new FJsonObject); + //General Stuff + JsonObject->SetStringField(TEXT("name"), FPaths::MakeValidFileName(InScreenshotName, TEXT('_'))); + JsonObject->SetStringField(TEXT("context"), GWorld->GetName()); + JsonObject->SetStringField(TEXT("id"), FGuid::NewGuid().ToString()); + JsonObject->SetStringField(TEXT("Commit"), FEngineVersion::Current().HasChangelist() ? FString::FromInt(FEngineVersion::Current().GetChangelist()) : FString(TEXT(""))); + FVector2D ViewportSize; //Width and Height + GEngine->GameViewport->GetViewportSize(ViewportSize); + JsonObject->SetNumberField(TEXT("width"), ViewportSize.X); + JsonObject->SetNumberField(TEXT("height"), ViewportSize.Y); + //RHI + JsonObject->SetStringField(TEXT("platform"), FPlatformProperties::IniPlatformName()); + JsonObject->SetStringField(TEXT("rhi"), FHardwareInfo::GetHardwareInfo(NAME_RHI)); + FString FeatureLevel; + GetFeatureLevelName(GMaxRHIFeatureLevel, FeatureLevel); + JsonObject->SetStringField(TEXT("featureLevel"), FeatureLevel); + JsonObject->SetBoolField(TEXT("bIsStereo"), GEngine->StereoRenderingDevice.IsValid() ? GEngine->StereoRenderingDevice->IsStereoEnabled() : false); + //Vendor + JsonObject->SetStringField(TEXT("vendor"), RHIVendorIdToString()); + JsonObject->SetStringField(TEXT("adapterName"), GRHIAdapterName); + JsonObject->SetStringField(TEXT("adapterInternalDriverVersion"), GRHIAdapterInternalDriverVersion); + JsonObject->SetStringField(TEXT("adapterUserDriverVersion"), GRHIAdapterUserDriverVersion); + JsonObject->SetStringField(TEXT("uniqueDeviceId"), FPlatformMisc::GetDeviceId()); + //Quality + Scalability::FQualityLevels QualityLevels = Scalability::GetQualityLevels(); + JsonObject->SetNumberField(TEXT("resolutionQuality"), QualityLevels.ResolutionQuality); + JsonObject->SetNumberField(TEXT("viewDistanceQuality"), QualityLevels.ViewDistanceQuality); + JsonObject->SetNumberField(TEXT("antiAliasingQuality"), QualityLevels.AntiAliasingQuality); + JsonObject->SetNumberField(TEXT("shadowQuality"), QualityLevels.ShadowQuality); + JsonObject->SetNumberField(TEXT("postProcessQuality"), QualityLevels.PostProcessQuality); + JsonObject->SetNumberField(TEXT("textureQuality"), QualityLevels.TextureQuality); + JsonObject->SetNumberField(TEXT("effectsQuality"), QualityLevels.EffectsQuality); + JsonObject->SetNumberField(TEXT("foliageQuality"), QualityLevels.FoliageQuality); + //ComparisonOptions + JsonObject->SetBoolField(TEXT("bHasComparisonRules"), true); + JsonObject->SetNumberField(TEXT("toleranceRed"), 8); + JsonObject->SetNumberField(TEXT("toleranceGreen"), 8); + JsonObject->SetNumberField(TEXT("toleranceBlue"), 8); + JsonObject->SetNumberField(TEXT("toleranceAlpha"), 8); + JsonObject->SetNumberField(TEXT("toleranceMinBrightness"), 0); + JsonObject->SetNumberField(TEXT("toleranceMaxBrightness"), 255); + JsonObject->SetNumberField(TEXT("maximumLocalError"), MaxLocalError); + JsonObject->SetNumberField(TEXT("maximumGlobalError"), MaxGlobalError); + JsonObject->SetBoolField(TEXT("bIgnoreAntiAliasing"), true); + JsonObject->SetBoolField(TEXT("bIgnoreColors"), false); + + + + //Serialize to String + TSharedRef> Writer = TJsonWriterFactory<>::Create(&MetadataJsonString); + FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer); + GLog->Log(FString::Printf(TEXT("Gameplay Automation Screenshot Metadata Serialized to %d characters"), MetadataJsonString.Len())); + + + + //Output path for both screenshot image and metadata json + DeterminedPath = FPaths::AutomationDir() / TEXT("Incoming") / GWorld->GetName() / InScreenshotName / FPlatformProperties::IniPlatformName(); + + //we parse some stuff out of HardwareDetailsString and make a pretty folder name + FString HardwareDetailsString; + FString HardwareDetails = FHardwareInfo::GetHardwareDetailsString(); + + FString RHIString; + FString RHILookup = NAME_RHI.ToString() + TEXT("="); + if (FParse::Value(*HardwareDetails, *RHILookup, RHIString)) + { + HardwareDetailsString = (HardwareDetailsString + TEXT("_")) + RHIString; + } + + FString TextureFormatString; + FString TextureFormatLookup = NAME_TextureFormat.ToString() + TEXT("="); + if (FParse::Value(*HardwareDetails, *TextureFormatLookup, TextureFormatString)) + { + HardwareDetailsString = (HardwareDetailsString + TEXT("_")) + TextureFormatString; + } + + FString DeviceTypeString; + FString DeviceTypeLookup = NAME_DeviceType.ToString() + TEXT("="); + if (FParse::Value(*HardwareDetails, *DeviceTypeLookup, DeviceTypeString)) + { + HardwareDetailsString = (HardwareDetailsString + TEXT("_")) + TextureFormatString; + } + + //Also add FeatureLevel from earlier + HardwareDetailsString = (HardwareDetailsString + TEXT("_")) + FeatureLevel; + + if (HardwareDetailsString.Len() > 0) + { + //remove leading "_" + HardwareDetailsString = HardwareDetailsString.RightChop(1); + } + + //now plop that back onto the path we're building + DeterminedPath = DeterminedPath / HardwareDetailsString; + DeterminedPath = DeterminedPath / FPlatformMisc::GetDeviceId() + TEXT(".png"); + + //Remove as many noisy rendering conditions as we can until the screenshot has been taken + AutomationViewExtension = FSceneViewExtensions::NewExtension(); + + GLog->Log(FString::Printf(TEXT("Determined Path for screenshot \"%s\" to be %s"), *ScreenshotName, *DeterminedPath)); + } + } + + ~FAutomationUtilsGameplayAutomationScreenshotInstance() + { + Unbind(); + } + +public: + void HandleScreenshotData(int32 InSizeX, int32 InSizeY, const TArray& InImageData) + { + check(IsInGameThread()); + + + GLog->Log(FString::Printf(TEXT("Gameplay Automation Screenshot \"%s\" taken with size: %d x %d"), *ScreenshotName, InSizeX, InSizeY)); + + //create directory if it doesn't exist + IFileManager::Get().MakeDirectory(*FPaths::GetPath(DeterminedPath), true); + + //Save Image File + TArray CompressedBitmap; + FImageUtils::CompressImageArray(InSizeX, InSizeY, InImageData, CompressedBitmap); + FFileHelper::SaveArrayToFile(CompressedBitmap, *DeterminedPath); + GLog->Log(FString::Printf(TEXT("Saved %d bytes of screenshot image to %s"), CompressedBitmap.Num(), *DeterminedPath)); + + //Save Metadata Json + FString MetadataPath = FPaths::ChangeExtension(DeterminedPath, TEXT("json")); + FFileHelper::SaveStringToFile(MetadataJsonString, *MetadataPath, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM); + GLog->Log(FString::Printf(TEXT("Saved %d bytes of metadata json to %s"), MetadataJsonString.Len(), *MetadataPath)); + + //remove our rendering options + AutomationViewExtension.Reset(); + + //deallocate this object, it's done its part + delete this; + } + + void WorldDestroyed(ULevel* InLevel, UWorld* InWorld) + { + if (InLevel == nullptr && InWorld == World.Get()) + { + GLog->Log(FString::Printf(TEXT("Screenshot \"%s\" skipped - level was removed from world before we got our screenshot"), *ScreenshotName)); + //nothing left for this object to do (we missed the timing somehow), so clean up + delete this; + } + } + +private: + void Unbind() + { + if (GEngine && GEngine->GameViewport) + { + GEngine->GameViewport->OnScreenshotCaptured().RemoveAll(this); + } + FWorldDelegates::LevelRemovedFromWorld.RemoveAll(this); + } + +private: + FString ScreenshotName; + FString MetadataJsonString; + FString DeterminedPath; + TWeakObjectPtr World; + TSharedPtr< class FAutomationUtilsGameplayViewExtension, ESPMode::ThreadSafe > AutomationViewExtension; +}; + + + + + +//Blueprint Library +UAutomationUtilsBlueprintLibrary::UAutomationUtilsBlueprintLibrary(const FObjectInitializer& ObjectInitializer) +: Super(ObjectInitializer) +{ +} + +void UAutomationUtilsBlueprintLibrary::TakeGameplayAutomationScreenshot(const FString& ScreenshotName, float MaxGlobalError, float MaxLocalError) +{ + //Finish Loading Before Screenshot + if (!FPlatformProperties::RequiresCookedData()) + { + //Finish Compiling all shaders + GShaderCompilingManager->FinishAllCompilation(); + } + + //Stream in everything + IStreamingManager::Get().StreamAllResources(0.0f); + + //Force all mip maps to load + UTexture::ForceUpdateTextureStreaming(); + + //Allocate automation object + //this new may look scary, but the object deletes itself when its screenshot is processed + //(same method as in FTest) + FAutomationUtilsGameplayAutomationScreenshotInstance* TempObject = new FAutomationUtilsGameplayAutomationScreenshotInstance(ScreenshotName, MaxGlobalError, MaxLocalError); + + //Actually Take Screenshot + FScreenshotRequest::RequestScreenshot(ScreenshotName, false, true); +} + + + diff --git a/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtils/Public/AutomationUtilsBlueprintLibrary.h b/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtils/Public/AutomationUtilsBlueprintLibrary.h new file mode 100644 index 000000000000..22e68c421fc5 --- /dev/null +++ b/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtils/Public/AutomationUtilsBlueprintLibrary.h @@ -0,0 +1,18 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" +#include "Engine/LatentActionManager.h" +#include "Kismet/BlueprintFunctionLibrary.h" + + +#include "AutomationUtilsBlueprintLibrary.generated.h" + +UCLASS() +class AUTOMATIONUTILS_API UAutomationUtilsBlueprintLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_UCLASS_BODY() + + UFUNCTION(BlueprintCallable, Category = "Automation") + static void TakeGameplayAutomationScreenshot(const FString& ScreenshotName, float MaxGlobalError = .02, float MaxLocalError = .12); +}; \ No newline at end of file diff --git a/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtilsEditor/AutomationUtilsEditor.Build.cs b/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtilsEditor/AutomationUtilsEditor.Build.cs new file mode 100644 index 000000000000..507ab153b6fb --- /dev/null +++ b/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtilsEditor/AutomationUtilsEditor.Build.cs @@ -0,0 +1,20 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +namespace UnrealBuildTool.Rules +{ + public class AutomationUtilsEditor : ModuleRules + { + public AutomationUtilsEditor(ReadOnlyTargetRules Target) : base(Target) + { + PrivateDependencyModuleNames.AddRange( + new string[] + { + "Core", + "CoreUObject", + "Engine", + "ScreenshotTools" + } + ); + } + } +} diff --git a/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtilsEditor/Private/AutomationUtilsEditor.cpp b/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtilsEditor/Private/AutomationUtilsEditor.cpp new file mode 100644 index 000000000000..a9b93d786d50 --- /dev/null +++ b/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtilsEditor/Private/AutomationUtilsEditor.cpp @@ -0,0 +1,26 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +#include "CoreMinimal.h" +#include "Modules/ModuleInterface.h" +#include "Modules/ModuleManager.h" + +#define LOCTEXT_NAMESPACE "AutomationUtilsEditor" + +class FAutomationUtilsEditorModule : public IModuleInterface +{ +public: + + virtual void StartupModule() override + { + + } + + virtual void ShutdownModule() override + { + + } +}; + +IMPLEMENT_MODULE( FAutomationUtilsEditorModule, AutomationUtilsEditor); + +#undef LOCTEXT_NAMESPACE diff --git a/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtilsEditor/Private/ScreenshotComparisonCommandlet.cpp b/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtilsEditor/Private/ScreenshotComparisonCommandlet.cpp new file mode 100644 index 000000000000..855b811ededb --- /dev/null +++ b/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtilsEditor/Private/ScreenshotComparisonCommandlet.cpp @@ -0,0 +1,109 @@ +#include "ScreenshotComparisonCommandlet.h" +#include "Misc/Paths.h" +#include "Modules/ModuleManager.h" +#include "Interfaces/IScreenShotToolsModule.h" +#include "Interfaces/IScreenShotManager.h" +#include "HAL/FileManager.h" + + +DEFINE_LOG_CATEGORY(LogScreenshotComparison); + +UScreenshotComparisonCommandlet::UScreenshotComparisonCommandlet(const FObjectInitializer& ObjectInitializer) +: Super(ObjectInitializer) +{ +} + +int32 UScreenshotComparisonCommandlet::Main(const FString& CmdLineParameters) +{ + FString IncomingPath = FPaths::AutomationDir() / TEXT("Incoming/"); + FString GroundTruthPath = FPaths::ProjectDir() / TEXT("Test") / TEXT("Screenshots/"); + + TArray MapList; + FString MapListStr; + if (FParse::Value(*CmdLineParameters, TEXT("Maps="), MapListStr)) + { + MapListStr.ParseIntoArray(MapList, TEXT("+"), true); + } + + UE_LOG(LogScreenshotComparison, Log, TEXT("Incoming Path: %s"), *IncomingPath); + UE_LOG(LogScreenshotComparison, Log, TEXT("Ground Truth Path: %s"), *GroundTruthPath); + + //Get All Screenshots in Incoming + TArray FilesToCompare; + if (FPaths::DirectoryExists(IncomingPath)) + { + FString AbsoluteIncomingPath = IncomingPath; + FPaths::MakePathRelativeTo(AbsoluteIncomingPath, *FPaths::RootDir()); + AbsoluteIncomingPath = FPaths::RootDir() / AbsoluteIncomingPath; + + + if (MapList.Num()) + { + for (FString LevelName : MapList) + { + TArray LevelFiles; + IFileManager::Get().FindFilesRecursive(LevelFiles, *(AbsoluteIncomingPath / LevelName), TEXT("*.png"), true, false); + FilesToCompare.Append(LevelFiles); + } + } + else + { + //No Restrictions -> get ALL images + IFileManager::Get().FindFilesRecursive(FilesToCompare, *AbsoluteIncomingPath, TEXT("*.png"), true, false); + } + } + + if (FilesToCompare.Num()) + { + UE_LOG(LogScreenshotComparison, Log, TEXT("Comparing %d screenshots"), FilesToCompare.Num()); + } + else + { + UE_LOG(LogScreenshotComparison, Warning, TEXT("Found no screenshots (*.png) in IncomingPath %s"), *IncomingPath); + } + + + + //Do comparison + IScreenShotToolsModule& ScreenShotModule = FModuleManager::LoadModuleChecked("ScreenShotComparisonTools"); + IScreenShotManagerPtr ScreenshotManager = ScreenShotModule.GetScreenShotManager(); + int32 New = 0; + int32 Fails = 0; + int32 Passes = 0; + for (FString ScreenshotName : FilesToCompare) + { + FPaths::MakePathRelativeTo(ScreenshotName, *IncomingPath); + FImageComparisonResult Result = ScreenshotManager->CompareScreenshotAsync(ScreenshotName).Get(); + if (Result.IsNew()) + { + UE_LOG(LogScreenshotComparison, Warning, TEXT("Incoming file %s is new"), *Result.IncomingFile); + New++; + } + else if (Result.AreSimilar()) + { + UE_LOG(LogScreenshotComparison, Log, TEXT("Incoming file %s is similar! (Global %f, Local %f)"), + *Result.IncomingFile, Result.GlobalDifference, Result.MaxLocalDifference); + Passes++; + } + else + { + UE_LOG(LogScreenshotComparison, Error, TEXT("Incoming file %s is different! (Global %f, Local %f) : %s"), + *Result.IncomingFile, Result.GlobalDifference, Result.MaxLocalDifference, *Result.ErrorMessage.ToString()); + Fails++; + } + } + + int32 Total = New + Passes + Fails; + if (Total) + { + UE_LOG(LogScreenshotComparison, Log, TEXT("Comparison Complete! (New: %d (%.2f%%), Fail: %d (%.2f%%), Pass: %d (%.2f%%)"), + New, (float)New / Total * 100, Fails, (float)Fails / Total * 100, Passes, (float)Passes / Total * 100); + } + else + { + UE_LOG(LogScreenshotComparison, Log, TEXT("Comparison Complete!")); + } + + + return 0; +} \ No newline at end of file diff --git a/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtilsEditor/Public/ScreenshotComparisonCommandlet.h b/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtilsEditor/Public/ScreenshotComparisonCommandlet.h new file mode 100644 index 000000000000..91844c34da2a --- /dev/null +++ b/Engine/Plugins/Experimental/AutomationUtils/Source/AutomationUtilsEditor/Public/ScreenshotComparisonCommandlet.h @@ -0,0 +1,21 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +#pragma once + +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" +#include "Commandlets/Commandlet.h" +#include "ScreenshotComparisonCommandlet.generated.h" + +DECLARE_LOG_CATEGORY_EXTERN(LogScreenshotComparison, Log, All); + +UCLASS(config = Editor) +class UScreenshotComparisonCommandlet : public UCommandlet +{ + GENERATED_UCLASS_BODY() + +public: + + //~ Begin UCommandlet Interface + virtual int32 Main(const FString& CmdLineParams) override; + //~ End UCommandlet Interface +}; \ No newline at end of file diff --git a/Engine/Plugins/Experimental/ControlRig/Source/ControlRig/Private/Units/Math/RigUnit_Float.h b/Engine/Plugins/Experimental/ControlRig/Source/ControlRig/Private/Units/Math/RigUnit_Float.h index 024e0db1721a..ed68b1604620 100644 --- a/Engine/Plugins/Experimental/ControlRig/Source/ControlRig/Private/Units/Math/RigUnit_Float.h +++ b/Engine/Plugins/Experimental/ControlRig/Source/ControlRig/Private/Units/Math/RigUnit_Float.h @@ -88,4 +88,34 @@ struct FRigUnit_Clamp_Float: public FRigUnit { Result = FMath::Clamp(Value, Min, Max); } +}; + +/** Two args and a result of float type */ +USTRUCT(meta = (DisplayName = "MapRange", Category = "Math|Float")) +struct FRigUnit_MapRange_Float: public FRigUnit +{ + GENERATED_BODY() + + UPROPERTY(meta = (Input)) + float Value; + + UPROPERTY(meta = (Input)) + float MinIn; + + UPROPERTY(meta = (Input)) + float MaxIn; + + UPROPERTY(meta = (Input)) + float MinOut; + + UPROPERTY(meta = (Input)) + float MaxOut; + + UPROPERTY(meta = (Output)) + float Result; + + virtual void Execute(const FRigUnitContext& InContext) override + { + Result = FMath::GetMappedRangeValueClamped(FVector2D(MinIn, MaxIn), FVector2D(MinOut, MaxOut), Value); + } }; \ No newline at end of file diff --git a/Engine/Plugins/Experimental/ControlRig/Source/ControlRigDeveloper/Public/Graph/ControlRigGraphSchema.h b/Engine/Plugins/Experimental/ControlRig/Source/ControlRigDeveloper/Public/Graph/ControlRigGraphSchema.h index c54ec528ab91..99332fabbb1a 100644 --- a/Engine/Plugins/Experimental/ControlRig/Source/ControlRigDeveloper/Public/Graph/ControlRigGraphSchema.h +++ b/Engine/Plugins/Experimental/ControlRig/Source/ControlRigDeveloper/Public/Graph/ControlRigGraphSchema.h @@ -66,6 +66,7 @@ public: virtual void TrySetDefaultText(UEdGraphPin& InPin, const FText& InNewDefaultText) const override; virtual bool ShouldAlwaysPurgeOnModification() const override { return false; } virtual bool ArePinsCompatible(const UEdGraphPin* PinA, const UEdGraphPin* PinB, const UClass* CallingContext, bool bIgnoreArray /*= false*/) const override; + virtual bool DoesSupportPinWatching() const override { return true; } /** Create a graph node for a rig */ UControlRigGraphNode* CreateGraphNode(UControlRigGraph* InGraph, const FName& InPropertyName) const; diff --git a/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/ControlRigEditor.Build.cs b/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/ControlRigEditor.Build.cs index c66d584b38c3..c444923698d3 100644 --- a/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/ControlRigEditor.Build.cs +++ b/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/ControlRigEditor.Build.cs @@ -47,6 +47,7 @@ namespace UnrealBuildTool.Rules "Persona", "UMG", "TimeManagement", + "PropertyPath", } ); } diff --git a/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/ControlRigEditorModule.cpp b/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/ControlRigEditorModule.cpp index 94aa990eaf02..53875a3538e1 100644 --- a/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/ControlRigEditorModule.cpp +++ b/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/ControlRigEditorModule.cpp @@ -54,6 +54,7 @@ #include "Graph/NodeSpawners/ControlRigUnitNodeSpawner.h" #include "Graph/NodeSpawners/ControlRigVariableNodeSpawner.h" #include "Kismet2/BlueprintEditorUtils.h" +#include "Kismet2/KismetDebugUtilities.h" #include "Graph/ControlRigGraphNode.h" #include "EdGraphUtilities.h" #include "ControlRigGraphPanelNodeFactory.h" @@ -889,6 +890,25 @@ void FControlRigEditorModule::GetContextMenuActions(const UControlRigGraphSchema } } MenuBuilder->EndSection(); + + // Add the watch pin / unwatch pin menu items + MenuBuilder->BeginSection("EdGraphSchemaWatches", LOCTEXT("WatchesHeader", "Watches")); + { + UBlueprint* OwnerBlueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(CurrentGraph); + { + const UEdGraphPin* WatchedPin = ((InGraphPin->Direction == EGPD_Input) && (InGraphPin->LinkedTo.Num() > 0)) ? InGraphPin->LinkedTo[0] : InGraphPin; + if (FKismetDebugUtilities::IsPinBeingWatched(OwnerBlueprint, WatchedPin)) + { + MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().StopWatchingPin); + } + else + { + MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().StartWatchingPin); + } + } + } + MenuBuilder->EndSection(); + } } } diff --git a/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/Editor/ControlRigEditor.cpp b/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/Editor/ControlRigEditor.cpp index fc280beef002..323a019f0018 100644 --- a/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/Editor/ControlRigEditor.cpp +++ b/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/Editor/ControlRigEditor.cpp @@ -277,6 +277,7 @@ void FControlRigEditor::OnCreateGraphEditorCommands(TSharedPtr G void FControlRigEditor::Compile() { + GetBlueprintObj()->SetObjectBeingDebugged(nullptr); ClearDetailObject(); FBlueprintEditor::Compile(); } @@ -729,6 +730,9 @@ void FControlRigEditor::UpdateControlRig() { ControlRig->SetObjectBinding(MakeShared()); } + + // Make sure the object being debugged is the preview instance + GetBlueprintObj()->SetObjectBeingDebugged(ControlRig); // initialize is moved post reinstance FInputBlendPose Filter; diff --git a/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/Graph/ControlRigConnectionDrawingPolicy.cpp b/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/Graph/ControlRigConnectionDrawingPolicy.cpp index 2936f50446d9..38ae6ea0cecf 100644 --- a/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/Graph/ControlRigConnectionDrawingPolicy.cpp +++ b/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/Graph/ControlRigConnectionDrawingPolicy.cpp @@ -11,24 +11,25 @@ void FControlRigConnectionDrawingPolicy::BuildPinToPinWidgetMap(TMap>& InPinToPinWidgetMap, TSharedRef& InGraphPinWidget) + static void AddSubPins_Recursive(UEdGraphPin* PinObj, TMap>& InPinToPinWidgetMap, TSharedPtr& InGraphPinWidget) { for(UEdGraphPin* SubPin : PinObj->SubPins) { // Only add to the pin-to-pin widget map if the sub-pin widget is not there already - TSharedRef* SubPinWidgetPtr = InPinToPinWidgetMap.Find(SubPin); + TSharedPtr* SubPinWidgetPtr = InPinToPinWidgetMap.Find(SubPin); if(SubPinWidgetPtr == nullptr) { SubPinWidgetPtr = &InGraphPinWidget; } - InPinToPinWidgetMap.Add(SubPin, *SubPinWidgetPtr); - AddSubPins_Recursive(SubPin, InPinToPinWidgetMap, *SubPinWidgetPtr); + TSharedPtr PinWidgetPtr = *SubPinWidgetPtr; + InPinToPinWidgetMap.Add(SubPin, PinWidgetPtr); + AddSubPins_Recursive(SubPin, InPinToPinWidgetMap, PinWidgetPtr); } } }; - TSharedRef GraphPinWidget = StaticCastSharedRef(ConnectorIt.Key()); + TSharedPtr GraphPinWidget = StaticCastSharedRef(ConnectorIt.Key()); Local::AddSubPins_Recursive(GraphPinWidget->GetPinObj(), PinToPinWidgetMap, GraphPinWidget); } } @@ -89,13 +90,13 @@ void FControlRigConnectionDrawingPolicy::DetermineLinkGeometry( /*out*/ FArrangedWidget*& EndWidgetGeometry ) { - if (TSharedRef* pOutputWidget = PinToPinWidgetMap.Find(OutputPin)) + if (TSharedPtr* pOutputWidget = PinToPinWidgetMap.Find(OutputPin)) { - StartWidgetGeometry = PinGeometries->Find(*pOutputWidget); + StartWidgetGeometry = PinGeometries->Find((*pOutputWidget).ToSharedRef()); } - if (TSharedRef* pInputWidget = PinToPinWidgetMap.Find(InputPin)) + if (TSharedPtr* pInputWidget = PinToPinWidgetMap.Find(InputPin)) { - EndWidgetGeometry = PinGeometries->Find(*pInputWidget); + EndWidgetGeometry = PinGeometries->Find((*pInputWidget).ToSharedRef()); } } \ No newline at end of file diff --git a/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/Graph/SControlRigGraphNode.cpp b/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/Graph/SControlRigGraphNode.cpp index a190352849b7..238575c364cd 100644 --- a/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/Graph/SControlRigGraphNode.cpp +++ b/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/Graph/SControlRigGraphNode.cpp @@ -13,6 +13,11 @@ #include "GraphEditorSettings.h" #include "ControlRigEditorStyle.h" #include "Widgets/Layout/SWrapBox.h" +#include "Engine/Engine.h" +#include "KismetNodes/KismetNodeInfoContext.h" +#include "Kismet2/KismetDebugUtilities.h" +#include "PropertyPathHelpers.h" +#include "UObject/PropertyPortFlags.h" #define LOCTEXT_NAMESPACE "SControlRigGraphNode" @@ -638,4 +643,58 @@ FReply SControlRigGraphNode::HandleAddArrayElement(TWeakPtr In return FReply::Handled(); } +void SControlRigGraphNode::GetNodeInfoPopups(FNodeInfoContext* Context, TArray& Popups) const +{ + FKismetNodeInfoContext* K2Context = (FKismetNodeInfoContext*)Context; + + const FLinearColor LatentBubbleColor(1.f, 0.5f, 0.25f); + const FLinearColor PinnedWatchColor(0.35f, 0.25f, 0.25f); + + // Display any pending latent actions + if (UObject* ActiveObject = K2Context->ActiveObjectBeingDebugged) + { + // Display pinned watches + if (K2Context->WatchedNodeSet.Contains(GraphNode)) + { + UBlueprint* Blueprint = K2Context->SourceBlueprint; + const UEdGraphSchema* Schema = GraphNode->GetSchema(); + + FString PinnedWatchText; + int32 ValidWatchCount = 0; + for (int32 PinIndex = 0; PinIndex < GraphNode->Pins.Num(); ++PinIndex) + { + UEdGraphPin* WatchPin = GraphNode->Pins[PinIndex]; + if (K2Context->WatchedPinSet.Contains(WatchPin)) + { + if (ValidWatchCount > 0) + { + PinnedWatchText += TEXT("\n"); + } + + FString PinName = UEdGraphSchema_K2::TypeToText(WatchPin->PinType).ToString(); + PinName += TEXT(" "); + PinName += Schema->GetPinDisplayName(WatchPin).ToString(); + + FString WatchText; + if (PropertyPathHelpers::GetPropertyValueAsString(ActiveObject, WatchPin->PinName.ToString(), WatchText)) + { + PinnedWatchText += FText::Format(LOCTEXT("WatchingAndValidFmt", "Watching {0}\n\t{1}"), FText::FromString(PinName), FText::FromString(WatchText)).ToString();//@TODO: Print out object being debugged name? + } + else + { + PinnedWatchText += FText::Format(LOCTEXT("WatchingAndValidFmt", "Invalid Property {0}"), FText::FromString(PinName)).ToString();//@TODO: Print out object being debugged name? + } + + ValidWatchCount++; + } + } + + if (ValidWatchCount) + { + new (Popups) FGraphInformationPopupInfo(NULL, PinnedWatchColor, PinnedWatchText); + } + } + } +} + #undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/Graph/SControlRigGraphNode.h b/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/Graph/SControlRigGraphNode.h index 132572862f49..5b053f51b06c 100644 --- a/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/Graph/SControlRigGraphNode.h +++ b/Engine/Plugins/Experimental/ControlRig/Source/ControlRigEditor/Private/Graph/SControlRigGraphNode.h @@ -39,7 +39,7 @@ public: virtual TSharedRef CreateNodeContentArea() override; virtual TSharedPtr GetHoveredPin( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) const override; - + virtual void GetNodeInfoPopups(FNodeInfoContext* Context, TArray& Popups) const override; private: bool ParentUseLowDetailNodeTitles() const { diff --git a/Engine/Plugins/Experimental/PythonScriptPlugin/Source/PythonScriptPlugin/Private/PyOnlineDocsWriter.cpp b/Engine/Plugins/Experimental/PythonScriptPlugin/Source/PythonScriptPlugin/Private/PyOnlineDocsWriter.cpp index 776e0a2b48f7..2ae52e1ba642 100644 --- a/Engine/Plugins/Experimental/PythonScriptPlugin/Source/PythonScriptPlugin/Private/PyOnlineDocsWriter.cpp +++ b/Engine/Plugins/Experimental/PythonScriptPlugin/Source/PythonScriptPlugin/Private/PyOnlineDocsWriter.cpp @@ -329,7 +329,7 @@ void FPyOnlineDocsWriter::GenerateFiles(const FString& InPythonStubPath) // https://pip.pypa.io/en/stable/user_guide/#using-pip-from-your-program FString PyCommandStr; - FString PythonPath = FPaths::ConvertRelativePathToFull(FPaths::EngineSourceDir()) / TEXT("ThirdParty/Python/Win64/python.exe"); + FString PythonPath = FPaths::ConvertRelativePathToFull(FPaths::EngineDir()) / TEXT("Binaries/ThirdParty/Python/Win64/python.exe"); PyCommandStr += TEXT( "import sys\n" diff --git a/Engine/Plugins/Experimental/PythonScriptPlugin/Source/PythonScriptPlugin/Private/PyWrapperTypeRegistry.cpp b/Engine/Plugins/Experimental/PythonScriptPlugin/Source/PythonScriptPlugin/Private/PyWrapperTypeRegistry.cpp index e9db42fe5d33..448f57e52c9d 100644 --- a/Engine/Plugins/Experimental/PythonScriptPlugin/Source/PythonScriptPlugin/Private/PyWrapperTypeRegistry.cpp +++ b/Engine/Plugins/Experimental/PythonScriptPlugin/Source/PythonScriptPlugin/Private/PyWrapperTypeRegistry.cpp @@ -370,7 +370,8 @@ void FPyWrapperTypeReinstancer::ProcessPending() { for (const auto& ClassToReinstancePair : ClassesToReinstance) { - FCoreUObjectDelegates::RegisterClassForHotReloadReinstancingDelegate.Broadcast(ClassToReinstancePair.Key, ClassToReinstancePair.Value); + // Assume the classes have changed + FCoreUObjectDelegates::RegisterClassForHotReloadReinstancingDelegate.Broadcast(ClassToReinstancePair.Key, ClassToReinstancePair.Value, EHotReloadedClassFlags::Changed); } FCoreUObjectDelegates::ReinstanceHotReloadedClassesDelegate.Broadcast(); diff --git a/Engine/Plugins/Experimental/RemoteSession/Source/RemoteSession/Private/Channels/RemoteSessionARCameraChannel.cpp b/Engine/Plugins/Experimental/RemoteSession/Source/RemoteSession/Private/Channels/RemoteSessionARCameraChannel.cpp index 7e242f878306..c53b07307af9 100644 --- a/Engine/Plugins/Experimental/RemoteSession/Source/RemoteSession/Private/Channels/RemoteSessionARCameraChannel.cpp +++ b/Engine/Plugins/Experimental/RemoteSession/Source/RemoteSession/Private/Channels/RemoteSessionARCameraChannel.cpp @@ -78,6 +78,7 @@ public: FMaterialShader::ModifyCompilationEnvironment(Platform, OutEnvironment); OutEnvironment.SetDefine(TEXT("POST_PROCESS_MATERIAL"), 1); OutEnvironment.SetDefine(TEXT("POST_PROCESS_MATERIAL_BEFORE_TONEMAP"), (Material->GetBlendableLocation() != BL_AfterTonemapping) ? 1 : 0); + OutEnvironment.SetDefine(TEXT("POST_PROCESS_AR_PASSTHROUGH"), 1); } FRemoteSessionARCameraVS() { } diff --git a/Engine/Plugins/Experimental/SkeletalReduction/SkeletalReduction.uplugin b/Engine/Plugins/Experimental/SkeletalReduction/SkeletalReduction.uplugin index 4595771a7953..9a4f43b32333 100644 --- a/Engine/Plugins/Experimental/SkeletalReduction/SkeletalReduction.uplugin +++ b/Engine/Plugins/Experimental/SkeletalReduction/SkeletalReduction.uplugin @@ -1,8 +1,8 @@ { "FileVersion" : 3, "Version" : 1, - "VersionName" : "0.1", - "FriendlyName" : "Skeletal Mesh Simplifier (Experimental)", + "VersionName" : "1.0", + "FriendlyName" : "Skeletal Mesh Simplifier (Early Access)", "Description" : "A plugin to generate LOD for deforming meshes.", "Category" : "Editor", "CreatedBy" : "Epic Games, Inc.", @@ -12,7 +12,7 @@ "SupportURL" : "", "EnabledByDefault" : true, "CanContainContent" : false, - "IsBetaVersion" : true, + "IsBetaVersion" : false, "Installed" : false, "Modules" : [ diff --git a/Engine/Plugins/Experimental/SkeletalReduction/Source/Private/SkeletalMeshReductionPlugin.cpp b/Engine/Plugins/Experimental/SkeletalReduction/Source/Private/SkeletalMeshReductionPlugin.cpp index 8ab01cf6fcc4..85f370e53434 100644 --- a/Engine/Plugins/Experimental/SkeletalReduction/Source/Private/SkeletalMeshReductionPlugin.cpp +++ b/Engine/Plugins/Experimental/SkeletalReduction/Source/Private/SkeletalMeshReductionPlugin.cpp @@ -22,7 +22,7 @@ #include "SkeletalSimplifier.h" #include "SkeletalMeshReductionSkinnedMesh.h" #include "Stats/StatsMisc.h" - +#include "Assets/ClothingAsset.h" #define LOCTEXT_NAMESPACE "SkeletalMeshReduction" @@ -80,6 +80,14 @@ public: return ReductionSettings.NumOfTrianglesPercentage < Threshold_One || ReductionSettings.NumOfVertPercentage < Threshold_One; } break; + //Absolute count is consider has being always reduced + case SkeletalMeshTerminationCriterion::SMTC_AbsNumOfVerts: + case SkeletalMeshTerminationCriterion::SMTC_AbsNumOfTriangles: + case SkeletalMeshTerminationCriterion::SMTC_AbsTriangleOrVert: + { + return true; + } + break; } return false; @@ -1381,11 +1389,15 @@ void FQuadricSkeletalMeshReduction::ReduceSkeletalMesh(USkeletalMesh& SkeletalMe //If the Current LOD is an import from file bool bOldLodWasFromFile = SkeletalMesh.IsValidLODIndex(LODIndex) && SkeletalMesh.GetLODInfo(LODIndex)->bHasBeenSimplified == false; + //True if the LOD is added by this reduction + bool bLODModelAdded = false; + // Insert a new LOD model entry if needed. if (LODIndex == SkeletalMeshResource.LODModels.Num()) { FSkeletalMeshLODModel* ModelPtr = NULL; SkeletalMeshResource.LODModels.Add(ModelPtr); + bLODModelAdded = true; } // Copy over LOD info from LOD0 if there is no previous info. @@ -1448,8 +1460,50 @@ void FQuadricSkeletalMeshReduction::ReduceSkeletalMesh(USkeletalMesh& SkeletalMe UE_LOG(LogSkeletalMeshReduction, Warning, TEXT("Building LOD %d - Invalid Base LOD entered. Using Base LOD 0 instead"), LODIndex); } } - //Reducing Base LOD, we need to use the temporary data so it can be iterative + + auto FillClothingData = [&SkeletalMeshResource, &LODIndex, bLODModelAdded](int32 &EnableSectionNumber, TArray &SectionStatus) + { + EnableSectionNumber = 0; + SectionStatus.Empty(); + if (!bLODModelAdded && SkeletalMeshResource.LODModels.IsValidIndex(LODIndex)) + { + int32 SectionNumber = SkeletalMeshResource.LODModels[LODIndex].Sections.Num(); + SectionStatus.Reserve(SectionNumber); + for (int32 SectionIndex = 0; SectionIndex < SectionNumber; ++SectionIndex) + { + SectionStatus.Add(!SkeletalMeshResource.LODModels[LODIndex].Sections[SectionIndex].bDisabled); + if (SectionStatus[SectionIndex]) + { + EnableSectionNumber++; + } + } + } + }; + + // Unbind any existing clothing assets before we reimport the geometry + TArray ClothingBindings; + //Get a map of enable/disable sections + int32 OriginalSectionNumberBeforeReduction = 0; + TArray OriginalSectionEnableBeforeReduction; + + //Do not play with cloth if the LOD is added + if (!bLODModelAdded) + { + //Store the clothBinding + ClothingAssetUtils::GetMeshClothingAssetBindings(&SkeletalMesh, ClothingBindings, LODIndex); + FillClothingData(OriginalSectionNumberBeforeReduction, OriginalSectionEnableBeforeReduction); + //Unbind the Cloth for this LOD before we reduce it, we will put back the cloth after the reduction, if it still match the sections + for (ClothingAssetUtils::FClothingAssetMeshBinding& Binding : ClothingBindings) + { + if (Binding.LODIndex == LODIndex) + { + Binding.Asset->UnbindFromSkeletalMesh(&SkeletalMesh, Binding.LODIndex); + } + } + } + bool bReducingSourceModel = false; + //Reducing Base LOD, we need to use the temporary data so it can be iterative if (BaseLOD == LODIndex && SkelResource->OriginalReductionSourceMeshData.IsValidIndex(BaseLOD) && !SkelResource->OriginalReductionSourceMeshData[BaseLOD]->IsEmpty()) { TMap> TempLODMorphTargetData; @@ -1544,6 +1598,13 @@ void FQuadricSkeletalMeshReduction::ReduceSkeletalMesh(USkeletalMesh& SkeletalMe { delete Old; } + else if(bReducingSourceModel) + { + //In case we reduce the source model we want to keep the original import data + FSkeletalMeshImportData RawMesh; + SrcModel->RawSkeletalMeshBulkData.LoadRawMesh(RawMesh); + LODModels[LODIndex]->RawSkeletalMeshBulkData.SaveRawMesh(RawMesh); + } } @@ -1633,6 +1694,46 @@ void FQuadricSkeletalMeshReduction::ReduceSkeletalMesh(USkeletalMesh& SkeletalMe SkeletalMesh.GetLODInfo(LODIndex)->bHasBeenSimplified = true; SkeletalMesh.bHasBeenSimplified = true; } + + if (!bLODModelAdded) + { + //Get the number of enabled section + int32 SectionNumberAfterReduction = 0; + TArray SectionEnableAfterReduction; + FillClothingData(SectionNumberAfterReduction, SectionEnableAfterReduction); + + //Put back the clothing for this newly reduce LOD only if the section count match. + if (ClothingBindings.Num() > 0 && OriginalSectionNumberBeforeReduction == SectionNumberAfterReduction) + { + TArray RemapSectionIndex; + int32 SectionIndexTest = 0; + for (int32 SectionIndexRef = 0; SectionIndexRef < OriginalSectionEnableBeforeReduction.Num(); SectionIndexRef++) + { + int32& RemapValue = RemapSectionIndex.Add_GetRef(INDEX_NONE); + if (!OriginalSectionEnableBeforeReduction[SectionIndexRef]) + { + continue; + } + for (; SectionIndexTest <= SectionIndexRef; SectionIndexTest++) + { + if (SectionEnableAfterReduction.IsValidIndex(SectionIndexTest) && SectionEnableAfterReduction[SectionIndexTest]) + { + RemapValue = SectionIndexTest++; + break; + } + } + } + + for (ClothingAssetUtils::FClothingAssetMeshBinding& Binding : ClothingBindings) + { + int32 RemapBindingSectionIndex = RemapSectionIndex[Binding.SectionIndex]; + if (RemapBindingSectionIndex != INDEX_NONE && Binding.LODIndex == LODIndex && NewModel->Sections.IsValidIndex(RemapBindingSectionIndex)) + { + Binding.Asset->BindToSkeletalMesh(&SkeletalMesh, Binding.LODIndex, RemapBindingSectionIndex, Binding.AssetInternalLodIndex, false); + } + } + } + } SkeletalMesh.CalculateRequiredBones(SkeletalMeshResource.LODModels[LODIndex], SkeletalMesh.RefSkeleton, &BonesToRemove); } diff --git a/Engine/Plugins/Experimental/SkeletalReduction/Source/SkeletalMeshReduction.Build.cs b/Engine/Plugins/Experimental/SkeletalReduction/Source/SkeletalMeshReduction.Build.cs index cf62c55c6d8d..c2ef8cf24774 100644 --- a/Engine/Plugins/Experimental/SkeletalReduction/Source/SkeletalMeshReduction.Build.cs +++ b/Engine/Plugins/Experimental/SkeletalReduction/Source/SkeletalMeshReduction.Build.cs @@ -55,7 +55,8 @@ namespace UnrealBuildTool.Rules "UnrealEd", "AnimationModifiers", "MeshBoneReduction", - "QuadricMeshReduction" + "QuadricMeshReduction", + "ClothingSystemRuntime" // ... add private dependencies that you statically link with here ... // QuadricMeshReduction is only for testing } diff --git a/Engine/Plugins/FX/Niagara/Source/Niagara/Classes/NiagaraDataInterfaceSpline.h b/Engine/Plugins/FX/Niagara/Source/Niagara/Classes/NiagaraDataInterfaceSpline.h index e0cad2c72561..0d568ec6d354 100644 --- a/Engine/Plugins/FX/Niagara/Source/Niagara/Classes/NiagaraDataInterfaceSpline.h +++ b/Engine/Plugins/FX/Niagara/Source/Niagara/Classes/NiagaraDataInterfaceSpline.h @@ -11,7 +11,7 @@ struct FNDISpline_InstanceData { //Cached ptr to component we sample from. - USplineComponent* Component; + TWeakObjectPtr Component; //Cached ComponentToWorld. FMatrix Transform; diff --git a/Engine/Plugins/FX/Niagara/Source/Niagara/Private/NiagaraComponent.cpp b/Engine/Plugins/FX/Niagara/Source/Niagara/Private/NiagaraComponent.cpp index 208ec4f5086c..883a9eaab7f4 100644 --- a/Engine/Plugins/FX/Niagara/Source/Niagara/Private/NiagaraComponent.cpp +++ b/Engine/Plugins/FX/Niagara/Source/Niagara/Private/NiagaraComponent.cpp @@ -296,37 +296,32 @@ void FNiagaraSceneProxy::GetDynamicRayTracingInstances(FRayTracingMaterialGather void FNiagaraSceneProxy::GatherSimpleLights(const FSceneViewFamily& ViewFamily, FSimpleLightArray& OutParticleLights) const { - NiagaraRendererLights *LightRenderer = nullptr; - FNiagaraDynamicDataLights *DynamicData = nullptr; for (int32 Idx = 0; Idx < EmitterRenderers.Num(); Idx++) { NiagaraRenderer *Renderer = EmitterRenderers[Idx]; if (Renderer && Renderer->GetPropertiesClass() == UNiagaraLightRendererProperties::StaticClass()) { - LightRenderer = static_cast(Renderer); - DynamicData = static_cast(Renderer->GetDynamicData()); - break; + NiagaraRendererLights* LightRenderer = static_cast(Renderer); + FNiagaraDynamicDataLights* DynamicData = static_cast(Renderer->GetDynamicData()); + + if (DynamicData) + { + int32 LightCount = DynamicData->LightArray.Num(); + + OutParticleLights.InstanceData.Reserve(LightCount + OutParticleLights.InstanceData.Num()); + OutParticleLights.PerViewData.Reserve(LightCount + OutParticleLights.PerViewData.Num()); + + for (NiagaraRendererLights::SimpleLightData &LightData : DynamicData->LightArray) + { + // When not using camera-offset, output one position for all views to share. + OutParticleLights.PerViewData.Add(LightData.PerViewEntry); + + // Add an entry for the light instance. + OutParticleLights.InstanceData.Add(LightData.LightEntry); + } + } } } - - - if (DynamicData) - { - int32 LightCount = DynamicData->LightArray.Num(); - - OutParticleLights.InstanceData.Reserve(LightCount); - OutParticleLights.PerViewData.Reserve(LightCount); - - for (NiagaraRendererLights::SimpleLightData &LightData : DynamicData->LightArray) - { - // When not using camera-offset, output one position for all views to share. - OutParticleLights.PerViewData.Add(LightData.PerViewEntry); - - // Add an entry for the light instance. - OutParticleLights.InstanceData.Add(LightData.LightEntry); - } - } - } diff --git a/Engine/Plugins/FX/Niagara/Source/Niagara/Private/NiagaraDataInterfaceSpline.cpp b/Engine/Plugins/FX/Niagara/Source/Niagara/Private/NiagaraDataInterfaceSpline.cpp index 4188e30dae82..9ce88ebf6719 100644 --- a/Engine/Plugins/FX/Niagara/Source/Niagara/Private/NiagaraDataInterfaceSpline.cpp +++ b/Engine/Plugins/FX/Niagara/Source/Niagara/Private/NiagaraDataInterfaceSpline.cpp @@ -329,40 +329,9 @@ bool UNiagaraDataInterfaceSpline::InitPerInstanceData(void* PerInstanceData, FNi { FNDISpline_InstanceData* InstData = new (PerInstanceData) FNDISpline_InstanceData(); - InstData->Component = nullptr; + InstData->Component.Reset(); InstData->Transform = FMatrix::Identity; InstData->TransformInverseTransposed = FMatrix::Identity; - if (Source) - { - USplineComponent* SourceComp = Source->FindComponentByClass(); - - if (SourceComp) - { - InstData->Component = SourceComp; - } - } - else - { - if (UNiagaraComponent* SimComp = SystemInstance->GetComponent()) - { - if (AActor* Owner = SimComp->GetAttachmentRootActor()) - { - USplineComponent* SourceComp = Owner->FindComponentByClass(); - - if (SourceComp) - { - InstData->Component = SourceComp; - } - } - } - } - - //Re-evaluate source in case it's changed? - if (InstData->Component) - { - InstData->Transform = InstData->Component->GetComponentToWorld().ToMatrixWithScale(); - InstData->TransformInverseTransposed = InstData->Transform.InverseFast().GetTransposed(); - } return true; } @@ -378,9 +347,30 @@ bool UNiagaraDataInterfaceSpline::PerInstanceTick(void* PerInstanceData, FNiagar check(SystemInstance); FNDISpline_InstanceData* InstData = (FNDISpline_InstanceData*)PerInstanceData; - if (InstData->Component) + USplineComponent* SplineComponent = InstData->Component.Get(); + if (SplineComponent == nullptr) { - InstData->Transform = InstData->Component->GetComponentToWorld().ToMatrixWithScale(); + if (Source != nullptr) + { + SplineComponent = Source->FindComponentByClass(); + } + else + { + if (UNiagaraComponent* SimComp = SystemInstance->GetComponent()) + { + if (AActor* Owner = SimComp->GetAttachmentRootActor()) + { + SplineComponent = Owner->FindComponentByClass(); + } + } + } + InstData->Component = SplineComponent; + } + + //Re-evaluate source in case it's changed? + if (SplineComponent != nullptr) + { + InstData->Transform = SplineComponent->GetComponentToWorld().ToMatrixWithScale(); InstData->TransformInverseTransposed = InstData->Transform.InverseFast().GetTransposed(); } @@ -398,14 +388,14 @@ void UNiagaraDataInterfaceSpline::SampleSplinePositionByUnitDistance(FVectorVMCo VectorVM::FExternalFuncRegisterHandler OutPosY(Context); VectorVM::FExternalFuncRegisterHandler OutPosZ(Context); - if (InstData->Component) + if (USplineComponent* SplineComponent = InstData->Component.Get()) { for (int32 i = 0; i < Context.NumInstances; ++i) { float DistanceUnitDistance = SplineSampleParam.Get(); - FVector Pos = InstData->Component->GetLocationAtDistanceAlongSpline(DistanceUnitDistance * InstData->Component->GetSplineLength(), ESplineCoordinateSpace::Local); + FVector Pos = SplineComponent->GetLocationAtDistanceAlongSpline(DistanceUnitDistance * SplineComponent->GetSplineLength(), ESplineCoordinateSpace::Local); TransformHandler.TransformPosition(Pos, InstData->Transform); *OutPosX.GetDest() = Pos.X; @@ -447,14 +437,14 @@ void UNiagaraDataInterfaceSpline::SampleSplineUpVectorByUnitDistance(FVectorVMCo VectorVM::FExternalFuncRegisterHandler OutPosY(Context); VectorVM::FExternalFuncRegisterHandler OutPosZ(Context); - if (InstData->Component) + if (USplineComponent* SplineComponent = InstData->Component.Get()) { for (int32 i = 0; i < Context.NumInstances; ++i) { float DistanceUnitDistance = SplineSampleParam.Get(); - FVector Pos = InstData->Component->GetUpVectorAtDistanceAlongSpline(DistanceUnitDistance * InstData->Component->GetSplineLength(), ESplineCoordinateSpace::Local); + FVector Pos = SplineComponent->GetUpVectorAtDistanceAlongSpline(DistanceUnitDistance * SplineComponent->GetSplineLength(), ESplineCoordinateSpace::Local); TransformHandler.TransformVector(Pos, InstData->Transform); *OutPosX.GetDest() = Pos.X; @@ -497,13 +487,13 @@ void UNiagaraDataInterfaceSpline::SampleSplineRightVectorByUnitDistance(FVectorV VectorVM::FExternalFuncRegisterHandler OutPosZ(Context); - if (InstData->Component) + if (USplineComponent* SplineComponent = InstData->Component.Get()) { for (int32 i = 0; i < Context.NumInstances; ++i) { float DistanceUnitDistance = SplineSampleParam.Get(); - FVector Pos = InstData->Component->GetRightVectorAtDistanceAlongSpline(DistanceUnitDistance * InstData->Component->GetSplineLength(), ESplineCoordinateSpace::Local); + FVector Pos = SplineComponent->GetRightVectorAtDistanceAlongSpline(DistanceUnitDistance * SplineComponent->GetSplineLength(), ESplineCoordinateSpace::Local); TransformHandler.TransformVector(Pos, InstData->Transform); *OutPosX.GetDest() = Pos.X; @@ -543,13 +533,13 @@ void UNiagaraDataInterfaceSpline::SampleSplineTangentByUnitDistance(FVectorVMCon VectorVM::FExternalFuncRegisterHandler OutPosY(Context); VectorVM::FExternalFuncRegisterHandler OutPosZ(Context); - if (InstData->Component) + if (USplineComponent* SplineComponent = InstData->Component.Get()) { for (int32 i = 0; i < Context.NumInstances; ++i) { float DistanceUnitDistance = SplineSampleParam.Get(); - FVector Pos = InstData->Component->GetTangentAtDistanceAlongSpline(DistanceUnitDistance * InstData->Component->GetSplineLength(), ESplineCoordinateSpace::Local); + FVector Pos = SplineComponent->GetTangentAtDistanceAlongSpline(DistanceUnitDistance * SplineComponent->GetSplineLength(), ESplineCoordinateSpace::Local); TransformHandler.TransformVector(Pos, InstData->Transform); *OutPosX.GetDest() = Pos.X; @@ -591,13 +581,13 @@ void UNiagaraDataInterfaceSpline::SampleSplineDirectionByUnitDistance(FVectorVMC VectorVM::FExternalFuncRegisterHandler OutPosY(Context); VectorVM::FExternalFuncRegisterHandler OutPosZ(Context); - if (InstData->Component) + if (USplineComponent* SplineComponent = InstData->Component.Get()) { for (int32 i = 0; i < Context.NumInstances; ++i) { float DistanceUnitDistance = SplineSampleParam.Get(); - FVector Pos = InstData->Component->GetDirectionAtDistanceAlongSpline(DistanceUnitDistance * InstData->Component->GetSplineLength(), ESplineCoordinateSpace::Local); + FVector Pos = SplineComponent->GetDirectionAtDistanceAlongSpline(DistanceUnitDistance * SplineComponent->GetSplineLength(), ESplineCoordinateSpace::Local); TransformHandler.TransformVector(Pos, InstData->Transform); *OutPosX.GetDest() = Pos.X; @@ -678,11 +668,11 @@ void UNiagaraDataInterfaceSpline::FindClosestUnitDistanceFromPositionWS(FVectorV VectorVM::FUserPtrHandler InstData(Context); VectorVM::FExternalFuncRegisterHandler OutUnitDistance(Context); - if (InstData->Component) + if (USplineComponent* SplineComponent = InstData->Component.Get()) { - const int32 NumPoints = InstData->Component->GetSplinePointsPosition().Points.Num(); - const float FinalKeyTime = InstData->Component->GetSplinePointsPosition().Points[NumPoints - 1].InVal; + const int32 NumPoints = SplineComponent->GetSplinePointsPosition().Points.Num(); + const float FinalKeyTime = SplineComponent->GetSplinePointsPosition().Points[NumPoints - 1].InVal; for (int32 i = 0; i < Context.NumInstances; ++i) { @@ -693,7 +683,7 @@ void UNiagaraDataInterfaceSpline::FindClosestUnitDistanceFromPositionWS(FVectorV FVector Pos(PosX, PosY, PosZ); // This first call finds the key time, but this is not in 0..1 range for the spline. - float KeyTime = InstData->Component->FindInputKeyClosestToWorldLocation(Pos); + float KeyTime = SplineComponent->FindInputKeyClosestToWorldLocation(Pos); // We need to convert into the range by dividing through by the overall duration of the spline according to the keys. float UnitDistance = KeyTime / FinalKeyTime; diff --git a/Engine/Plugins/FX/Niagara/Source/NiagaraEditor/Private/NiagaraNodeOp.cpp b/Engine/Plugins/FX/Niagara/Source/NiagaraEditor/Private/NiagaraNodeOp.cpp index 3630548c8d6b..c946b12a89e5 100644 --- a/Engine/Plugins/FX/Niagara/Source/NiagaraEditor/Private/NiagaraNodeOp.cpp +++ b/Engine/Plugins/FX/Niagara/Source/NiagaraEditor/Private/NiagaraNodeOp.cpp @@ -24,7 +24,7 @@ void UNiagaraNodeOp::AllocateDefaultPins() for (int32 SrcIndex = 0; SrcIndex < OpInfo->Inputs.Num(); ++SrcIndex) { const FNiagaraOpInOutInfo& InOutInfo = OpInfo->Inputs[SrcIndex]; - UEdGraphPin* Pin = CreatePin(EGPD_Input, Schema->TypeDefinitionToPinType(InOutInfo.DataType), *InOutInfo.FriendlyName.ToString()); + UEdGraphPin* Pin = CreatePin(EGPD_Input, Schema->TypeDefinitionToPinType(InOutInfo.DataType), *InOutInfo.Name.ToString()); check(Pin); Pin->bDefaultValueIsIgnored = false; Pin->bDefaultValueIsReadOnly = false; @@ -49,7 +49,7 @@ void UNiagaraNodeOp::AllocateDefaultPins() for (int32 OutIdx = 0; OutIdx < OpInfo->Outputs.Num(); ++OutIdx) { const FNiagaraOpInOutInfo& InOutInfo = OpInfo->Outputs[OutIdx]; - UEdGraphPin* Pin = CreatePin(EGPD_Output, Schema->TypeDefinitionToPinType(InOutInfo.DataType), *InOutInfo.FriendlyName.ToString()); + UEdGraphPin* Pin = CreatePin(EGPD_Output, Schema->TypeDefinitionToPinType(InOutInfo.DataType), *InOutInfo.Name.ToString()); check(Pin); Pin->PinToolTip = InOutInfo.Description.ToString(); } diff --git a/Engine/Plugins/Media/MediaFrameworkUtilities/Source/MediaFrameworkUtilitiesEditor/Private/CaptureTab/SMediaFrameworkCaptureOutputWidget.cpp b/Engine/Plugins/Media/MediaFrameworkUtilities/Source/MediaFrameworkUtilitiesEditor/Private/CaptureTab/SMediaFrameworkCaptureOutputWidget.cpp index 27c3a750dd19..d82e75fc38e5 100644 --- a/Engine/Plugins/Media/MediaFrameworkUtilities/Source/MediaFrameworkUtilitiesEditor/Private/CaptureTab/SMediaFrameworkCaptureOutputWidget.cpp +++ b/Engine/Plugins/Media/MediaFrameworkUtilities/Source/MediaFrameworkUtilitiesEditor/Private/CaptureTab/SMediaFrameworkCaptureOutputWidget.cpp @@ -562,11 +562,6 @@ void SMediaFrameworkCaptureCurrentViewportWidget::Construct(const FArguments& In } END_SLATE_FUNCTION_BUILD_OPTIMIZATION -SMediaFrameworkCaptureCurrentViewportWidget::~SMediaFrameworkCaptureCurrentViewportWidget() -{ - ShutdownViewport(); -} - void SMediaFrameworkCaptureCurrentViewportWidget::StartOutput() { ShutdownViewport(); @@ -627,8 +622,6 @@ void SMediaFrameworkCaptureCurrentViewportWidget::StartOutput() { GEditor->OnLevelViewportClientListChanged().AddSP(this, &SMediaFrameworkCaptureCurrentViewportWidget::OnLevelViewportClientListChanged); EditorSceneViewport = SceneViewport; - FIntPoint TargetSize = MediaOutputPtr->GetRequestedSize(); - SceneViewport->SetFixedViewportSize(TargetSize.X, TargetSize.Y); MediaCapture->OnStateChangedNative.AddSP(this, &SMediaFrameworkCaptureCurrentViewportWidget::OnMediaCaptureStateChanged); if (!MediaCapture->CaptureSceneViewport(SceneViewport, CaptureOptions)) @@ -640,6 +633,15 @@ void SMediaFrameworkCaptureCurrentViewportWidget::StartOutput() } } +void SMediaFrameworkCaptureCurrentViewportWidget::StopOutput() +{ + if (MediaCapture.IsValid()) + { + MediaCapture->StopCapture(false); + } + ShutdownViewport(); +} + void SMediaFrameworkCaptureCurrentViewportWidget::OnLevelViewportClientListChanged() { bool bFound = false; @@ -676,32 +678,19 @@ void SMediaFrameworkCaptureCurrentViewportWidget::OnMediaCaptureStateChanged() void SMediaFrameworkCaptureCurrentViewportWidget::OnPrePIE() { - if (MediaCapture.IsValid()) - { - MediaCapture->StopCapture(false); - } - ShutdownViewport(); + StopOutput(); } void SMediaFrameworkCaptureCurrentViewportWidget::OnPrePIEEnded() { - if (MediaCapture.IsValid()) - { - MediaCapture->StopCapture(false); - } - ShutdownViewport(); + StopOutput(); } void SMediaFrameworkCaptureCurrentViewportWidget::ShutdownViewport() { - TSharedPtr EditorSceneViewportPin = EditorSceneViewport.Pin(); - if (EditorSceneViewportPin.IsValid() && EditorSceneViewportPin->GetViewportWidget().IsValid()) - { - EditorSceneViewportPin->SetFixedViewportSize(0, 0); - } - GEditor->OnLevelViewportClientListChanged().RemoveAll(this); + TSharedPtr EditorSceneViewportPin = EditorSceneViewport.Pin(); TSharedPtr LevelViewportPin = LevelViewport.Pin(); if (LevelViewportPin.IsValid() && LevelViewportPin->GetSharedActiveViewport() == EditorSceneViewportPin) { diff --git a/Engine/Plugins/Media/MediaFrameworkUtilities/Source/MediaFrameworkUtilitiesEditor/Private/CaptureTab/SMediaFrameworkCaptureOutputWidget.h b/Engine/Plugins/Media/MediaFrameworkUtilities/Source/MediaFrameworkUtilitiesEditor/Private/CaptureTab/SMediaFrameworkCaptureOutputWidget.h index f8093df1a590..2c8e04cb6b89 100644 --- a/Engine/Plugins/Media/MediaFrameworkUtilities/Source/MediaFrameworkUtilitiesEditor/Private/CaptureTab/SMediaFrameworkCaptureOutputWidget.h +++ b/Engine/Plugins/Media/MediaFrameworkUtilities/Source/MediaFrameworkUtilitiesEditor/Private/CaptureTab/SMediaFrameworkCaptureOutputWidget.h @@ -153,7 +153,7 @@ public: SLATE_END_ARGS() void Construct(const FArguments& InArgs); - virtual ~SMediaFrameworkCaptureCurrentViewportWidget(); + virtual void StopOutput() override; void StartOutput(); diff --git a/Engine/Plugins/Media/MediaFrameworkUtilities/Source/MediaFrameworkUtilitiesEditor/Private/MediaFrameworkWorldSettingsAssetUserData.cpp b/Engine/Plugins/Media/MediaFrameworkUtilities/Source/MediaFrameworkUtilitiesEditor/Private/MediaFrameworkWorldSettingsAssetUserData.cpp index 5692843fa1b1..6c9610f10cd1 100644 --- a/Engine/Plugins/Media/MediaFrameworkUtilities/Source/MediaFrameworkUtilitiesEditor/Private/MediaFrameworkWorldSettingsAssetUserData.cpp +++ b/Engine/Plugins/Media/MediaFrameworkUtilities/Source/MediaFrameworkUtilitiesEditor/Private/MediaFrameworkWorldSettingsAssetUserData.cpp @@ -25,6 +25,12 @@ FMediaFrameworkCaptureRenderTargetCameraOutputInfo::FMediaFrameworkCaptureRender } +UMediaFrameworkWorldSettingsAssetUserData::UMediaFrameworkWorldSettingsAssetUserData() +{ + CurrentViewportMediaOutput.CaptureOptions.bResizeSourceBuffer = true; +} + + PRAGMA_DISABLE_DEPRECATION_WARNINGS void UMediaFrameworkWorldSettingsAssetUserData::Serialize(FArchive& Ar) { diff --git a/Engine/Plugins/Media/MediaFrameworkUtilities/Source/MediaFrameworkUtilitiesEditor/Private/MediaFrameworkWorldSettingsAssetUserData.h b/Engine/Plugins/Media/MediaFrameworkUtilities/Source/MediaFrameworkUtilitiesEditor/Private/MediaFrameworkWorldSettingsAssetUserData.h index 8d2d79fe9614..8325c3fbcb1f 100644 --- a/Engine/Plugins/Media/MediaFrameworkUtilities/Source/MediaFrameworkUtilitiesEditor/Private/MediaFrameworkWorldSettingsAssetUserData.h +++ b/Engine/Plugins/Media/MediaFrameworkUtilities/Source/MediaFrameworkUtilitiesEditor/Private/MediaFrameworkWorldSettingsAssetUserData.h @@ -95,6 +95,8 @@ class UMediaFrameworkWorldSettingsAssetUserData : public UAssetUserData GENERATED_BODY() public: + UMediaFrameworkWorldSettingsAssetUserData(); + UPROPERTY(EditAnywhere, Category="Media Render Target Capture", meta=(ShowOnlyInnerProperties)) TArray RenderTargetCaptures; @@ -104,7 +106,7 @@ public: /** * Capture the current viewport. It may be the level editor active viewport or a PIE instance launch with "New Editor Window PIE". * @note The behavior is different from MediaCapture.CaptureActiveSceneViewport. Here we can capture the editor viewport (since we are in the editor). - * @note If the viewport is the level editor active viewport, then 1-all inputs will be disabled 2-the viewport size will be fixed 3-the viewport will always rendered. + * @note If the viewport is the level editor active viewport, then all inputs will be disabled and the viewport will always rendered. */ UPROPERTY(EditAnywhere, Category="Media Current Viewport Capture", meta=(DisplayName="Current Viewport")) FMediaFrameworkCaptureCurrentViewportOutputInfo CurrentViewportMediaOutput; diff --git a/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Private/FileMediaOutput.cpp b/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Private/FileMediaOutput.cpp index cd59ade2b476..daed58db8f6b 100644 --- a/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Private/FileMediaOutput.cpp +++ b/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Private/FileMediaOutput.cpp @@ -51,7 +51,7 @@ FIntPoint UFileMediaOutput::GetRequestedSize() const return DesiredSize; } - return FIntPoint(GSystemResolution.ResX, GSystemResolution.ResY); + return UMediaOutput::RequestCaptureSourceSize; } diff --git a/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Private/MediaCapture.cpp b/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Private/MediaCapture.cpp index 5bc52c045006..f775dc971636 100644 --- a/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Private/MediaCapture.cpp +++ b/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Private/MediaCapture.cpp @@ -76,6 +76,7 @@ UMediaCapture::FCaptureFrame::FCaptureFrame() FMediaCaptureOptions::FMediaCaptureOptions() : Crop(EMediaCaptureCroppingType::None) , CustomCapturePoint(FIntPoint::ZeroValue) + , bResizeSourceBuffer(false) { } @@ -95,7 +96,10 @@ UMediaCapture::UMediaCapture(const FObjectInitializer& ObjectInitializer) , DesiredOutputPixelFormat(EPixelFormat::PF_A2B10G10R10) , ConversionOperation(EMediaCaptureConversionOperation::NONE) , MediaOutputName(TEXT("[undefined]")) + , bUseRequestedTargetSize(false) , bResolvedTargetInitialized(false) + , bShouldCaptureRHITexture(false) + , bViewportHasFixedViewportSize(false) , WaitingForResolveCommandExecutionCounter(0) { } @@ -151,9 +155,21 @@ bool UMediaCapture::CaptureSceneViewport(TSharedPtr& InSceneView DesiredCaptureOptions = InCaptureOptions; CacheMediaOutput(EMediaCaptureSourceType::SCENE_VIEWPORT); + if (bUseRequestedTargetSize) + { + DesiredSize = InSceneViewport->GetSize(); + } + else if (DesiredCaptureOptions.bResizeSourceBuffer) + { + SetFixedViewportSize(InSceneViewport); + } + + CacheOutputOptions(); + const bool bCurrentlyCapturing = false; if (!MediaCaptureDetails::ValidateSceneViewport(InSceneViewport, DesiredCaptureOptions, DesiredSize, DesiredPixelFormat, bCurrentlyCapturing)) { + ResetFixedViewportSize(InSceneViewport, false); MediaCaptureDetails::ShowSlateNotification(); return false; } @@ -161,12 +177,13 @@ bool UMediaCapture::CaptureSceneViewport(TSharedPtr& InSceneView SetState(EMediaCaptureState::Preparing); if (!CaptureSceneViewportImpl(InSceneViewport)) { + ResetFixedViewportSize(InSceneViewport, false); SetState(EMediaCaptureState::Stopped); MediaCaptureDetails::ShowSlateNotification(); return false; } - //no lock required the command on the render thread is not active + //no lock required, the command on the render thread is not active CapturingSceneViewport = InSceneViewport; InitializeResolveTarget(MediaOutput->NumberOfTextureBuffers); @@ -191,6 +208,17 @@ bool UMediaCapture::CaptureTextureRenderTarget2D(UTextureRenderTarget2D* InRende DesiredCaptureOptions = CaptureOptions; CacheMediaOutput(EMediaCaptureSourceType::RENDER_TARGET); + if (bUseRequestedTargetSize) + { + DesiredSize = FIntPoint(InRenderTarget2D->SizeX, InRenderTarget2D->SizeY); + } + else if (DesiredCaptureOptions.bResizeSourceBuffer) + { + InRenderTarget2D->ResizeTarget(DesiredSize.X, DesiredSize.Y); + } + + CacheOutputOptions(); + const bool bCurrentlyCapturing = false; if (!MediaCaptureDetails::ValidateTextureRenderTarget2D(InRenderTarget2D, DesiredCaptureOptions, DesiredSize, DesiredPixelFormat, bCurrentlyCapturing)) { @@ -222,9 +250,14 @@ void UMediaCapture::CacheMediaOutput(EMediaCaptureSourceType InSourceType) DesiredSize = MediaOutput->GetRequestedSize(); DesiredPixelFormat = MediaOutput->GetRequestedPixelFormat(); ConversionOperation = MediaOutput->GetConversionOperation(InSourceType); +} + +void UMediaCapture::CacheOutputOptions() +{ DesiredOutputSize = GetOutputSize(DesiredSize, ConversionOperation); DesiredOutputPixelFormat = GetOutputPixelFormat(DesiredPixelFormat, ConversionOperation); MediaOutputName = *MediaOutput->GetName(); + bShouldCaptureRHITexture = ShouldCaptureRHITexture(); } FIntPoint UMediaCapture::GetOutputSize(const FIntPoint & InSize, const EMediaCaptureConversionOperation & InConversionOperation) const @@ -266,10 +299,15 @@ bool UMediaCapture::UpdateSceneViewport(TSharedPtr& InSceneViewp check(IsInGameThread()); - const bool bCurrentlyCapturing = true; + if (!bUseRequestedTargetSize && DesiredCaptureOptions.bResizeSourceBuffer) + { + SetFixedViewportSize(InSceneViewport); + } + const bool bCurrentlyCapturing = true; if (!MediaCaptureDetails::ValidateSceneViewport(InSceneViewport, DesiredCaptureOptions, DesiredSize, DesiredPixelFormat, bCurrentlyCapturing)) { + ResetFixedViewportSize(InSceneViewport, false); StopCapture(false); MediaCaptureDetails::ShowSlateNotification(); return false; @@ -277,6 +315,7 @@ bool UMediaCapture::UpdateSceneViewport(TSharedPtr& InSceneViewp if (!UpdateSceneViewportImpl(InSceneViewport)) { + ResetFixedViewportSize(InSceneViewport, false); StopCapture(false); MediaCaptureDetails::ShowSlateNotification(); return false; @@ -284,6 +323,7 @@ bool UMediaCapture::UpdateSceneViewport(TSharedPtr& InSceneViewp { FScopeLock Lock(&AccessingCapturingSource); + ResetFixedViewportSize(CapturingSceneViewport.Pin(), true); CapturingSceneViewport = InSceneViewport; CapturingRenderTarget = nullptr; } @@ -302,6 +342,11 @@ bool UMediaCapture::UpdateTextureRenderTarget2D(UTextureRenderTarget2D * InRende check(IsInGameThread()); + if (!bUseRequestedTargetSize && DesiredCaptureOptions.bResizeSourceBuffer) + { + InRenderTarget2D->ResizeTarget(DesiredSize.X, DesiredSize.Y); + } + const bool bCurrentlyCapturing = true; if (!MediaCaptureDetails::ValidateTextureRenderTarget2D(InRenderTarget2D, DesiredCaptureOptions, DesiredSize, DesiredPixelFormat, bCurrentlyCapturing)) { @@ -319,6 +364,7 @@ bool UMediaCapture::UpdateTextureRenderTarget2D(UTextureRenderTarget2D * InRende { FScopeLock Lock(&AccessingCapturingSource); + ResetFixedViewportSize(CapturingSceneViewport.Pin(), true); CapturingRenderTarget = InRenderTarget2D; CapturingSceneViewport.Reset(); } @@ -355,6 +401,7 @@ void UMediaCapture::StopCapture(bool bAllowPendingFrameToBeProcess) FlushRenderingCommands(); } StopCaptureImpl(bAllowPendingFrameToBeProcess); + ResetFixedViewportSize(CapturingSceneViewport.Pin(), false); CapturingRenderTarget = nullptr; CapturingSceneViewport.Reset(); @@ -408,6 +455,25 @@ void UMediaCapture::BroadcastStateChanged() OnStateChangedNative.Broadcast(); } +void UMediaCapture::SetFixedViewportSize(TSharedPtr InSceneViewport) +{ + InSceneViewport->SetFixedViewportSize(DesiredSize.X, DesiredSize.Y); + bViewportHasFixedViewportSize = true; +} + +void UMediaCapture::ResetFixedViewportSize(TSharedPtr InViewport, bool bInFlushRenderingCommands) +{ + if (bViewportHasFixedViewportSize && InViewport.IsValid()) + { + if (bInFlushRenderingCommands && WaitingForResolveCommandExecutionCounter > 0) + { + FlushRenderingCommands(); + } + InViewport->SetFixedViewportSize(0, 0); + bViewportHasFixedViewportSize = false; + } +} + bool UMediaCapture::HasFinishedProcessing() const { return WaitingForResolveCommandExecutionCounter == 0 @@ -417,29 +483,39 @@ bool UMediaCapture::HasFinishedProcessing() const void UMediaCapture::InitializeResolveTarget(int32 InNumberOfBuffers) { + if (bShouldCaptureRHITexture) + { + // No buffer is needed if the callback is with the RHI Texture + InNumberOfBuffers = 1; + } + NumberOfCaptureFrame = InNumberOfBuffers; check(CaptureFrames.Num() == 0); CaptureFrames.AddDefaulted(InNumberOfBuffers); - UMediaCapture* This = this; - ENQUEUE_RENDER_COMMAND(MediaOutputCaptureFrameCreateTexture)( - [This](FRHICommandListImmediate& RHICmdList) - { - FRHIResourceCreateInfo CreateInfo; - for (int32 Index = 0; Index < This->NumberOfCaptureFrame; ++Index) + // Only create CPU readback texture when we are using the CPU callback + if (!bShouldCaptureRHITexture) + { + UMediaCapture* This = this; + ENQUEUE_RENDER_COMMAND(MediaOutputCaptureFrameCreateTexture)( + [This](FRHICommandListImmediate& RHICmdList) { - This->CaptureFrames[Index].ReadbackTexture = RHICreateTexture2D( - This->DesiredOutputSize.X, - This->DesiredOutputSize.Y, - This->DesiredOutputPixelFormat, - 1, - 1, - TexCreate_CPUReadback, - CreateInfo - ); - } - This->bResolvedTargetInitialized = true; - }); + FRHIResourceCreateInfo CreateInfo; + for (int32 Index = 0; Index < This->NumberOfCaptureFrame; ++Index) + { + This->CaptureFrames[Index].ReadbackTexture = RHICreateTexture2D( + This->DesiredOutputSize.X, + This->DesiredOutputSize.Y, + This->DesiredOutputPixelFormat, + 1, + 1, + TexCreate_CPUReadback, + CreateInfo + ); + } + This->bResolvedTargetInitialized = true; + }); + } } bool UMediaCapture::ValidateMediaOutput() const @@ -735,13 +811,21 @@ void UMediaCapture::OnEndFrame_GameThread() } } - // Asynchronously copy duplicate target from GPU to System Memory - RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, CapturingFrame->ReadbackTexture, FResolveParams()); - - CapturingFrame->bResolvedTargetRequested = true; + if (InMediaCapture->bShouldCaptureRHITexture) + { + SCOPE_CYCLE_COUNTER(STAT_MediaCapture_RenderThread_Callback); + InMediaCapture->OnRHITextureCaptured_RenderingThread(CapturingFrame->CaptureBaseData, CapturingFrame->UserData, DestRenderTarget.TargetableTexture); + CapturingFrame->bResolvedTargetRequested = false; + } + else + { + // Asynchronously copy duplicate target from GPU to System Memory + RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, CapturingFrame->ReadbackTexture, FResolveParams()); + CapturingFrame->bResolvedTargetRequested = true; + } } - if (ReadyFrame && InMediaCapture->GetState() != EMediaCaptureState::Error) + if (!InMediaCapture->bShouldCaptureRHITexture && ReadyFrame && InMediaCapture->GetState() != EMediaCaptureState::Error) { check(ReadyFrame->ReadbackTexture.IsValid()); diff --git a/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Private/MediaOutput.cpp b/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Private/MediaOutput.cpp index 425c93842e77..fbb5943b8fc4 100644 --- a/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Private/MediaOutput.cpp +++ b/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Private/MediaOutput.cpp @@ -5,7 +5,9 @@ #include "MediaCapture.h" #include "MediaIOCoreModule.h" -/* IMediaOptions interface +const FIntPoint UMediaOutput::RequestCaptureSourceSize = FIntPoint::ZeroValue; + +/* UMediaOutput *****************************************************************************/ UMediaOutput::UMediaOutput(const FObjectInitializer& ObjectInitializer) diff --git a/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Public/MediaCapture.h b/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Public/MediaCapture.h index 02f4dbbfbc87..8adde89c7124 100644 --- a/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Public/MediaCapture.h +++ b/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Public/MediaCapture.h @@ -82,8 +82,17 @@ public: * Crop the captured SceneViewport or TextureRenderTarget2D to the desired size. * @note Only valid when Crop is set to Custom. */ - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "MediaCapture") + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="MediaCapture") FIntPoint CustomCapturePoint; + + /** + * When the capture start, resize the source buffer to the desired size. + * @note Only valid when a size is specified by the MediaOutput. + * @note For viewport, the window size will not change. Only the viewport will be resized. + * @note For RenderTarget, the asset will be modified and resized to the desired size. + */ + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="MediaCapture") + bool bResizeSourceBuffer; }; @@ -212,9 +221,30 @@ protected: FFrameRate SourceFrameTimecodeFramerate; uint32 SourceFrameNumberRenderThread; }; + + /** + * Capture the data that will pass along to the callback. + * @note The capture is done on the Render Thread but triggered from the Game Thread. + */ virtual TSharedPtr GetCaptureFrameUserData_GameThread() { return TSharedPtr(); } + + /** Should we call OnFrameCaptured_RenderingThread() with a RHI Texture -or- copy the memory to CPU ram and call OnFrameCaptured_RenderingThread(). */ + virtual bool ShouldCaptureRHITexture() const { return false; } + + /** + * Callback when the buffer was successfully copied to CPU ram. + * The callback in called from a critical point. If you intend to process the buffer, do so in another thread. + * The buffer is only valid for the duration of the callback. + */ virtual void OnFrameCaptured_RenderingThread(const FCaptureBaseData& InBaseData, TSharedPtr InUserData, void* InBuffer, int32 Width, int32 Height) { } + /** + * Callback when the buffer was successfully copied on the GPU ram. + * The callback in called from a critical point. If you intend to process the texture, do so in another thread. + * The texture is valid for the duration of the callback. + */ + virtual void OnRHITextureCaptured_RenderingThread(const FCaptureBaseData& InBaseData, TSharedPtr InUserData, FTextureRHIRef InTexture) { } + protected: UTextureRenderTarget2D* GetTextureRenderTarget() { return CapturingRenderTarget; } TSharedPtr GetCapturingSceneViewport() { return CapturingSceneViewport.Pin(); } @@ -229,9 +259,12 @@ private: void InitializeResolveTarget(int32 InNumberOfBuffers); void OnEndFrame_GameThread(); void CacheMediaOutput(EMediaCaptureSourceType InSourceType); + void CacheOutputOptions(); FIntPoint GetOutputSize(const FIntPoint & InSize, const EMediaCaptureConversionOperation & InConversionOperation) const; EPixelFormat GetOutputPixelFormat(const EPixelFormat & InPixelFormat, const EMediaCaptureConversionOperation & InConversionOperation) const; void BroadcastStateChanged(); + void SetFixedViewportSize(TSharedPtr InSceneViewport); + void ResetFixedViewportSize(TSharedPtr InViewport, bool bInFlushRenderingCommands); private: struct FCaptureFrame @@ -261,7 +294,10 @@ private: FMediaCaptureOptions DesiredCaptureOptions; EMediaCaptureConversionOperation ConversionOperation; FString MediaOutputName; + bool bUseRequestedTargetSize; bool bResolvedTargetInitialized; + bool bShouldCaptureRHITexture; + bool bViewportHasFixedViewportSize; TAtomic WaitingForResolveCommandExecutionCounter; }; diff --git a/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Public/MediaOutput.h b/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Public/MediaOutput.h index cbd9ee3e7359..6edac71a01f1 100644 --- a/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Public/MediaOutput.h +++ b/Engine/Plugins/Media/MediaIOFramework/Source/MediaIOCore/Public/MediaOutput.h @@ -44,6 +44,7 @@ public: * Number of texture used to transfer the texture from the GPU to the system memory. * A smaller number is most likely to block the GPU (wait for the transfer to complete). * A bigger number is most likely to increase latency. + * @note Some Capture are not are executed on the GPU. If it's the case then no buffer will be needed and no buffer will be created. */ UPROPERTY(EditAnywhere, AdvancedDisplay, Category="Output", meta=(ClampMin=1, ClampMax=4)) int32 NumberOfTextureBuffers; @@ -61,8 +62,25 @@ public: virtual bool Validate(FString& OutFailureReason) const; public: - virtual FIntPoint GetRequestedSize() const PURE_VIRTUAL(UMediaOutput::GetRequestedSize, return FIntPoint::ZeroValue; ); + + static const FIntPoint RequestCaptureSourceSize; + + /** + * The size of the buffer we wish to capture. + * The size of the buffer can not change during the capture. + * Return UMediaOutput::RequestCaptureSourceSize if you wish to take the buffer size as the requested size. + */ + virtual FIntPoint GetRequestedSize() const PURE_VIRTUAL(UMediaOutput::GetRequestedSize, return RequestCaptureSourceSize; ); + + /** + * The pixel format of the buffer we wish to capture. + * Some conversion are available. See EMediaCaptureConversionOperation + */ virtual EPixelFormat GetRequestedPixelFormat() const PURE_VIRTUAL(UMediaOutput::GetRequestedPixelFormat, return EPixelFormat::PF_Unknown; ); + + /** + * The conversion we wish to accomplish on the GPU before the DMA transfer occurs. + */ virtual EMediaCaptureConversionOperation GetConversionOperation(EMediaCaptureSourceType InSourceType) const { return EMediaCaptureConversionOperation::NONE; } protected: diff --git a/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Private/Wmf/WmfMediaHardwareVideoDecodingTextureSample.cpp b/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Private/Wmf/WmfMediaHardwareVideoDecodingTextureSample.cpp index 3866a947d3b9..af8ce6503736 100644 --- a/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Private/Wmf/WmfMediaHardwareVideoDecodingTextureSample.cpp +++ b/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Private/Wmf/WmfMediaHardwareVideoDecodingTextureSample.cpp @@ -46,7 +46,7 @@ ID3D11Texture2D* FWmfMediaHardwareVideoDecodingTextureSample::InitializeSourceTe return SourceTexture; } -#if !WITH_SERVER_CODE +#if !UE_SERVER void FWmfMediaHardwareVideoDecodingTextureSample::ShutdownPoolable() { FWmfMediaTextureSample::ShutdownPoolable(); diff --git a/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Private/WmfMediaModule.cpp b/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Private/WmfMediaModule.cpp index 85a98c2eba74..eabdeec8a1e0 100644 --- a/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Private/WmfMediaModule.cpp +++ b/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Private/WmfMediaModule.cpp @@ -69,6 +69,11 @@ public: //~ IWmfMediaModule interface + virtual bool IsInitialized() const override + { + return Initialized; + } + virtual TSharedPtr CreatePlayer(IMediaEventSink& EventSink) override { #if WMFMEDIA_SUPPORTED_PLATFORM diff --git a/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Public/IWmfMediaModule.h b/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Public/IWmfMediaModule.h index 8aa592e69f71..3d8d1eb427d3 100644 --- a/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Public/IWmfMediaModule.h +++ b/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Public/IWmfMediaModule.h @@ -17,6 +17,12 @@ class IWmfMediaModule { public: + /** + * Is the Wmf media module initialized? + @return True if the module is initialized. + */ + virtual bool IsInitialized() const = 0; + /** * Creates a Windows Media Foundation based media player. * diff --git a/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Public/WmfMediaHardwareVideoDecodingShaders.h b/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Public/WmfMediaHardwareVideoDecodingShaders.h index 8134e2d71bdd..91c7610679f2 100644 --- a/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Public/WmfMediaHardwareVideoDecodingShaders.h +++ b/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Public/WmfMediaHardwareVideoDecodingShaders.h @@ -54,7 +54,7 @@ public: SetSamplerParameter(RHICmdList, ShaderRHI, BilinearClampedSamplerUV, TStaticSamplerState::GetRHI()); SetShaderValue(RHICmdList, ShaderRHI, ColorTransform, MediaShaders::CombineColorTransformAndOffset(MediaShaders::YuvToSrgbDefault, MediaShaders::YUVOffset8bits)); - SetShaderValue(RHICmdList, ShaderRHI, SrgbToLinear, InIsOutputSrgb); + SetShaderValue(RHICmdList, ShaderRHI, SrgbToLinear, InIsOutputSrgb ? 1 : 0); // Explicitly specify integer value, as using boolean falls over on XboxOne. } diff --git a/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Public/WmfMediaHardwareVideoDecodingTextureSample.h b/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Public/WmfMediaHardwareVideoDecodingTextureSample.h index 8ffb3aefb78a..1a81c0ae617d 100644 --- a/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Public/WmfMediaHardwareVideoDecodingTextureSample.h +++ b/Engine/Plugins/Media/WmfMedia/Source/WmfMedia/Public/WmfMediaHardwareVideoDecodingTextureSample.h @@ -105,7 +105,7 @@ public: /** * Called the the sample is returned to the pool for cleanup purposes */ -#if !WITH_SERVER_CODE +#if !UE_SERVER virtual void ShutdownPoolable() override; #endif diff --git a/Engine/Plugins/Online/Android/OnlineSubsystemGooglePlay/Source/OnlineSubsystemGooglePlay_UPL.xml b/Engine/Plugins/Online/Android/OnlineSubsystemGooglePlay/Source/OnlineSubsystemGooglePlay_UPL.xml index 7334210959a8..cff853f93af3 100644 --- a/Engine/Plugins/Online/Android/OnlineSubsystemGooglePlay/Source/OnlineSubsystemGooglePlay_UPL.xml +++ b/Engine/Plugins/Online/Android/OnlineSubsystemGooglePlay/Source/OnlineSubsystemGooglePlay_UPL.xml @@ -8,15 +8,21 @@ - - //Google Play SDK onCreate additions - googleClient = new GoogleApiClient.Builder(this) - .addConnectionCallbacks(this) - .addOnConnectionFailedListener(this) - .addApi(Games.API).addScope(Games.SCOPE_GAMES) - .addApi(Plus.API).addScope(Plus.SCOPE_PLUS_LOGIN) - .build(); - + + //Google Play SDK onCreate additions + try { + GoogleApiClient.Builder gbuilder = new GoogleApiClient.Builder(this); + gbuilder.addConnectionCallbacks(this); + gbuilder.addOnConnectionFailedListener(this); + gbuilder.addApiIfAvailable(Games.API, Games.SCOPE_GAMES); + googleClient = gbuilder.build(); + } + catch (Exception e) + { + Log.debug("GoogleApiClient exception caught: " + e.toString()); + } + Log.debug("googleClient is " + ((googleClient == null) ? "disabled" : "valid")); + diff --git a/Engine/Plugins/Online/IOS/OnlineSubsystemIOS/Source/Private/OnlineUserCloudInterfaceIOS.cpp b/Engine/Plugins/Online/IOS/OnlineSubsystemIOS/Source/Private/OnlineUserCloudInterfaceIOS.cpp index 83b0c58a10ff..5f8a1ebcf038 100644 --- a/Engine/Plugins/Online/IOS/OnlineSubsystemIOS/Source/Private/OnlineUserCloudInterfaceIOS.cpp +++ b/Engine/Plugins/Online/IOS/OnlineSubsystemIOS/Source/Private/OnlineUserCloudInterfaceIOS.cpp @@ -25,7 +25,16 @@ [[NSNotificationCenter defaultCenter] addObserver:self selector : @selector(iCloudAccountAvailabilityChanged:) name: NSUbiquityIdentityDidChangeNotification object : nil]; } - CloudContainer = [CKContainer defaultContainer]; + NSString *ICloudContainerIdentifier = [[NSBundle mainBundle].infoDictionary objectForKey : @"ICloudContainerIdentifier"]; + if (ICloudContainerIdentifier != nil) + { + NSLog(@"Using a custom CloudKit container: %@", ICloudContainerIdentifier); + CloudContainer = [CKContainer containerWithIdentifier:ICloudContainerIdentifier]; + } + else + { + CloudContainer = [CKContainer defaultContainer]; + } SharedDatabase = [CloudContainer publicCloudDatabase]; UserDatabase = [CloudContainer privateCloudDatabase]; } diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/Chat/ChatSlashCommands.cpp b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/Chat/ChatSlashCommands.cpp index 0ade3a0d3b62..481248389d1c 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/Chat/ChatSlashCommands.cpp +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/Chat/ChatSlashCommands.cpp @@ -119,7 +119,7 @@ bool FRegisteredSlashCommands::NotifyUserTextChanged(const FText& InText) { if (Cmd->RequiresUserForExecution()) { - if (!AutoCompleteDatum->OptionalTargetUser.IsValid()) + if (AutoCompleteDatum->OptionalTargetUser.IsValid()) { Cmd->ExecuteSlashCommand(AutoCompleteDatum->OptionalTargetUser.Get()); return true; diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/Party/PartyMember.cpp b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/Party/PartyMember.cpp index 5824e8e4daef..2657ba511403 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/Party/PartyMember.cpp +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/Party/PartyMember.cpp @@ -38,6 +38,11 @@ const USocialParty* FPartyMemberRepData::GetOwnerParty() const return OwnerMember.IsValid() ? &OwnerMember->GetParty() : nullptr; } +const UPartyMember* FPartyMemberRepData::GetOwningMember() const +{ + return OwnerMember.IsValid() ? OwnerMember.Get() : nullptr; +} + ////////////////////////////////////////////////////////////////////////// // PartyMember ////////////////////////////////////////////////////////////////////////// diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/Party/SocialParty.cpp b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/Party/SocialParty.cpp index 02e3c4eab373..954e6884878e 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/Party/SocialParty.cpp +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/Party/SocialParty.cpp @@ -416,10 +416,15 @@ void USocialParty::InitializePartyInternal() PartyInterface->AddOnPartyMemberDataReceivedDelegate_Handle(FOnPartyMemberDataReceivedDelegate::CreateUObject(this, &USocialParty::HandlePartyMemberDataReceived)); PartyInterface->AddOnPartyMemberPromotedDelegate_Handle(FOnPartyMemberPromotedDelegate::CreateUObject(this, &USocialParty::HandlePartyMemberPromoted)); PartyInterface->AddOnPartyMemberExitedDelegate_Handle(FOnPartyMemberExitedDelegate::CreateUObject(this, &USocialParty::HandlePartyMemberExited)); - //PartyInterface->AddOnPartyMemberStateChangedDelegate_Handle(FOnPartyMemberStateChangedDelegate::CreateUObject(this, &USocialParty::HandlePartyMemberStateChanged)); + // Create a UPartyMember for every existing member on the OSS party TArray> OssPartyMembers; PartyInterface->GetPartyMembers(*OwningLocalUserId, GetPartyId(), OssPartyMembers); + // Always initialize the local member first + if (ensure(OssPartyMembers.RemoveAll([this](const TSharedRef& Member) { return *Member->GetUserId() == *OwningLocalUserId; } ) > 0)) + { + GetOrCreatePartyMember(*OwningLocalUserId); + } for (TSharedRef& OssMember : OssPartyMembers) { GetOrCreatePartyMember(*OssMember->GetUserId()); @@ -535,9 +540,7 @@ UPartyMember* USocialParty::GetOrCreatePartyMember(const FUniqueNetId& MemberId) PartyMembersById.Add(MemberIdRepl, PartyMember); PartyMember->InitializePartyMember(OssPartyMember.ToSharedRef(), FSimpleDelegate::CreateUObject(this, &USocialParty::HandleMemberInitialized, PartyMember)); - RefreshPublicJoinability(); - - OnPartyMemberCreated().Broadcast(*PartyMember); + OnMemberCreatedInternal(*PartyMember); } else { @@ -864,6 +867,12 @@ void USocialParty::HandlePrivacySettingsChanged(const FPartyPrivacySettings& New RefreshPublicJoinability(); } +void USocialParty::OnMemberCreatedInternal(UPartyMember& NewMember) +{ + RefreshPublicJoinability(); + OnPartyMemberCreated().Broadcast(NewMember); +} + void USocialParty::HandlePartyLeft(const FUniqueNetId& LocalUserId, const FOnlinePartyId& PartyId) { // this function is called when a party is left due to unintentional leave (e.g. disconnect) @@ -978,7 +987,7 @@ bool USocialParty::ShouldStayWithPartyOnExit() const bool USocialParty::IsPartyFunctionalityDegraded() const { - return bIsMissingXmppConnection || bIsMissingPlatformSession; + return bIsMissingXmppConnection.Get(false) || bIsMissingPlatformSession; } int32 USocialParty::GetNumPartyMembers() const @@ -1404,9 +1413,10 @@ void USocialParty::SetIsMissingPlatformSession(bool bInIsMissingPlatformSession) void USocialParty::SetIsMissingXmppConnection(bool bInMissingXmppConnection) { - if (bInMissingXmppConnection != bIsMissingXmppConnection) + if (!bIsMissingXmppConnection.IsSet() || + bInMissingXmppConnection != bIsMissingXmppConnection) { - UE_LOG(LogParty, VeryVerbose, TEXT("Party [%s] is %s missing XMPP connection"), *ToDebugString(), bInMissingXmppConnection ? TEXT("now") : TEXT("no longer")); + UE_CLOG(bIsMissingXmppConnection.IsSet(), LogParty, VeryVerbose, TEXT("Party [%s] is %s missing XMPP connection"), *ToDebugString(), bInMissingXmppConnection ? TEXT("now") : TEXT("no longer")); const bool bWasPartyFunctionalityDegraded = IsPartyFunctionalityDegraded(); bIsMissingXmppConnection = bInMissingXmppConnection; diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialDebugTools.cpp b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialDebugTools.cpp index 8ea2679c9f60..5d938d58fbeb 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialDebugTools.cpp +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialDebugTools.cpp @@ -63,56 +63,89 @@ bool USocialDebugTools::Exec(class UWorld* InWorld, const TCHAR* Cmd, FOutputDev } else { + bool bAllInstancesRequested = false; + TArray TargetInstances; + // strip out context to parse next entry - FParse::Command(&Cmd, *FString(TEXT("CONTEXT=") + Instance)); + FParse::Command(&Cmd, *FString(TEXT("CONTEXT=") + Instance)); + + if(Instance.Equals(TEXT("all"))) + { + bAllInstancesRequested = true; + GetContextNames(TargetInstances); + if(TargetInstances.Num() == 0) + { + UE_LOG(LogParty, Log, TEXT("CONTEXT=ALL used, but no OSS contexts found!")); + } + } + else + { + TargetInstances.Add(Instance); + } if (FParse::Command(&Cmd, TEXT("LOGIN"))) { - const FString Id = FParse::Token(Cmd, false); - const FString Auth = FParse::Token(Cmd, false); - - Login(Instance, FOnlineAccountCredentials(TEXT("epic"), Id, Auth), FLoginComplete::CreateLambda([this, Instance](bool bSuccess) + if (bAllInstancesRequested) { - UE_LOG(LogParty, Display, TEXT("Login OSS context[%s] %s"), *Instance, *LexToString(bSuccess)); - })); + UE_LOG(LogParty, Log, TEXT("CONTEXT=ALL cannot be used for the LOGIN command!")); + } + else + { + const FString Id = FParse::Token(Cmd, false); + const FString Auth = FParse::Token(Cmd, false); + + Login(Instance, FOnlineAccountCredentials(TEXT("epic"), Id, Auth), FLoginComplete::CreateLambda([this, Instance](bool bSuccess) + { + UE_LOG(LogParty, Display, TEXT("Login OSS context[%s] %s"), *Instance, *LexToString(bSuccess)); + })); + } } else if (FParse::Command(&Cmd, TEXT("LOGOUT"))) { - Logout(Instance, FLogoutComplete::CreateLambda([this, Instance](bool bSuccess) + for (const FString& TargetInstance : TargetInstances) { - UE_LOG(LogParty, Display, TEXT("Logout OSS context[%s] %s"), *Instance, *LexToString(bSuccess)); - })); + Logout(TargetInstance, FLogoutComplete::CreateLambda([this, TargetInstance](bool bSuccess) + { + UE_LOG(LogParty, Display, TEXT("Logout OSS context[%s] %s"), *TargetInstance, *LexToString(bSuccess)); + })); + } } else if (FParse::Command(&Cmd, TEXT("JOINPARTY"))) { const FString Id = FParse::Token(Cmd, false); const FString Auth = FParse::Token(Cmd, false); const FString FriendName = FParse::Token(Cmd, false); - - Login(Instance, FOnlineAccountCredentials(TEXT("epic"), Id, Auth), FLoginComplete::CreateLambda([this, Instance, FriendName](bool bSuccess) + + for( const FString& TargetInstance : TargetInstances ) { - UE_LOG(LogParty, Display, TEXT("Login OSS context[%s] %s"), *Instance, *LexToString(bSuccess)); - - if (bSuccess) + Login(TargetInstance, FOnlineAccountCredentials(TEXT("epic"), Id, Auth), FLoginComplete::CreateLambda([this, TargetInstance, FriendName](bool bSuccess) { - LeaveParty(Instance, FLeavePartyComplete::CreateLambda([this, Instance, FriendName](bool bLeavePartySuccess) - { - UE_LOG(LogParty, Display, TEXT("Leave party OSS context[%s] %s"), *Instance, *LexToString(bLeavePartySuccess)); + UE_LOG(LogParty, Display, TEXT("Login OSS context[%s] %s"), *TargetInstance, *LexToString(bSuccess)); - JoinParty(Instance, FriendName, FJoinPartyComplete::CreateLambda([this, Instance](bool bJoinPartySuccess) + if (bSuccess) + { + LeaveParty(TargetInstance, FLeavePartyComplete::CreateLambda([this, TargetInstance, FriendName](bool bLeavePartySuccess) { - UE_LOG(LogParty, Display, TEXT("Join party OSS context[%s] %s"), *Instance, *LexToString(bJoinPartySuccess)); - })); - })); - } - })); + UE_LOG(LogParty, Display, TEXT("Leave party OSS context[%s] %s"), *TargetInstance, *LexToString(bLeavePartySuccess)); + + JoinParty(TargetInstance, FriendName, FJoinPartyComplete::CreateLambda([this, TargetInstance](bool bJoinPartySuccess) + { + UE_LOG(LogParty, Display, TEXT("Join party OSS context[%s] %s"), *TargetInstance, *LexToString(bJoinPartySuccess)); + })); + })); + } + })); + } } else if (FParse::Command(&Cmd, TEXT("LEAVEPARTY"))) { - LeaveParty(Instance, FLeavePartyComplete::CreateLambda([this, Instance](bool bSuccess) + for (const FString& TargetInstance : TargetInstances) { - UE_LOG(LogParty, Display, TEXT("Leave party OSS context[%s] %s"), *Instance, *LexToString(bSuccess)); - })); + LeaveParty(TargetInstance, FLeavePartyComplete::CreateLambda([this, TargetInstance](bool bSuccess) + { + UE_LOG(LogParty, Display, TEXT("Leave party OSS context[%s] %s"), *TargetInstance, *LexToString(bSuccess)); + })); + } } else if (FParse::Command(&Cmd, TEXT("AUTOACCEPTFRIENDINVITES"))) { @@ -140,11 +173,15 @@ bool USocialDebugTools::Exec(class UWorld* InWorld, const TCHAR* Cmd, FOutputDev } else if (FParse::Command(&Cmd, TEXT("DUMPPARTY"))) { - IOnlineSubsystem* OnlineSub = GetContext(Instance).GetOSS(); - if (OnlineSub && - OnlineSub->GetPartyInterface().IsValid()) + for (const FString& TargetInstance : TargetInstances) { - OnlineSub->GetPartyInterface()->DumpPartyState(); + UE_LOG(LogParty, Display, TEXT("---DUMPPARTY - party OSS context[%s]"), *TargetInstance); + IOnlineSubsystem* OnlineSub = GetContext(TargetInstance).GetOSS(); + if (OnlineSub && + OnlineSub->GetPartyInterface().IsValid()) + { + OnlineSub->GetPartyInterface()->DumpPartyState(); + } } } } @@ -593,6 +630,9 @@ void USocialDebugTools::FInstanceContext::Shutdown() OnlineParty.Reset(); } OnlineSub->Shutdown(); + + const FString OSSName = FName(MCP_SUBSYSTEM).ToString() + TEXT(":") + Name; + IOnlineSubsystem::Destroy(*OSSName); } } diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialManager.cpp b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialManager.cpp index 806f159e5da2..a3048372da0b 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialManager.cpp +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialManager.cpp @@ -203,7 +203,7 @@ void USocialManager::InitSocialManager() } #if !UE_SERVER && !UE_BUILD_SHIPPING - SocialDebugTools = NewObject(this); + SocialDebugTools = NewObject(this, GetSocialDebugToolsClass()); check(SocialDebugTools); #endif @@ -302,6 +302,11 @@ void USocialManager::HandlePlatformSessionInviteAccepted(const TSharedRef USocialManager::GetSocialDebugToolsClass() const +{ + return USocialDebugTools::StaticClass(); +} + USocialToolkit* USocialManager::GetFirstLocalUserToolkit() const { if (SocialToolkits.Num() > 0) @@ -826,7 +831,7 @@ void USocialManager::HandleWorldEstablished(UWorld* World) void USocialManager::HandleLocalPlayerAdded(int32 LocalUserNum) { - ULocalPlayer* NewLocalPlayer = GetGameInstance().FindLocalPlayerFromControllerId(LocalUserNum); + ULocalPlayer* NewLocalPlayer = GetGameInstance().GetLocalPlayerByIndex(LocalUserNum); check(NewLocalPlayer); CreateSocialToolkit(*NewLocalPlayer); @@ -841,6 +846,47 @@ void USocialManager::HandleLocalPlayerRemoved(int32 LocalUserNum) } } +void USocialManager::RestorePartyStateFromPartySystem(const FOnRestorePartyStateFromPartySystemComplete& OnRestoreComplete) +{ + UE_LOG(LogParty, Verbose, TEXT("RestorePartyStateFromPartySystem")); + FUniqueNetIdRepl LocalUserId = GetFirstLocalUserId(ESocialSubsystem::Primary); + + // If the player has any parties, do not try to restore + if (LocalUserId.IsValid() && + bCanCreatePartyObjects && + JoinedPartiesByTypeId.Num() == 0 && + JoinAttemptsByTypeId.Num() == 0) + { + IOnlinePartyPtr PartyInterface = Online::GetPartyInterfaceChecked(GetWorld()); + PartyInterface->RestoreParties(*LocalUserId, FOnRestorePartiesComplete::CreateUObject(this, &USocialManager::OnRestorePartiesComplete, OnRestoreComplete)); + } + else + { + OnRestoreComplete.ExecuteIfBound(false); + } +} + +void USocialManager::OnRestorePartiesComplete(const FUniqueNetId& LocalUserId, const FOnlineError& Result, const FOnRestorePartyStateFromPartySystemComplete OnRestoreComplete) +{ + if (Result.WasSuccessful()) + { + // Restore our parties + IOnlinePartyPtr PartyInterface = Online::GetPartyInterfaceChecked(GetWorld()); + TArray> JoinedParties; + PartyInterface->GetJoinedParties(LocalUserId, JoinedParties); + for (const TSharedRef& PartyId : JoinedParties) + { + TSharedPtr Party = PartyInterface->GetParty(LocalUserId, *PartyId); + check(Party.IsValid()); + if (!EstablishNewParty(LocalUserId, *PartyId, Party->PartyTypeId)) + { + UE_LOG(LogParty, Warning, TEXT("OnRestorePartiesComplete: User=[%s] Party=[%s] Type=%d failed to establish party"), *LocalUserId.ToDebugString(), *PartyId->ToDebugString(), Party->PartyTypeId.GetValue()); + } + } + } + OnRestoreComplete.ExecuteIfBound(Result.WasSuccessful()); +} + void USocialManager::HandleQueryJoinabilityComplete(const FUniqueNetId& LocalUserId, const FOnlinePartyId& PartyId, EJoinPartyCompletionResult Result, int32 NotApprovedReasonCode, FOnlinePartyTypeId PartyTypeId) { if (FJoinPartyAttempt* JoinAttempt = JoinAttemptsByTypeId.Find(PartyTypeId)) diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialSettings.cpp b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialSettings.cpp index db2997863b05..8cec08c98bd3 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialSettings.cpp +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialSettings.cpp @@ -33,3 +33,9 @@ int32 USocialSettings::GetDefaultMaxPartySize() const USocialSettings& SettingsCDO = *GetDefault(); return SettingsCDO.DefaultMaxPartySize; } + +float USocialSettings::GetUserListAutoUpdateRate() +{ + const USocialSettings& SettingsCDO = *GetDefault(); + return SettingsCDO.UserListAutoUpdateRate; +} diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialToolkit.cpp b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialToolkit.cpp index dc74d83d6c6e..a3e5cd0cecf9 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialToolkit.cpp +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialToolkit.cpp @@ -110,7 +110,7 @@ public: //@todo DanH Social: Need a non-backdoor way to get toolkits from the manager (an issue when we don't know where the manager is) - new game subsystems should be a nice solve TMap, TWeakObjectPtr> USocialToolkit::AllToolkitsByOwningPlayer; -USocialToolkit* USocialToolkit::GetToolkitForPlayer(ULocalPlayer* LocalPlayer) +USocialToolkit* USocialToolkit::GetToolkitForPlayerInternal(ULocalPlayer* LocalPlayer) { TWeakObjectPtr* FoundToolkit = AllToolkitsByOwningPlayer.Find(LocalPlayer); return FoundToolkit ? FoundToolkit->Get() : nullptr; @@ -607,8 +607,10 @@ void USocialToolkit::HandlePlayerLoginStatusChanged(int32 LocalUserNum, ELoginSt { if (NewStatus == ELoginStatus::LoggedIn) { - if (!ensure(AllUsers.Num() == 0)) + if (AllUsers.Num() != 0) { + UE_LOG(LogParty, Error, TEXT("HandlePlayerLoginStatusChanged: Changed login status but we were not informed their status had changed previously")); + // Nobody told us we logged out! Handle it now just so we're fresh, but not good! OnOwnerLoggedOut(); } diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialTypes.cpp b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialTypes.cpp index 4ceb66399e35..39b16050e417 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialTypes.cpp +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/SocialTypes.cpp @@ -7,6 +7,10 @@ // FUserPlatform ////////////////////////////////////////////////////////////////////////// +#define PLATFORM_NAME_PC TEXT("PC") +#define PLATFORM_NAME_CONSOLE TEXT("CONSOLE") +#define PLATFORM_NAME_MOBILE TEXT("MOBILE") + bool FUserPlatform::operator==(const FString& OtherStr) const { return PlatformStr == OtherStr; @@ -17,6 +21,26 @@ bool FUserPlatform::operator==(const FUserPlatform& Other) const return PlatformStr == Other.PlatformStr; } +const FString FUserPlatform::GetTypeName() const +{ + if (IsDesktop()) + { + return PLATFORM_NAME_PC; + } + + if (IsMobile()) + { + return PLATFORM_NAME_MOBILE; + } + + FUserPlatform LocalPlatform = FUserPlatform(IOnlineSubsystem::GetLocalPlatformName()); + if (IsConsole() && LocalPlatform.IsConsole() && PlatformStr != LocalPlatform) + { + return PLATFORM_NAME_CONSOLE; + } + return PlatformStr; +} + bool FUserPlatform::IsValid() const { return !PlatformStr.IsEmpty(); diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/User/SocialUserList.cpp b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/User/SocialUserList.cpp index 85c1e2b2eeed..9dccdf42f5ee 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/User/SocialUserList.cpp +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/User/SocialUserList.cpp @@ -7,6 +7,8 @@ #include "Containers/Ticker.h" #include "Interfaces/OnlinePresenceInterface.h" +#include "SocialSettings.h" +#include "Algo/Transform.h" TSharedRef FSocialUserList::CreateUserList(USocialToolkit& InOwnerToolkit, const FSocialUserListConfig& InConfig) { @@ -61,7 +63,10 @@ void FSocialUserList::InitializeList() TryAddUserFast(*User); } - SetAutoUpdatePeriod(AutoUpdatePeriod); + if (ListConfig.bRequireAutoUpdate) + { + SetAutoUpdatePeriod(USocialSettings::GetUserListAutoUpdateRate()); + } } void FSocialUserList::AddReferencedObjects(FReferenceCollector& Collector) @@ -71,7 +76,7 @@ void FSocialUserList::AddReferencedObjects(FReferenceCollector& Collector) void FSocialUserList::UpdateNow() { - HandleAutoUpdateList(0.f); + UpdateListInternal(); } void FSocialUserList::SetAutoUpdatePeriod(float InAutoUpdatePeriod) @@ -117,23 +122,25 @@ void FSocialUserList::HandleOwnerToolkitReset() void FSocialUserList::HandlePartyInviteReceived(USocialUser& InvitingUser) { TryAddUser(InvitingUser); + UpdateListInternal(); } void FSocialUserList::HandlePartyInviteHandled(USocialUser* InvitingUser) { TryRemoveUser(*InvitingUser); - UpdateNow(); + UpdateListInternal(); } void FSocialUserList::HandleFriendInviteReceived(USocialUser& User, ESocialSubsystem SubsystemType) { TryAddUser(User); + UpdateListInternal(); } void FSocialUserList::HandleFriendInviteRemoved(ESocialSubsystem SubsystemType, USocialUser* User) { TryRemoveUser(*User); - UpdateNow(); + UpdateListInternal(); } void FSocialUserList::HandleFriendshipEstablished(USocialUser& NewFriend, ESocialSubsystem SubsystemType, bool bIsNewRelationship) @@ -149,14 +156,14 @@ void FSocialUserList::HandleFriendshipEstablished(USocialUser& NewFriend, ESocia { // Any non-friends list that cares about friendship does so to remove entries (i.e. invites & recent players) TryRemoveUser(NewFriend); - UpdateNow(); + UpdateListInternal(); } } void FSocialUserList::HandleFriendRemoved(ESocialSubsystem SubsystemType, USocialUser* User) { TryRemoveUser(*User); - UpdateNow(); + UpdateListInternal(); } void FSocialUserList::HandleUserBlocked(USocialUser& BlockedUser, ESocialSubsystem SubsystemType, bool bIsNewRelationship) @@ -171,7 +178,7 @@ void FSocialUserList::HandleUserBlocked(USocialUser& BlockedUser, ESocialSubsyst TryRemoveUser(BlockedUser); } - UpdateNow(); + UpdateListInternal(); } void FSocialUserList::HandleUserBlockStatusChanged(ESocialSubsystem SubsystemType, bool bIsBlocked, USocialUser* User) @@ -179,7 +186,7 @@ void FSocialUserList::HandleUserBlockStatusChanged(ESocialSubsystem SubsystemTyp if (!bIsBlocked) { TryRemoveUser(*User); - UpdateNow(); + UpdateListInternal(); } } @@ -356,7 +363,50 @@ bool FSocialUserList::EvaluatePresenceFlag(bool bPresenceValue, ESocialUserState return true; } +// encapsulates UserList sorting comparator and supporting data needed +struct FUserSortData +{ + FUserSortData(USocialUser* InUser, EOnlinePresenceState::Type InStatus, bool InPlayingThisGame, FString InDisplayName) + : User(InUser), OnlineStatus(InStatus), PlayingThisGame(InPlayingThisGame), DisplayName(MoveTemp(InDisplayName)) + { } + + USocialUser* User; + EOnlinePresenceState::Type OnlineStatus; + bool PlayingThisGame; + FString DisplayName; + + bool operator<(const FUserSortData& OtherSortData) const + { + // Goes from if online, then alphabetical + if (OnlineStatus == OtherSortData.OnlineStatus) + { + if (PlayingThisGame == OtherSortData.PlayingThisGame) + { + return DisplayName < OtherSortData.DisplayName; + } + else + { + return false; + } + } + else + { + // @todo StephanJ: note Online < Offline < Away, but it's okay for now since we show offline in a separate list #future + return OnlineStatus < OtherSortData.OnlineStatus; + } + } +}; + bool FSocialUserList::HandleAutoUpdateList(float) +{ + if (bAllowAutoUpdate) + { + UpdateListInternal(); + } + return true; +} + +void FSocialUserList::UpdateListInternal() { // Re-evaluate whether each user with dirtied presence is still fit for the list for (TWeakObjectPtr DirtyUser : UsersWithDirtyPresence) @@ -385,24 +435,25 @@ bool FSocialUserList::HandleAutoUpdateList(float) Users.RemoveAllSwap( [this] (USocialUser* User) { - return PendingRemovals.Contains(User); + if (PendingRemovals.Contains(User)) + { + PendingRemovals.Remove(User); + OnUserRemoved().Broadcast(*User); + return true; + } + + return false; }); - for (TWeakObjectPtr User : PendingRemovals) - { - if (User.IsValid()) - { - OnUserRemoved().Broadcast(*User.Get()); - } - } + ensure(PendingRemovals.Num() == 0); PendingRemovals.Reset(); } - + if (PendingAdds.Num() > 0) { bListChanged = true; Users.Append(PendingAdds); - + for (USocialUser* User : PendingAdds) { OnUserAdded().Broadcast(*User); @@ -414,43 +465,23 @@ bool FSocialUserList::HandleAutoUpdateList(float) { bNeedsSort = false; - Users.Sort([](USocialUser& UserA, USocialUser& UserB) - { - const UPartyMember* PartyMemberA = UserA.GetPartyMember(IOnlinePartySystem::GetPrimaryPartyTypeId()); - const UPartyMember* PartyMemberB = UserB.GetPartyMember(IOnlinePartySystem::GetPrimaryPartyTypeId()); + const int32 NumUsers = Users.Num(); + TArray SortedData; + SortedData.Reserve(NumUsers); - // Put party members at the top - if (PartyMemberA && !PartyMemberB) - { - return true; - } - else if (PartyMemberB && !PartyMemberA) - { - return false; - } + Algo::Transform(Users, SortedData, [](USocialUser* const User) -> FUserSortData + { + return FUserSortData(User, User->GetOnlineStatus(), User->IsPlayingThisGame(), User->GetDisplayName()); + }); - // Goes from if online, then alphabetical - if (UserA.GetOnlineStatus() == UserB.GetOnlineStatus()) - { - if (UserA.IsPlayingThisGame() == UserB.IsPlayingThisGame()) - { - return UserA.GetDisplayName() < UserB.GetDisplayName(); - } - else - { - return UserA.IsPlayingThisGame() > UserB.IsPlayingThisGame(); - } - } - else - { - // @todo StephanJ: note Online < Offline < Away, but it's okay for now since we show offline in a separate list #future - return UserA.GetOnlineStatus() < UserB.GetOnlineStatus(); - } - - }); + Algo::Sort(SortedData); + // replace contents of Users from SortedData array + for (int Index = 0; Index < NumUsers; Index++) + { + Users[Index] = SortedData[Index].User; + } + OnUpdateComplete().Broadcast(); } - - return true; } diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/User/SocialUserList.h b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/User/SocialUserList.h index 35d58010513e..98dd45151cc0 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/User/SocialUserList.h +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Private/User/SocialUserList.h @@ -17,6 +17,7 @@ public: FOnUpdateComplete& OnUpdateComplete() const override { return OnUpdateCompleteEvent; } void UpdateNow(); + void SetAllowAutoUpdate(bool bIsEnabled) { bAllowAutoUpdate = bIsEnabled; } void SetAutoUpdatePeriod(float InAutoUpdatePeriod); const TArray& GetUsers() const { return Users; } @@ -52,6 +53,7 @@ private: bool EvaluatePresenceFlag(bool bPresenceValue, ESocialUserStateFlags Flag) const; bool HandleAutoUpdateList(float); + void UpdateListInternal(); private: FSocialUserList(USocialToolkit& InOwnerToolkit, const FSocialUserListConfig& Config); @@ -70,6 +72,8 @@ private: FSocialUserListConfig ListConfig; + // give external access to disable list update for perf + bool bAllowAutoUpdate = true; bool bNeedsSort = false; float AutoUpdatePeriod = 5.f; FDelegateHandle UpdateTickerHandle; diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/Party/PartyMember.h b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/Party/PartyMember.h index f6c721c8aba8..ec8fe5806325 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/Party/PartyMember.h +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/Party/PartyMember.h @@ -27,6 +27,7 @@ protected: virtual bool CanEditData() const override; virtual void CompareAgainst(const FOnlinePartyRepDataBase& OldData) const override; virtual const USocialParty* GetOwnerParty() const override; + virtual const UPartyMember* GetOwningMember() const; private: TWeakObjectPtr OwnerMember; diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/Party/PartyTypes.h b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/Party/PartyTypes.h index 5d46958ebc7d..75bae772ba12 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/Party/PartyTypes.h +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/Party/PartyTypes.h @@ -437,15 +437,9 @@ public: \ FOn##Property##ChangedDif& On##Property##ChangedDif() const { return On##Property##ChangedDifEvent; } \ \ Property##ArgType Get##Property() const { return Property; } \ - \ - bool Has##Property##InitiallyReplicated() const { return b##Property##InitiallyReplicated; } \ private: \ void Compare##Property(const Owner& OldData) const \ { \ - if(!b##Property##InitiallyReplicated) \ - { \ - b##Property##InitiallyReplicated = true; \ - } \ if (Property != OldData.Property) \ { \ LogPropertyChanged(TEXT(#Owner), TEXT(#Property), true); \ @@ -454,8 +448,7 @@ private: \ } \ } \ mutable FOn##Property##Changed On##Property##ChangedEvent; \ - mutable FOn##Property##ChangedDif On##Property##ChangedDifEvent; \ - mutable bool b##Property##InitiallyReplicated + mutable FOn##Property##ChangedDif On##Property##ChangedDifEvent /** * Exposes a rep data property and provides a default property setter. diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/Party/SocialParty.h b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/Party/SocialParty.h index fbb0b7a530c0..bef8662294b7 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/Party/SocialParty.h +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/Party/SocialParty.h @@ -218,6 +218,7 @@ protected: virtual FPartyPrivacySettings GetDesiredPrivacySettings() const; virtual void OnLocalPlayerIsLeaderChanged(bool bIsLeader); virtual void HandlePrivacySettingsChanged(const FPartyPrivacySettings& NewPrivacySettings); + virtual void OnMemberCreatedInternal(UPartyMember& NewMember); virtual void OnLeftPartyInternal(EMemberExitedReason Reason); virtual void OnInviteSentInternal(ESocialSubsystem SubsystemType, const USocialUser& InvitedUser, bool bWasSuccessful); @@ -300,7 +301,6 @@ private: // Handlers void HandlePartyMemberJIP(const FUniqueNetId& LocalUserId, const FOnlinePartyId& PartyId, bool Success); void HandlePartyMemberPromoted(const FUniqueNetId& LocalUserId, const FOnlinePartyId& PartyId, const FUniqueNetId& NewLeaderId); void HandlePartyPromotionLockoutChanged(const FUniqueNetId& LocalUserId, const FOnlinePartyId& PartyId, bool bArePromotionsLocked); - void HandlePartyMemberConnectionStatusChanged(const FUniqueNetId& LocalUserId, const FOnlinePartyId& PartyId, EMemberConnectionStatus MemberConnectionStatus); void HandleMemberInitialized(UPartyMember* Member); void HandleMemberSessionIdChanged(const FSessionId& NewSessionId, UPartyMember* Member); @@ -348,7 +348,7 @@ private: /** Reservation beacon client instance while getting approval for new party members*/ UPROPERTY() - APartyBeaconClient* ReservationBeaconClient = nullptr; + APartyBeaconClient* ReservationBeaconClient = nullptr; /** * Last known spectator beacon client net driver name @@ -359,13 +359,13 @@ private: /** Spectator beacon client instance while getting approval for spectator*/ UPROPERTY() - ASpectatorBeaconClient* SpectatorBeaconClient = nullptr; + ASpectatorBeaconClient* SpectatorBeaconClient = nullptr; /** * True when we have limited functionality due to lacking an xmpp connection. * Don't set directly, use the private setter to trigger events appropriately. */ - bool bIsMissingXmppConnection = false; + TOptional bIsMissingXmppConnection; bool bIsMissingPlatformSession = false; bool bIsLeavingParty = false; diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialDebugTools.h b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialDebugTools.h index b6b04ae5f481..f72ed3a731dc 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialDebugTools.h +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialDebugTools.h @@ -43,10 +43,7 @@ public: virtual void SetPartyMemberData(const FString& Instance, const UStruct* StructType, const void* StructData, const FSetPartyMemberDataComplete& OnComplete); virtual void SetPartyMemberDataJson(const FString& Instance, const FString& JsonStr, const FSetPartyMemberDataComplete& OnComplete); -private: - - bool bAutoAcceptFriendInvites; - bool bAutoAcceptPartyInvites; + virtual void GetContextNames(TArray& OutContextNames) const { Contexts.GenerateKeyArray(OutContextNames); } struct FInstanceContext { @@ -73,10 +70,16 @@ private: FDelegateHandle FriendInviteReceivedDelegateHandle; FDelegateHandle PartyInviteReceivedDelegateHandle; }; + + FInstanceContext& GetContext(const FString& Instance); + FInstanceContext* GetContextForUser(const FUniqueNetId& UserId); +private: + + bool bAutoAcceptFriendInvites; + bool bAutoAcceptPartyInvites; + TMap Contexts; - FInstanceContext& GetContext(const FString& Instance); - FInstanceContext* GetContextForUser(const FUniqueNetId& UserId); TSharedPtr GetDefaultPartyJoinInfo() const; IOnlineSubsystem* GetDefaultOSS() const; void PrintExecUsage(); diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialManager.h b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialManager.h index 827a1457e226..9d8eef8986a6 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialManager.h +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialManager.h @@ -65,6 +65,10 @@ public: void CreateParty(const FOnlinePartyTypeId& PartyTypeId, const FPartyConfiguration& PartyConfig, const FOnCreatePartyAttemptComplete& OnCreatePartyComplete); void CreatePersistentParty(const FOnCreatePartyAttemptComplete& OnCreatePartyComplete = FOnCreatePartyAttemptComplete()); + /** Attempt to restore our party state from the party system */ + DECLARE_DELEGATE_OneParam(FOnRestorePartyStateFromPartySystemComplete, bool /*bSucceeded*/) + void RestorePartyStateFromPartySystem(const FOnRestorePartyStateFromPartySystemComplete& OnRestoreComplete); + bool IsPartyJoinInProgress(const FOnlinePartyTypeId& TypeId) const; bool IsPersistentPartyJoinInProgress() const; @@ -174,6 +178,8 @@ protected: //@todo DanH: TEMP - for now relying on FN to bind to its game-level UFortOnlineSessionClient instance #required void HandlePlatformSessionInviteAccepted(const TSharedRef& LocalUserId, const FOnlineSessionSearchResult& InviteResult); + virtual TSubclassOf GetSocialDebugToolsClass() const; + /** Info on the persistent party we were in when losing connection to the party service and want to rejoin when it returns */ TSharedPtr RejoinableParty; @@ -201,7 +207,8 @@ private: // Handlers void HandleWorldEstablished(UWorld* World); void HandleLocalPlayerAdded(int32 LocalUserNum); void HandleLocalPlayerRemoved(int32 LocalUserNum); - + + void OnRestorePartiesComplete(const FUniqueNetId& LocalUserId, const FOnlineError& Result, const FOnRestorePartyStateFromPartySystemComplete OnRestoreComplete); void HandleQueryJoinabilityComplete(const FUniqueNetId& LocalUserId, const FOnlinePartyId& PartyId, EJoinPartyCompletionResult Result, int32 NotApprovedReasonCode, FOnlinePartyTypeId PartyTypeId); void HandleCreatePartyComplete(const FUniqueNetId& LocalUserId, const TSharedPtr& PartyId, ECreatePartyCompletionResult Result, FOnlinePartyTypeId PartyTypeId, FOnCreatePartyAttemptComplete CompletionDelegate); void HandleJoinPartyComplete(const FUniqueNetId& LocalUserId, const FOnlinePartyId& PartyId, EJoinPartyCompletionResult Result, int32 NotApprovedReasonCode, FOnlinePartyTypeId PartyTypeId); diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialSettings.h b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialSettings.h index f9d663e076fd..75c9550c66f4 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialSettings.h +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialSettings.h @@ -23,6 +23,7 @@ public: static FString GetUniqueIdEnvironmentPrefix(ESocialSubsystem SubsystemType); static bool ShouldPreferPlatformInvites(); static int32 GetDefaultMaxPartySize(); + static float GetUserListAutoUpdateRate(); private: /** @@ -40,4 +41,7 @@ private: UPROPERTY(config) bool bPreferPlatformInvites = true; + + UPROPERTY(config) + float UserListAutoUpdateRate = 7.5f; }; \ No newline at end of file diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialToolkit.h b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialToolkit.h index dbc615952b9b..8ae3d0439aaf 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialToolkit.h +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialToolkit.h @@ -30,7 +30,12 @@ class PARTY_API USocialToolkit : public UObject GENERATED_BODY() public: - static USocialToolkit* GetToolkitForPlayer(ULocalPlayer* LocalPlayer); + template + static ToolkitT* GetToolkitForPlayer(ULocalPlayer* LocalPlayer) + { + static_assert(TIsDerivedFrom::IsDerived, "GetToolkitForPlayer only supports getting USocialToolkit type objects"); + return Cast(GetToolkitForPlayerInternal(LocalPlayer)); + } USocialToolkit(); @@ -197,6 +202,7 @@ private: // Handlers void HandleGameDestroyed(const FName SessionName, bool bWasSuccessful); private: + static USocialToolkit* GetToolkitForPlayerInternal(ULocalPlayer* LocalPlayer); static TMap, TWeakObjectPtr> AllToolkitsByOwningPlayer; UPROPERTY() diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialTypes.h b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialTypes.h index 8a0ec113fd8e..f6259b07e1b9 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialTypes.h +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/SocialTypes.h @@ -91,6 +91,7 @@ public: operator const FString&() const { return PlatformStr; } const FString& ToString() const { return PlatformStr; } + const FString GetTypeName() const; bool operator==(const FString& OtherStr) const; bool operator!=(const FString& OtherStr) const { return !operator==(OtherStr); } diff --git a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/User/ISocialUserList.h b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/User/ISocialUserList.h index 3c8c39ffc811..20f0070ce7c4 100644 --- a/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/User/ISocialUserList.h +++ b/Engine/Plugins/Online/OnlineFramework/Source/Party/Public/User/ISocialUserList.h @@ -35,6 +35,7 @@ public: ESocialUserStateFlags RequiredPresenceFlags = ESocialUserStateFlags::None; ESocialUserStateFlags ForbiddenPresenceFlags = ESocialUserStateFlags::None; FOnCustomFilterUser OnCustomFilterUser; + bool bRequireAutoUpdate = false; }; class ISocialUserList @@ -57,6 +58,9 @@ public: /** Trigger an update of the list immediately, regardless of auto update period */ virtual void UpdateNow() = 0; + /** Give external overwrite to disable list auto update for perf */ + virtual void SetAllowAutoUpdate(bool bIsEnabled) = 0; + /** Sets the period at which to update the list with all users that */ virtual void SetAutoUpdatePeriod(float InAutoUpdatePeriod) = 0; }; \ No newline at end of file diff --git a/Engine/Plugins/Online/OnlineSubsystem/Source/Private/OnlineSubsystemModule.cpp b/Engine/Plugins/Online/OnlineSubsystem/Source/Private/OnlineSubsystemModule.cpp index 83f6d0ffd384..596771cf8b96 100644 --- a/Engine/Plugins/Online/OnlineSubsystem/Source/Private/OnlineSubsystemModule.cpp +++ b/Engine/Plugins/Online/OnlineSubsystem/Source/Private/OnlineSubsystemModule.cpp @@ -233,47 +233,55 @@ void FOnlineSubsystemModule::EnumerateOnlineSubsystems(FEnumerateOnlineSubsystem FName FOnlineSubsystemModule::ParseOnlineSubsystemName(const FName& FullName, FName& SubsystemName, FName& InstanceName) const { -#if !(UE_GAME || UE_SERVER) - SubsystemName = DefaultPlatformService; - InstanceName = FOnlineSubsystemImpl::DefaultInstanceName; - - if (!FullName.IsNone()) + struct FInstanceNameEntry { - FString FullNameStr = FullName.ToString(); + FName SubsystemName; + FName InstanceName; + FName FullPath; + }; + static TMap InstanceNames; - int32 DelimIdx = INDEX_NONE; - static const TCHAR InstanceDelim = ':'; - if (FullNameStr.FindChar(InstanceDelim, DelimIdx)) - { - if (DelimIdx > 0) - { - SubsystemName = FName(*FullNameStr.Left(DelimIdx)); - } - - if ((DelimIdx + 1) < FullNameStr.Len()) - { - InstanceName = FName(*FullNameStr.RightChop(DelimIdx + 1)); - } - } - else - { - SubsystemName = FName(*FullNameStr); - } + FInstanceNameEntry* Entry = InstanceNames.Find(FullName); + if (Entry) + { + SubsystemName = Entry->SubsystemName; + InstanceName = Entry->InstanceName; } + else + { + SubsystemName = DefaultPlatformService; + InstanceName = FOnlineSubsystemImpl::DefaultInstanceName; - return FName(*FString::Printf(TEXT("%s:%s"), *SubsystemName.ToString(), *InstanceName.ToString())); -#else - - SubsystemName = FullName.IsNone() ? DefaultPlatformService : FullName; - InstanceName = FOnlineSubsystemImpl::DefaultInstanceName; + if (!FullName.IsNone()) + { + FString FullNameStr = FullName.ToString(); -#if !UE_BUILD_SHIPPING - int32 DelimIdx = INDEX_NONE; - static const TCHAR InstanceDelim = ':'; - ensure(!FullName.ToString().FindChar(InstanceDelim, DelimIdx) && DelimIdx == INDEX_NONE); -#endif - return SubsystemName; -#endif // !(UE_GAME || UE_SERVER) + int32 DelimIdx = INDEX_NONE; + static const TCHAR InstanceDelim = ':'; + if (FullNameStr.FindChar(InstanceDelim, DelimIdx)) + { + if (DelimIdx > 0) + { + SubsystemName = FName(*FullNameStr.Left(DelimIdx)); + } + + if ((DelimIdx + 1) < FullNameStr.Len()) + { + InstanceName = FName(*FullNameStr.RightChop(DelimIdx + 1)); + } + } + else + { + SubsystemName = FName(*FullNameStr); + } + } + + Entry = &InstanceNames.Add(FullName); + Entry->SubsystemName = SubsystemName; + Entry->InstanceName = InstanceName; + Entry->FullPath = FName(*FString::Printf(TEXT("%s:%s"), *SubsystemName.ToString(), *InstanceName.ToString())); + } + return Entry->FullPath; } IOnlineSubsystem* FOnlineSubsystemModule::GetOnlineSubsystem(const FName InSubsystemName) @@ -303,7 +311,7 @@ IOnlineSubsystem* FOnlineSubsystemModule::GetOnlineSubsystem(const FName InSubsy if (OSSFactory != nullptr) { - UE_LOG_ONLINE(Verbose, TEXT("Creating online subsystem instance for: %s"), *InSubsystemName.ToString()); + UE_LOG_ONLINE(Log, TEXT("Creating online subsystem instance for: %s"), *InSubsystemName.ToString()); IOnlineSubsystemPtr NewSubsystemInstance = (*OSSFactory)->CreateSubsystem(InstanceName); if (NewSubsystemInstance.IsValid()) diff --git a/Engine/Plugins/Online/OnlineSubsystem/Source/Public/Interfaces/OnlinePartyInterface.h b/Engine/Plugins/Online/OnlineSubsystem/Source/Public/Interfaces/OnlinePartyInterface.h index 3fd66731b5a9..5882658c3b94 100644 --- a/Engine/Plugins/Online/OnlineSubsystem/Source/Public/Interfaces/OnlinePartyInterface.h +++ b/Engine/Plugins/Online/OnlineSubsystem/Source/Public/Interfaces/OnlinePartyInterface.h @@ -611,7 +611,7 @@ struct FPartyInvitationRecipient FString ONLINESUBSYSTEM_API ToDebugString() const; }; - +struct FOnlineError; enum class ECreatePartyCompletionResult; enum class EJoinPartyCompletionResult; enum class ELeavePartyCompletionResult; @@ -627,6 +627,13 @@ enum class EInvitationResponse; /////////////////////////////////////////////////////////////////// // Completion delegates /////////////////////////////////////////////////////////////////// +/** + * Restore parties async task completed callback + * + * @param LocalUserId id of user that initiated the request + * @param Result Result of the operation + */ +DECLARE_DELEGATE_TwoParams(FOnRestorePartiesComplete, const FUniqueNetId& /*LocalUserId*/, const FOnlineError& /*Result*/); /** * Party creation async task completed callback * @@ -929,6 +936,14 @@ protected: public: virtual ~IOnlinePartySystem() {}; + /** + * Restore party memberships. Intended to be called once during login to restore state from other running instances. + * + * @param LocalUserId the user to restore the party membership for + * @param CompletionDelegate the delegate to trigger on completion + */ + virtual void RestoreParties(const FUniqueNetId& LocalUserId, const FOnRestorePartiesComplete& CompletionDelegate) = 0; + /** * Create a new party * diff --git a/Engine/Plugins/Online/OnlineSubsystem/Source/Public/OnlineSubsystemNames.h b/Engine/Plugins/Online/OnlineSubsystem/Source/Public/OnlineSubsystemNames.h index 2dd8413eaee6..73707dd3a353 100644 --- a/Engine/Plugins/Online/OnlineSubsystem/Source/Public/OnlineSubsystemNames.h +++ b/Engine/Plugins/Online/OnlineSubsystem/Source/Public/OnlineSubsystemNames.h @@ -73,6 +73,10 @@ #define MCP_SUBSYSTEM FName(TEXT("MCP")) #endif +#ifndef MCP_SUBSYSTEM_EMBEDDED +#define MCP_SUBSYSTEM_EMBEDDED FName(TEXT("MCP:EMBEDDED")) +#endif + #ifndef TENCENT_SUBSYSTEM #define TENCENT_SUBSYSTEM FName(TEXT("TENCENT")) #endif diff --git a/Engine/Plugins/Online/VoiceChat/VivoxVoiceChat/Source/Private/Android/AndroidVivoxVoiceChat.cpp b/Engine/Plugins/Online/VoiceChat/VivoxVoiceChat/Source/Private/Android/AndroidVivoxVoiceChat.cpp index c35919bfebfc..e93ad49bb558 100644 --- a/Engine/Plugins/Online/VoiceChat/VivoxVoiceChat/Source/Private/Android/AndroidVivoxVoiceChat.cpp +++ b/Engine/Plugins/Online/VoiceChat/VivoxVoiceChat/Source/Private/Android/AndroidVivoxVoiceChat.cpp @@ -86,12 +86,12 @@ bool FAndroidVivoxVoiceChat::Uninitialize() { if (ApplicationWillEnterBackgroundHandle.IsValid()) { - FCoreDelegates::ApplicationWillDeactivateDelegate.Remove(ApplicationWillEnterBackgroundHandle); + FCoreDelegates::ApplicationWillEnterBackgroundDelegate.Remove(ApplicationWillEnterBackgroundHandle); ApplicationWillEnterBackgroundHandle.Reset(); } if (ApplicationDidEnterForegroundHandle.IsValid()) { - FCoreDelegates::ApplicationHasReactivatedDelegate.Remove(ApplicationDidEnterForegroundHandle); + FCoreDelegates::ApplicationHasEnteredForegroundDelegate.Remove(ApplicationDidEnterForegroundHandle); ApplicationDidEnterForegroundHandle.Reset(); } diff --git a/Engine/Plugins/Online/VoiceChat/VivoxVoiceChat/Source/Private/VivoxVoiceChat.cpp b/Engine/Plugins/Online/VoiceChat/VivoxVoiceChat/Source/Private/VivoxVoiceChat.cpp index 69072b17e7ac..8433356d39d5 100644 --- a/Engine/Plugins/Online/VoiceChat/VivoxVoiceChat/Source/Private/VivoxVoiceChat.cpp +++ b/Engine/Plugins/Online/VoiceChat/VivoxVoiceChat/Source/Private/VivoxVoiceChat.cpp @@ -118,39 +118,64 @@ FVivoxVoiceChat::~FVivoxVoiceChat() { } +#if PLATFORM_IOS +FCriticalSection Mutex; +#endif + static void* VivoxMalloc(size_t bytes) { LLM_SCOPE( ELLMTag::AudioVoiceChat ); +#if PLATFORM_IOS + FScopeLock Lock(&Mutex); +#endif return FMemory::Malloc(bytes); } static void VivoxFree(void* ptr) { LLM_SCOPE( ELLMTag::AudioVoiceChat ); +#if PLATFORM_IOS + FScopeLock Lock(&Mutex); +#endif FMemory::Free(ptr); } static void* VivoxRealloc(void* ptr, size_t bytes) { LLM_SCOPE( ELLMTag::AudioVoiceChat ); +#if PLATFORM_IOS + FScopeLock Lock(&Mutex); +#endif return FMemory::Realloc(ptr, bytes); } static void* VivoxCalloc(size_t num, size_t bytes) { LLM_SCOPE( ELLMTag::AudioVoiceChat ); - return FMemory::Malloc(bytes*num); +#if PLATFORM_IOS + FScopeLock Lock(&Mutex); +#endif + const size_t Size = bytes * num; + void* Ret = FMemory::Malloc(Size); + FMemory::Memzero(Ret, Size); + return Ret; } static void* VivoxMallocAligned(size_t alignment, size_t bytes) { LLM_SCOPE( ELLMTag::AudioVoiceChat ); +#if PLATFORM_IOS + FScopeLock Lock(&Mutex); +#endif return FMemory::Malloc(bytes, alignment); } static void VivoxFreeAligned(void* ptr) { LLM_SCOPE( ELLMTag::AudioVoiceChat ); +#if PLATFORM_IOS + FScopeLock Lock(&Mutex); +#endif FMemory::Free(ptr); } @@ -1148,7 +1173,10 @@ void FVivoxVoiceChat::onDisconnected(const VivoxClientApi::Uri& Server, const Vi ClearLoginSession(); - if (ConnectionState == EConnectionState::Disconnecting) + EConnectionState PreviousConnectionState = ConnectionState; + ConnectionState = EConnectionState::Disconnected; + + if (PreviousConnectionState == EConnectionState::Disconnecting) { TriggerCompletionDelegates(OnVoiceChatDisconnectCompleteDelegates, ResultSuccess); } @@ -1156,8 +1184,6 @@ void FVivoxVoiceChat::onDisconnected(const VivoxClientApi::Uri& Server, const Vi { OnVoiceChatDisconnectedDelegate.Broadcast(ResultFromVivoxStatus(Status)); } - - ConnectionState = EConnectionState::Disconnected; } void FVivoxVoiceChat::onLoginCompleted(const VivoxClientApi::AccountName& AccountName) diff --git a/Engine/Plugins/Runtime/AR/Apple/AppleARKit/AppleARKit.uplugin b/Engine/Plugins/Runtime/AR/Apple/AppleARKit/AppleARKit.uplugin index 58553d8477b4..e8ef8c825c95 100644 --- a/Engine/Plugins/Runtime/AR/Apple/AppleARKit/AppleARKit.uplugin +++ b/Engine/Plugins/Runtime/AR/Apple/AppleARKit/AppleARKit.uplugin @@ -13,6 +13,9 @@ "CanContainContent": true, "IsBetaVersion": false, "EnabledByDefault": false, + "SupportedTargetPlatforms" : [ + "IOS" + ], "Modules": [ { "Name": "AppleARKit", diff --git a/Engine/Plugins/Runtime/AR/Apple/AppleARKit/Source/AppleARKit/Private/AppleARKitVideoOverlay.cpp b/Engine/Plugins/Runtime/AR/Apple/AppleARKit/Source/AppleARKit/Private/AppleARKitVideoOverlay.cpp index 079a9d889ddf..9723ad942dee 100644 --- a/Engine/Plugins/Runtime/AR/Apple/AppleARKit/Source/AppleARKit/Private/AppleARKitVideoOverlay.cpp +++ b/Engine/Plugins/Runtime/AR/Apple/AppleARKit/Source/AppleARKit/Private/AppleARKitVideoOverlay.cpp @@ -276,6 +276,7 @@ public: FMaterialShader::ModifyCompilationEnvironment(Platform, OutEnvironment); OutEnvironment.SetDefine(TEXT("POST_PROCESS_MATERIAL"), 1); OutEnvironment.SetDefine(TEXT("POST_PROCESS_MATERIAL_BEFORE_TONEMAP"), (Material->GetBlendableLocation() != BL_AfterTonemapping) ? 1 : 0); + OutEnvironment.SetDefine(TEXT("POST_PROCESS_AR_PASSTHROUGH"), 1); } FARKitCameraOverlayVS() { } diff --git a/Engine/Plugins/Runtime/AR/Apple/AppleARKitFaceSupport/AppleARKitFaceSupport.uplugin b/Engine/Plugins/Runtime/AR/Apple/AppleARKitFaceSupport/AppleARKitFaceSupport.uplugin index 8380994f3363..24629f849809 100644 --- a/Engine/Plugins/Runtime/AR/Apple/AppleARKitFaceSupport/AppleARKitFaceSupport.uplugin +++ b/Engine/Plugins/Runtime/AR/Apple/AppleARKitFaceSupport/AppleARKitFaceSupport.uplugin @@ -13,6 +13,9 @@ "CanContainContent": true, "IsBetaVersion": false, "EnabledByDefault": false, + "SupportedTargetPlatforms" : [ + "IOS" + ], "Modules": [ { diff --git a/Engine/Plugins/Runtime/AR/Google/GoogleARCore/Source/GoogleARCoreRendering/Private/GoogleARCorePassthroughCameraRenderer.cpp b/Engine/Plugins/Runtime/AR/Google/GoogleARCore/Source/GoogleARCoreRendering/Private/GoogleARCorePassthroughCameraRenderer.cpp index 9a97af6ad4cd..31922ebe8e2e 100644 --- a/Engine/Plugins/Runtime/AR/Google/GoogleARCore/Source/GoogleARCoreRendering/Private/GoogleARCorePassthroughCameraRenderer.cpp +++ b/Engine/Plugins/Runtime/AR/Google/GoogleARCore/Source/GoogleARCoreRendering/Private/GoogleARCorePassthroughCameraRenderer.cpp @@ -171,8 +171,8 @@ public: FMaterialShader::ModifyCompilationEnvironment(Platform, OutEnvironment); OutEnvironment.SetDefine(TEXT("POST_PROCESS_MATERIAL"), 1); - OutEnvironment.SetDefine(TEXT("POST_PROCESS_MATERIAL_BEFORE_TONEMAP"), (Material->GetBlendableLocation() != BL_AfterTonemapping) ? 1 : 0); + OutEnvironment.SetDefine(TEXT("POST_PROCESS_AR_PASSTHROUGH"), 1); } FGoogleARCoreCameraOverlayVS( ) { } diff --git a/Engine/Plugins/Runtime/Analytics/Flurry/Source/IOSFlurry/Source/IOSFlurry/IOSFlurry.Build.cs b/Engine/Plugins/Runtime/Analytics/Flurry/Source/IOSFlurry/Source/IOSFlurry/IOSFlurry.Build.cs index 6e9f91c30718..31648e561bde 100644 --- a/Engine/Plugins/Runtime/Analytics/Flurry/Source/IOSFlurry/Source/IOSFlurry/IOSFlurry.Build.cs +++ b/Engine/Plugins/Runtime/Analytics/Flurry/Source/IOSFlurry/Source/IOSFlurry/IOSFlurry.Build.cs @@ -41,12 +41,12 @@ namespace UnrealBuildTool.Rules "CoreLocation" }); - bool bHasFlurrySDK = - (System.IO.Directory.Exists(System.IO.Path.Combine(Target.UEThirdPartySourceDirectory, "Flurry")) && - System.IO.Directory.Exists(System.IO.Path.Combine(Target.UEThirdPartySourceDirectory, "Flurry", "IOS"))) || - (System.IO.Directory.Exists(System.IO.Path.Combine(Target.UEThirdPartySourceDirectory, "NotForLicensees")) && - System.IO.Directory.Exists(System.IO.Path.Combine(Target.UEThirdPartySourceDirectory, "NotForLicensees", "Flurry")) && - System.IO.Directory.Exists(System.IO.Path.Combine(Target.UEThirdPartySourceDirectory, "NotForLicensees", "Flurry", "IOS"))); + bool bHasFlurrySDK = false; // Temporarily disabled. libFlurry_6.7.0.a was compiled without bitcode enabled, so it cannot be used in Shipping config any more +// (System.IO.Directory.Exists(System.IO.Path.Combine(Target.UEThirdPartySourceDirectory, "Flurry")) && +// System.IO.Directory.Exists(System.IO.Path.Combine(Target.UEThirdPartySourceDirectory, "Flurry", "IOS"))) || +// (System.IO.Directory.Exists(System.IO.Path.Combine(Target.UEThirdPartySourceDirectory, "NotForLicensees")) && +// System.IO.Directory.Exists(System.IO.Path.Combine(Target.UEThirdPartySourceDirectory, "NotForLicensees", "Flurry")) && +// System.IO.Directory.Exists(System.IO.Path.Combine(Target.UEThirdPartySourceDirectory, "NotForLicensees", "Flurry", "IOS"))); if (bHasFlurrySDK) { PublicIncludePaths.Add(Target.UEThirdPartySourceDirectory + "NotForLicensees/Flurry/IOS/"); diff --git a/Engine/Plugins/Runtime/AnimationBudgetAllocator/Source/AnimationBudgetAllocator/Private/AnimationBudgetAllocator.cpp b/Engine/Plugins/Runtime/AnimationBudgetAllocator/Source/AnimationBudgetAllocator/Private/AnimationBudgetAllocator.cpp index 0d68b521b961..b3c68175b198 100644 --- a/Engine/Plugins/Runtime/AnimationBudgetAllocator/Source/AnimationBudgetAllocator/Private/AnimationBudgetAllocator.cpp +++ b/Engine/Plugins/Runtime/AnimationBudgetAllocator/Source/AnimationBudgetAllocator/Private/AnimationBudgetAllocator.cpp @@ -368,6 +368,7 @@ int32 FAnimationBudgetAllocator::CalculateWorkDistributionAndQueue(float InDelta InComponentData.Component->EnableExternalUpdate(bTickThisFrame); InComponentData.Component->EnableExternalEvaluationRateLimiting(InComponentData.TickRate > 1); InComponentData.Component->SetExternalDeltaTime(InComponentData.AccumulatedDeltaTime); + InComponentData.Component->SetExternalTickRate(InComponentData.TickRate); InComponentData.AccumulatedDeltaTime = bTickThisFrame ? 0.0f : InComponentData.AccumulatedDeltaTime; @@ -394,224 +395,254 @@ int32 FAnimationBudgetAllocator::CalculateWorkDistributionAndQueue(float InDelta } }; - const int32 TotalIdealWorkUnits = AllSortedComponentData.Num(); - - SET_DWORD_STAT(STAT_AnimationBudgetAllocator_Demand, TotalIdealWorkUnits); - - if(TotalIdealWorkUnits > 0) +#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) + if(GAnimationBudgetDebugForce) { - // Calc smoothed average of last frames' work units - const float AverageTickTimeMs = TotalEstimatedTickTimeMs / NumWorkUnitsForAverage; - AverageWorkUnitTimeMs = FMath::FInterpTo(AverageWorkUnitTimeMs, AverageTickTimeMs, InDeltaSeconds, Parameters.WorkUnitSmoothingSpeed); - - SET_FLOAT_STAT(STAT_AnimationBudgetAllocator_AverageWorkUnitTime, AverageWorkUnitTimeMs); - CSV_CUSTOM_STAT(AnimationBudget, AverageWorkUnitTimeMs, AverageTickTimeMs, ECsvCustomStatOp::Set); - - // Want to map the remaining (non-fixed) work units so that we only execute N work units per frame. - // If we can go over budget to keep quality then we use that value - const float WorkUnitBudget = FMath::Max(Parameters.BudgetInMs / AverageWorkUnitTimeMs, (float)TotalIdealWorkUnits * Parameters.MinQuality); - - SET_FLOAT_STAT(STAT_AnimationBudgetAllocator_Budget, WorkUnitBudget); - - // Ramp-off work units that we tick every frame once required ticks start exceeding budget - const float WorkUnitsExcess = FMath::Max(0.0f, TotalIdealWorkUnits - WorkUnitBudget); - const float WorkUnitsToRunInFull = FMath::Clamp(WorkUnitBudget - (WorkUnitsExcess * Parameters.AlwaysTickFalloffAggression), (float)NumComponentsToNotSkip, (float)TotalIdealWorkUnits); - SET_DWORD_STAT(STAT_AnimationBudgetAllocator_AlwaysTick, WorkUnitsToRunInFull); - BUDGET_CSV_STAT(AnimationBudget, NumAlwaysTicked, WorkUnitsToRunInFull, ECsvCustomStatOp::Set); - const int32 FullIndexEnd = (int32)WorkUnitsToRunInFull; - - // Account for the actual time that we think the fixed ticks will take - // This works better when budget to work unit ratio is low - float FullTickTime = 0.0f; - int32 SortedComponentIndex; - for (SortedComponentIndex = 0; SortedComponentIndex < FullIndexEnd; ++SortedComponentIndex) + for(int32 SortedComponentIndex : AllSortedComponentData) { - FComponentData& ComponentData = AllComponentData[AllSortedComponentData[SortedComponentIndex]]; - FullTickTime += ComponentData.GameThreadLastCompletionTimeMs + ComponentData.GameThreadLastTickTimeMs; - } - - float FullTickWorkUnits = FMath::Min(FullTickTime / AverageWorkUnitTimeMs, WorkUnitsToRunInFull); - - float RemainingBudget = FMath::Max(0.0f, WorkUnitBudget - FullTickWorkUnits); - float RemainingWorkUnitsToRun = FMath::Max(0.0f, TotalIdealWorkUnits - FullTickWorkUnits); - - // Ramp off interpolated units in a similar way - const float WorkUnitsToInterpolate = FMath::Min(FMath::Max(RemainingBudget - (WorkUnitsExcess * Parameters.InterpolationFalloffAggression), (float)FMath::Min(Parameters.MaxInterpolatedComponents, NumComponentsToNotThrottle)), RemainingWorkUnitsToRun); - SET_DWORD_STAT(STAT_AnimationBudgetAllocator_Interpolated, WorkUnitsToInterpolate); - - const int32 InterpolationIndexEnd = FMath::Min((int32)WorkUnitsToInterpolate + (int32)WorkUnitsToRunInFull, TotalIdealWorkUnits); - - const float MaxInterpolationRate = (float)Parameters.InterpolationMaxRate; - - // Calc remaining (throttled) work units - RemainingBudget = FMath::Max(0.0f, RemainingBudget - (WorkUnitsToInterpolate * Parameters.InterpolationTickMultiplier)); - RemainingWorkUnitsToRun = FMath::Max(0.0f, RemainingWorkUnitsToRun - (WorkUnitsToInterpolate * Parameters.InterpolationTickMultiplier)); - - SET_DWORD_STAT(STAT_AnimationBudgetAllocator_Throttled, RemainingWorkUnitsToRun); - - // Midpoint of throttle gradient is RemainingWorkUnitsToRun / RemainingBudget. - // If we distributed this as a constant we would get each component ticked - // at the same rate. However we want to tick more significant meshes more often, - // so we keep the area under the curve constant and intercept the line with this centroid. - // Care must be taken with rounding to keep workload in-budget. - const float ThrottleRateDenominator = RemainingBudget > 1.0f ? RemainingBudget : 1.0f; - const float MaxThrottleRate = FMath::Min(FMath::CeilToFloat(FMath::Max(1.0f, RemainingWorkUnitsToRun / ThrottleRateDenominator) * 2.0f), (float)Parameters.MaxTickRate); - const float ThrottleDenominator = RemainingWorkUnitsToRun > 0.0f ? RemainingWorkUnitsToRun : 1.0f; - - // Bucket 1: always ticked - for (SortedComponentIndex = 0; SortedComponentIndex < FullIndexEnd; ++SortedComponentIndex) - { - FComponentData& ComponentData = AllComponentData[AllSortedComponentData[SortedComponentIndex]]; - - // not skipping frames here as we can either match demand or these components need a full update - ComponentData.TickRate = 1; - ComponentData.DesiredTickRate = 1; - ComponentData.bInterpolate = false; - } - - // Bucket 2: interpolated - int32 NumInterpolated = 0; - for (SortedComponentIndex = FullIndexEnd; SortedComponentIndex < InterpolationIndexEnd; ++SortedComponentIndex) - { - FComponentData& ComponentData = AllComponentData[AllSortedComponentData[SortedComponentIndex]]; - - const float Alpha = (((float)SortedComponentIndex - FullIndexEnd) / WorkUnitsToInterpolate); - ComponentData.DesiredTickRate = FMath::Min((int32)FMath::FloorToFloat(FMath::Lerp(2.0f, MaxInterpolationRate, Alpha) + 0.5f), 255); - ComponentData.bInterpolate = true; - NumInterpolated++; - } - - // Bucket 3: Rate limited - int32 NumThrottled = 0; - for (SortedComponentIndex = InterpolationIndexEnd; SortedComponentIndex < TotalIdealWorkUnits; ++SortedComponentIndex) - { - FComponentData& ComponentData = AllComponentData[AllSortedComponentData[SortedComponentIndex]]; - - const float Alpha = (((float)SortedComponentIndex - InterpolationIndexEnd) / ThrottleDenominator); - ComponentData.DesiredTickRate = FMath::Min((int32)FMath::FloorToFloat(FMath::Lerp(2.0f, MaxThrottleRate, Alpha) + 0.5f), 255); - ComponentData.bInterpolate = false; - NumThrottled++; - } - - BUDGET_CSV_STAT(AnimationBudget, NumInterpolated, NumInterpolated, ECsvCustomStatOp::Set); - BUDGET_CSV_STAT(AnimationBudget, NumThrottled, RemainingWorkUnitsToRun, ECsvCustomStatOp::Set); - - const float BudgetPressure = (float)TotalIdealWorkUnits / WorkUnitBudget; - SmoothedBudgetPressure = FMath::FInterpTo(SmoothedBudgetPressure, BudgetPressure, InDeltaSeconds, Parameters.BudgetPressureSmoothingSpeed); - - float BudgetPressureInterpAlpha = FMath::Clamp((SmoothedBudgetPressure - Parameters.BudgetFactorBeforeAggressiveReducedWork) * 0.5f, 0.0f, 1.0f); - int32 StateChangeThrottleInFrames = (int32)FMath::Lerp(4.0f, (float)Parameters.StateChangeThrottleInFrames, BudgetPressureInterpAlpha); - - SET_FLOAT_STAT(STAT_AnimationBudgetAllocator_SmoothedBudgetPressure, SmoothedBudgetPressure); - - // Queue for tick - for (SortedComponentIndex = 0; SortedComponentIndex < TotalIdealWorkUnits; ++SortedComponentIndex) - { - FComponentData& ComponentData = AllComponentData[AllSortedComponentData[SortedComponentIndex]]; - - // Ensure that root prerequisite doesnt end up with a lower (or different) tick rate than dependencies - if(ComponentData.RootPrerequisite != nullptr) + FComponentData& ComponentData = AllComponentData[SortedComponentIndex]; + ComponentData.TickRate = ComponentData.DesiredTickRate = GAnimationBudgetDebugForceRate; + ComponentData.bInterpolate = !!GAnimationBudgetDebugForceInterpolation; + if(ComponentData.Component->OnReduceWork().IsBound()) { - const int32 PrerequisiteHandle = ComponentData.RootPrerequisite->GetAnimationBudgetHandle(); - if(PrerequisiteHandle != INDEX_NONE) + if(ComponentData.bReducedWork && !GAnimationBudgetDebugForceReducedWork) { - FComponentData& RootPrerequisiteComponentData = AllComponentData[PrerequisiteHandle]; - RootPrerequisiteComponentData.TickRate = ComponentData.TickRate = FMath::Min(ComponentData.TickRate, RootPrerequisiteComponentData.TickRate); - RootPrerequisiteComponentData.DesiredTickRate = ComponentData.DesiredTickRate = FMath::Min(ComponentData.DesiredTickRate, RootPrerequisiteComponentData.DesiredTickRate); - RootPrerequisiteComponentData.StateChangeThrottle = ComponentData.StateChangeThrottle = FMath::Min(ComponentData.StateChangeThrottle, RootPrerequisiteComponentData.StateChangeThrottle); + ComponentData.Component->OnReduceWork().Execute(ComponentData.Component, false); + ComponentData.bReducedWork = false; + } + else if(!ComponentData.bReducedWork && GAnimationBudgetDebugForceReducedWork) + { + ComponentData.Component->OnReduceWork().Execute(ComponentData.Component, true); + ComponentData.bReducedWork = true; } } + ComponentData.StateChangeThrottle = -1; - QueueForTick(ComponentData, StateChangeThrottleInFrames); - } - - // If any components are not longer allowed to perform reduced work, force them back out - for(int32 DisallowedReducedWorkComponentIndex : DisallowedReducedWorkComponentData) - { - FComponentData& ComponentData = AllComponentData[DisallowedReducedWorkComponentIndex]; - if(ComponentData.bReducedWork && ComponentData.Component->OnReduceWork().IsBound()) - { -#if WITH_TICK_DEBUG - UE_LOG(LogTemp, Warning, TEXT("Force-increasing component work (mesh %s) (actor %llx)"), ComponentData.Component->SkeletalMesh ? *ComponentData.Component->SkeletalMesh->GetName() : TEXT("null"), (uint64)ComponentData.Component->GetOwner()); -#endif - ComponentData.Component->OnReduceWork().Execute(ComponentData.Component, false); - ComponentData.bReducedWork = false; - } - } - - if(--ReducedComponentWorkCounter <= 0) - { - const bool bEmergencyReducedWork = SmoothedBudgetPressure >= Parameters.BudgetPressureBeforeEmergencyReducedWork; - - // Scale num components to switch based on budget pressure - const int32 NumComponentsToSwitch = (int32)FMath::Lerp(1.0f, (float)Parameters.ReducedWorkThrottleMaxPerFrame, BudgetPressureInterpAlpha); - int32 ComponentsSwitched = 0; - - // If we have any components running reduced work when we have an excess, then move them out of the 'reduced' pool per tick - if (ReducedWorkComponentData.Num() > 0 && SmoothedBudgetPressure < Parameters.BudgetFactorBeforeReducedWork - Parameters.BudgetFactorBeforeReducedWorkEpsilon) - { - for(int32 ReducedWorkComponentIndex : ReducedWorkComponentData) - { - FComponentData& ComponentData = AllComponentData[ReducedWorkComponentIndex]; - if(ComponentData.bReducedWork && ComponentData.Component->OnReduceWork().IsBound()) - { -#if WITH_TICK_DEBUG - UE_LOG(LogTemp, Warning, TEXT("Increasing component work (mesh %s) (actor %llx)"), ComponentData.Component->SkeletalMesh ? *ComponentData.Component->SkeletalMesh->GetName() : TEXT("null"), (uint64)ComponentData.Component->GetOwner()); -#endif - ComponentData.Component->OnReduceWork().Execute(ComponentData.Component, false); - ComponentData.bReducedWork = false; - - ComponentsSwitched++; - if(ComponentsSwitched >= NumComponentsToSwitch) - { - break; - } - } - } - } - else if(SmoothedBudgetPressure > Parameters.BudgetFactorBeforeReducedWork) - { - // Any work units that we interpolate or throttle should also be eligible for work reduction (which can involve disabling other ticks), so set them all now if needed - for (SortedComponentIndex = TotalIdealWorkUnits - 1; SortedComponentIndex >= FullIndexEnd; --SortedComponentIndex) - { - FComponentData& ComponentData = AllComponentData[AllSortedComponentData[SortedComponentIndex]]; - - const bool bAllowReducedWork = (ComponentData.bAllowReducedWork || bEmergencyReducedWork) && !ComponentData.bAlwaysTick; - - if(bAllowReducedWork && !ComponentData.bReducedWork && ComponentData.Component->OnReduceWork().IsBound()) - { -#if WITH_TICK_DEBUG - UE_LOG(LogTemp, Warning, TEXT("Reducing component work (mesh %s) (actor %llx)"), ComponentData.Component->SkeletalMesh ? *ComponentData.Component->SkeletalMesh->GetName() : TEXT("null"), (uint64)ComponentData.Component->GetOwner()); -#endif - ComponentData.Component->OnReduceWork().Execute(ComponentData.Component, true); - ComponentData.bReducedWork = true; - - ComponentsSwitched++; - if(ComponentsSwitched >= NumComponentsToSwitch) - { - break; - } - } - } - } - - // Scale the rate at which we consider reducing component work based on budget pressure - ReducedComponentWorkCounter = (int32)FMath::Lerp((float)Parameters.ReducedWorkThrottleMaxInFrames, (float)Parameters.ReducedWorkThrottleMinInFrames, BudgetPressureInterpAlpha); + QueueForTick(ComponentData, -1); } } + else +#endif + { + const int32 TotalIdealWorkUnits = AllSortedComponentData.Num(); + + SET_DWORD_STAT(STAT_AnimationBudgetAllocator_Demand, TotalIdealWorkUnits); + + if(TotalIdealWorkUnits > 0) + { + // Calc smoothed average of last frames' work units + const float AverageTickTimeMs = TotalEstimatedTickTimeMs / NumWorkUnitsForAverage; + AverageWorkUnitTimeMs = FMath::FInterpTo(AverageWorkUnitTimeMs, AverageTickTimeMs, InDeltaSeconds, Parameters.WorkUnitSmoothingSpeed); + + SET_FLOAT_STAT(STAT_AnimationBudgetAllocator_AverageWorkUnitTime, AverageWorkUnitTimeMs); + CSV_CUSTOM_STAT(AnimationBudget, AverageWorkUnitTimeMs, AverageTickTimeMs, ECsvCustomStatOp::Set); + + // Want to map the remaining (non-fixed) work units so that we only execute N work units per frame. + // If we can go over budget to keep quality then we use that value + const float WorkUnitBudget = FMath::Max(Parameters.BudgetInMs / AverageWorkUnitTimeMs, (float)TotalIdealWorkUnits * Parameters.MinQuality); + + SET_FLOAT_STAT(STAT_AnimationBudgetAllocator_Budget, WorkUnitBudget); + + // Ramp-off work units that we tick every frame once required ticks start exceeding budget + const float WorkUnitsExcess = FMath::Max(0.0f, TotalIdealWorkUnits - WorkUnitBudget); + const float WorkUnitsToRunInFull = FMath::Clamp(WorkUnitBudget - (WorkUnitsExcess * Parameters.AlwaysTickFalloffAggression), (float)NumComponentsToNotSkip, (float)TotalIdealWorkUnits); + SET_DWORD_STAT(STAT_AnimationBudgetAllocator_AlwaysTick, WorkUnitsToRunInFull); + BUDGET_CSV_STAT(AnimationBudget, NumAlwaysTicked, WorkUnitsToRunInFull, ECsvCustomStatOp::Set); + const int32 FullIndexEnd = (int32)WorkUnitsToRunInFull; + + // Account for the actual time that we think the fixed ticks will take + // This works better when budget to work unit ratio is low + float FullTickTime = 0.0f; + int32 SortedComponentIndex; + for (SortedComponentIndex = 0; SortedComponentIndex < FullIndexEnd; ++SortedComponentIndex) + { + FComponentData& ComponentData = AllComponentData[AllSortedComponentData[SortedComponentIndex]]; + FullTickTime += ComponentData.GameThreadLastCompletionTimeMs + ComponentData.GameThreadLastTickTimeMs; + } + + float FullTickWorkUnits = FMath::Min(FullTickTime / AverageWorkUnitTimeMs, WorkUnitsToRunInFull); + + float RemainingBudget = FMath::Max(0.0f, WorkUnitBudget - FullTickWorkUnits); + float RemainingWorkUnitsToRun = FMath::Max(0.0f, TotalIdealWorkUnits - FullTickWorkUnits); + + // Ramp off interpolated units in a similar way + const float WorkUnitsToInterpolate = FMath::Min(FMath::Max(RemainingBudget - (WorkUnitsExcess * Parameters.InterpolationFalloffAggression), (float)FMath::Min(Parameters.MaxInterpolatedComponents, NumComponentsToNotThrottle)), RemainingWorkUnitsToRun); + SET_DWORD_STAT(STAT_AnimationBudgetAllocator_Interpolated, WorkUnitsToInterpolate); + + const int32 InterpolationIndexEnd = FMath::Min((int32)WorkUnitsToInterpolate + (int32)WorkUnitsToRunInFull, TotalIdealWorkUnits); + + const float MaxInterpolationRate = (float)Parameters.InterpolationMaxRate; + + // Calc remaining (throttled) work units + RemainingBudget = FMath::Max(0.0f, RemainingBudget - (WorkUnitsToInterpolate * Parameters.InterpolationTickMultiplier)); + RemainingWorkUnitsToRun = FMath::Max(0.0f, RemainingWorkUnitsToRun - (WorkUnitsToInterpolate * Parameters.InterpolationTickMultiplier)); + + SET_DWORD_STAT(STAT_AnimationBudgetAllocator_Throttled, RemainingWorkUnitsToRun); + + // Midpoint of throttle gradient is RemainingWorkUnitsToRun / RemainingBudget. + // If we distributed this as a constant we would get each component ticked + // at the same rate. However we want to tick more significant meshes more often, + // so we keep the area under the curve constant and intercept the line with this centroid. + // Care must be taken with rounding to keep workload in-budget. + const float ThrottleRateDenominator = RemainingBudget > 1.0f ? RemainingBudget : 1.0f; + const float MaxThrottleRate = FMath::Min(FMath::CeilToFloat(FMath::Max(1.0f, RemainingWorkUnitsToRun / ThrottleRateDenominator) * 2.0f), (float)Parameters.MaxTickRate); + const float ThrottleDenominator = RemainingWorkUnitsToRun > 0.0f ? RemainingWorkUnitsToRun : 1.0f; + + // Bucket 1: always ticked + for (SortedComponentIndex = 0; SortedComponentIndex < FullIndexEnd; ++SortedComponentIndex) + { + FComponentData& ComponentData = AllComponentData[AllSortedComponentData[SortedComponentIndex]]; + + // not skipping frames here as we can either match demand or these components need a full update + ComponentData.TickRate = 1; + ComponentData.DesiredTickRate = 1; + ComponentData.bInterpolate = false; + } + + // Bucket 2: interpolated + int32 NumInterpolated = 0; + for (SortedComponentIndex = FullIndexEnd; SortedComponentIndex < InterpolationIndexEnd; ++SortedComponentIndex) + { + FComponentData& ComponentData = AllComponentData[AllSortedComponentData[SortedComponentIndex]]; + + const float Alpha = (((float)SortedComponentIndex - FullIndexEnd) / WorkUnitsToInterpolate); + ComponentData.DesiredTickRate = FMath::Min((int32)FMath::FloorToFloat(FMath::Lerp(2.0f, MaxInterpolationRate, Alpha) + 0.5f), 255); + ComponentData.bInterpolate = true; + NumInterpolated++; + } + + // Bucket 3: Rate limited + int32 NumThrottled = 0; + for (SortedComponentIndex = InterpolationIndexEnd; SortedComponentIndex < TotalIdealWorkUnits; ++SortedComponentIndex) + { + FComponentData& ComponentData = AllComponentData[AllSortedComponentData[SortedComponentIndex]]; + + const float Alpha = (((float)SortedComponentIndex - InterpolationIndexEnd) / ThrottleDenominator); + ComponentData.DesiredTickRate = FMath::Min((int32)FMath::FloorToFloat(FMath::Lerp(2.0f, MaxThrottleRate, Alpha) + 0.5f), 255); + ComponentData.bInterpolate = false; + NumThrottled++; + } + + BUDGET_CSV_STAT(AnimationBudget, NumInterpolated, NumInterpolated, ECsvCustomStatOp::Set); + BUDGET_CSV_STAT(AnimationBudget, NumThrottled, RemainingWorkUnitsToRun, ECsvCustomStatOp::Set); + + const float BudgetPressure = (float)TotalIdealWorkUnits / WorkUnitBudget; + SmoothedBudgetPressure = FMath::FInterpTo(SmoothedBudgetPressure, BudgetPressure, InDeltaSeconds, Parameters.BudgetPressureSmoothingSpeed); + + float BudgetPressureInterpAlpha = FMath::Clamp((SmoothedBudgetPressure - Parameters.BudgetFactorBeforeAggressiveReducedWork) * 0.5f, 0.0f, 1.0f); + int32 StateChangeThrottleInFrames = (int32)FMath::Lerp(4.0f, (float)Parameters.StateChangeThrottleInFrames, BudgetPressureInterpAlpha); + + SET_FLOAT_STAT(STAT_AnimationBudgetAllocator_SmoothedBudgetPressure, SmoothedBudgetPressure); + + // Queue for tick + for (SortedComponentIndex = 0; SortedComponentIndex < TotalIdealWorkUnits; ++SortedComponentIndex) + { + FComponentData& ComponentData = AllComponentData[AllSortedComponentData[SortedComponentIndex]]; + + // Ensure that root prerequisite doesnt end up with a lower (or different) tick rate than dependencies + if(ComponentData.RootPrerequisite != nullptr) + { + const int32 PrerequisiteHandle = ComponentData.RootPrerequisite->GetAnimationBudgetHandle(); + if(PrerequisiteHandle != INDEX_NONE) + { + FComponentData& RootPrerequisiteComponentData = AllComponentData[PrerequisiteHandle]; + RootPrerequisiteComponentData.TickRate = ComponentData.TickRate = FMath::Min(ComponentData.TickRate, RootPrerequisiteComponentData.TickRate); + RootPrerequisiteComponentData.DesiredTickRate = ComponentData.DesiredTickRate = FMath::Min(ComponentData.DesiredTickRate, RootPrerequisiteComponentData.DesiredTickRate); + RootPrerequisiteComponentData.StateChangeThrottle = ComponentData.StateChangeThrottle = FMath::Min(ComponentData.StateChangeThrottle, RootPrerequisiteComponentData.StateChangeThrottle); + } + } + + QueueForTick(ComponentData, StateChangeThrottleInFrames); + } + + // If any components are not longer allowed to perform reduced work, force them back out + for(int32 DisallowedReducedWorkComponentIndex : DisallowedReducedWorkComponentData) + { + FComponentData& ComponentData = AllComponentData[DisallowedReducedWorkComponentIndex]; + if(ComponentData.bReducedWork && ComponentData.Component->OnReduceWork().IsBound()) + { +#if WITH_TICK_DEBUG + UE_LOG(LogTemp, Warning, TEXT("Force-increasing component work (mesh %s) (actor %llx)"), ComponentData.Component->SkeletalMesh ? *ComponentData.Component->SkeletalMesh->GetName() : TEXT("null"), (uint64)ComponentData.Component->GetOwner()); +#endif + ComponentData.Component->OnReduceWork().Execute(ComponentData.Component, false); + ComponentData.bReducedWork = false; + } + } + + if(--ReducedComponentWorkCounter <= 0) + { + const bool bEmergencyReducedWork = SmoothedBudgetPressure >= Parameters.BudgetPressureBeforeEmergencyReducedWork; + + // Scale num components to switch based on budget pressure + const int32 NumComponentsToSwitch = (int32)FMath::Lerp(1.0f, (float)Parameters.ReducedWorkThrottleMaxPerFrame, BudgetPressureInterpAlpha); + int32 ComponentsSwitched = 0; + + // If we have any components running reduced work when we have an excess, then move them out of the 'reduced' pool per tick + if (ReducedWorkComponentData.Num() > 0 && SmoothedBudgetPressure < Parameters.BudgetFactorBeforeReducedWork - Parameters.BudgetFactorBeforeReducedWorkEpsilon) + { + for(int32 ReducedWorkComponentIndex : ReducedWorkComponentData) + { + FComponentData& ComponentData = AllComponentData[ReducedWorkComponentIndex]; + if(ComponentData.bReducedWork && ComponentData.Component->OnReduceWork().IsBound()) + { +#if WITH_TICK_DEBUG + UE_LOG(LogTemp, Warning, TEXT("Increasing component work (mesh %s) (actor %llx)"), ComponentData.Component->SkeletalMesh ? *ComponentData.Component->SkeletalMesh->GetName() : TEXT("null"), (uint64)ComponentData.Component->GetOwner()); +#endif + ComponentData.Component->OnReduceWork().Execute(ComponentData.Component, false); + ComponentData.bReducedWork = false; + + ComponentsSwitched++; + if(ComponentsSwitched >= NumComponentsToSwitch) + { + break; + } + } + } + } + else if(SmoothedBudgetPressure > Parameters.BudgetFactorBeforeReducedWork) + { + // Any work units that we interpolate or throttle should also be eligible for work reduction (which can involve disabling other ticks), so set them all now if needed + for (SortedComponentIndex = TotalIdealWorkUnits - 1; SortedComponentIndex >= FullIndexEnd; --SortedComponentIndex) + { + FComponentData& ComponentData = AllComponentData[AllSortedComponentData[SortedComponentIndex]]; + + const bool bAllowReducedWork = (ComponentData.bAllowReducedWork || bEmergencyReducedWork) && !ComponentData.bAlwaysTick; + + if(bAllowReducedWork && !ComponentData.bReducedWork && ComponentData.Component->OnReduceWork().IsBound()) + { +#if WITH_TICK_DEBUG + UE_LOG(LogTemp, Warning, TEXT("Reducing component work (mesh %s) (actor %llx)"), ComponentData.Component->SkeletalMesh ? *ComponentData.Component->SkeletalMesh->GetName() : TEXT("null"), (uint64)ComponentData.Component->GetOwner()); +#endif + ComponentData.Component->OnReduceWork().Execute(ComponentData.Component, true); + ComponentData.bReducedWork = true; + + ComponentsSwitched++; + if(ComponentsSwitched >= NumComponentsToSwitch) + { + break; + } + } + } + } + + // Scale the rate at which we consider reducing component work based on budget pressure + ReducedComponentWorkCounter = (int32)FMath::Lerp((float)Parameters.ReducedWorkThrottleMaxInFrames, (float)Parameters.ReducedWorkThrottleMinInFrames, BudgetPressureInterpAlpha); + } + } #if CSV_PROFILER - if(AllSortedComponentData.Num() > 0) - { - for (int32 ComponentDataIndex : AllSortedComponentData) + if(AllSortedComponentData.Num() > 0) { - FComponentData& ComponentData = AllComponentData[ComponentDataIndex]; - OutAverageTickRate += (float)ComponentData.TickRate; - } + for (int32 ComponentDataIndex : AllSortedComponentData) + { + FComponentData& ComponentData = AllComponentData[ComponentDataIndex]; + OutAverageTickRate += (float)ComponentData.TickRate; + } - OutAverageTickRate /= (float)AllSortedComponentData.Num(); - } + OutAverageTickRate /= (float)AllSortedComponentData.Num(); + } #endif + } return NumTicked; } diff --git a/Engine/Plugins/Runtime/AnimationBudgetAllocator/Source/AnimationBudgetAllocator/Private/AnimationBudgetAllocatorCVars.cpp b/Engine/Plugins/Runtime/AnimationBudgetAllocator/Source/AnimationBudgetAllocator/Private/AnimationBudgetAllocatorCVars.cpp index 5043c895bc54..1aaca8d52e31 100644 --- a/Engine/Plugins/Runtime/AnimationBudgetAllocator/Source/AnimationBudgetAllocator/Private/AnimationBudgetAllocatorCVars.cpp +++ b/Engine/Plugins/Runtime/AnimationBudgetAllocator/Source/AnimationBudgetAllocator/Private/AnimationBudgetAllocatorCVars.cpp @@ -33,6 +33,54 @@ static FAutoConsoleVariableRef CVarSkelBatch_ShowAddresses( ECVF_Scalability); #endif +#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) + +int32 GAnimationBudgetDebugForce = 0; + +static FAutoConsoleVariableRef CVarSkelBatch_Force( + TEXT("a.Budget.Debug.Force"), + GAnimationBudgetDebugForce, + TEXT("Values: 0/1\n") + TEXT("Default: 0\n") + TEXT("Turns on forced rate/interp/reduced controls. These override any budget-driven values."), + ECVF_Scalability); + +int32 GAnimationBudgetDebugForceRate = 4; + +static FAutoConsoleVariableRef CVarSkelBatch_ForceRate( + TEXT("a.Budget.Debug.Force.Rate"), + GAnimationBudgetDebugForceRate, + TEXT("Values: >= 1\n") + TEXT("Default: 4\n") + TEXT("Forces all components to update at the specifed rate when a.Budget.Debug.Force is enabled."), + FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* InVariable) + { + GAnimationBudgetDebugForceRate = FMath::Max(GAnimationBudgetDebugForceRate, 1); + }), + ECVF_Scalability); + +int32 GAnimationBudgetDebugForceInterpolation = 0; + +static FAutoConsoleVariableRef CVarSkelBatch_ForceInterpolation( + TEXT("a.Budget.Debug.Force.Interp"), + GAnimationBudgetDebugForceInterpolation, + TEXT("Values: 0/1\n") + TEXT("Default: 0\n") + TEXT("Forces interpolation on when a.Budget.Debug.Force is enabled."), + ECVF_Scalability); + +int32 GAnimationBudgetDebugForceReducedWork = 0; + +static FAutoConsoleVariableRef CVarSkelBatch_ForceReducedWork( + TEXT("a.Budget.Debug.Force.Reduced"), + GAnimationBudgetDebugForceReducedWork, + TEXT("Values: 0/1\n") + TEXT("Default: 0\n") + TEXT("Forces reduced work on when a.Budget.Debug.Force is enabled."), + ECVF_Scalability); + +#endif + /** CVar-driven parameter block */ FAnimationBudgetAllocatorParameters GBudgetParameters; diff --git a/Engine/Plugins/Runtime/AnimationBudgetAllocator/Source/AnimationBudgetAllocator/Private/AnimationBudgetAllocatorCVars.h b/Engine/Plugins/Runtime/AnimationBudgetAllocator/Source/AnimationBudgetAllocator/Private/AnimationBudgetAllocatorCVars.h index 4b12e76811d6..a4b742157cb7 100644 --- a/Engine/Plugins/Runtime/AnimationBudgetAllocator/Source/AnimationBudgetAllocator/Private/AnimationBudgetAllocatorCVars.h +++ b/Engine/Plugins/Runtime/AnimationBudgetAllocator/Source/AnimationBudgetAllocator/Private/AnimationBudgetAllocatorCVars.h @@ -18,6 +18,16 @@ extern int32 GAnimationBudgetDebugEnabled; extern int32 GAnimationBudgetDebugShowAddresses; #endif +#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) + +/** Parameters used to force state for debugging */ +extern int32 GAnimationBudgetDebugForce; +extern int32 GAnimationBudgetDebugForceRate; +extern int32 GAnimationBudgetDebugForceInterpolation; +extern int32 GAnimationBudgetDebugForceReducedWork; + +#endif + /** CVar-driven parameter block */ extern FAnimationBudgetAllocatorParameters GBudgetParameters; diff --git a/Engine/Plugins/Runtime/AnimationBudgetAllocator/Source/AnimationBudgetAllocator/Public/AnimationBudgetAllocatorParameters.h b/Engine/Plugins/Runtime/AnimationBudgetAllocator/Source/AnimationBudgetAllocator/Public/AnimationBudgetAllocatorParameters.h index f63f737f29ab..1bf986ff6b7b 100644 --- a/Engine/Plugins/Runtime/AnimationBudgetAllocator/Source/AnimationBudgetAllocator/Public/AnimationBudgetAllocatorParameters.h +++ b/Engine/Plugins/Runtime/AnimationBudgetAllocator/Source/AnimationBudgetAllocator/Public/AnimationBudgetAllocatorParameters.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "AnimationBudgetAllocatorParameters.generated.h" diff --git a/Engine/Plugins/Runtime/AppleMoviePlayer/Source/AppleMoviePlayer/Private/AppleMovieStreamer.cpp b/Engine/Plugins/Runtime/AppleMoviePlayer/Source/AppleMoviePlayer/Private/AppleMovieStreamer.cpp index ebd602e7f07d..3a3f816bf3c1 100644 --- a/Engine/Plugins/Runtime/AppleMoviePlayer/Source/AppleMoviePlayer/Private/AppleMovieStreamer.cpp +++ b/Engine/Plugins/Runtime/AppleMoviePlayer/Source/AppleMoviePlayer/Private/AppleMovieStreamer.cpp @@ -92,7 +92,6 @@ ResumeTime ( kCMTimeZero ) { UE_LOG(LogMoviePlayer, Log, TEXT("FAVMoviePlayer ctor...")); - TextureData = MakeShareable(new FSlateTextureData()); MovieViewport = MakeShareable(new FMovieViewport()); } @@ -150,6 +149,7 @@ bool FAVPlayerMovieStreamer::Tick(float DeltaTime) { return false; } + // Check the list of textures pending deletion and remove any that are no longer valid for (int32 TextureIndex = 0; TextureIndex < TexturesPendingDeletion.Num(); ) { @@ -173,14 +173,7 @@ bool FAVPlayerMovieStreamer::Tick(float DeltaTime) // Remember that we were active. Used to edge detect active/not-active transitions bWasActive = true; - if( CheckForNextFrameAndCopy() ) - { - // Copy new frame data. - uint32 Stride; - uint8* DestTextureData = (uint8*)RHILockTexture2D( Texture->GetTypedResource(), 0, RLM_WriteOnly, Stride, false ); - FMemory::Memcpy( DestTextureData, TextureData->GetRawBytesPtr(), TextureData->GetRawBytes().Num() ); - RHIUnlockTexture2D( Texture->GetTypedResource(), 0, false ); - } + CheckForNextFrameAndCopy(); check(AVReader != nil); AVAssetReaderStatus Status = [AVReader status]; @@ -325,14 +318,15 @@ bool FAVPlayerMovieStreamer::StartNextMovie() MovieName = MovieQueue[0]; MovieQueue.RemoveAt(0); - bVideoTracksLoaded = LoadMovie(MovieName); - + return LoadMovieAsync(MovieName); } - return bVideoTracksLoaded; + return false; } -bool FAVPlayerMovieStreamer::LoadMovie(FString InMovieName) +bool FAVPlayerMovieStreamer::LoadMovieAsync(FString InMovieName) { + FScopeLock LockVideoTracksLoading(&VideoTracksLoadingLock); + // Reset flag to indicate the movie may have started, but isn't playing yet. bVideoTracksLoaded = false; NSURL* nsURL = nil; @@ -341,6 +335,7 @@ bool FAVPlayerMovieStreamer::LoadMovie(FString InMovieName) { nsURL = [NSURL fileURLWithPath : ConvertToNativePath(MoviePath, false).GetNSString()]; } + if (nsURL == nil) { return false; @@ -349,41 +344,21 @@ bool FAVPlayerMovieStreamer::LoadMovie(FString InMovieName) // Load the Movie with the appropriate URL. AVMovie = [[AVURLAsset alloc] initWithURL:nsURL options:nil]; - __block bool bLoadCompleted = false; - // Obtain the tracks asynchronously. NSArray* nsTrackKeys = @[@"tracks"]; [AVMovie loadValuesAsynchronouslyForKeys:nsTrackKeys completionHandler:^() { - // !!! This block will execute asynchronously !!! - // Once loaded, initialize our reader object to start pulling frames. - bVideoTracksLoaded = FinishLoadingTracks(); + FScopeLock AynscLockVideoTracksLoading(&VideoTracksLoadingLock); - // Play the next movie in the queue + // Once loaded, initialize our reader object to start pulling frames + bVideoTracksLoaded = FinishLoadingTracks(); #if PLATFORM_IOS bIsMovieInterrupted = GIsSuspended; #endif - - if (!bIsMovieInterrupted && bVideoTracksLoaded && AudioPlayer != nil) - { - // Good time to start the audio playing. - [AudioPlayer play]; - } - - // !!! - bLoadCompleted = true; }]; - - while (!bLoadCompleted) - { - FPlatformProcess::Sleep(0); - } - - // Movie has started. - - UE_LOG(LogMoviePlayer, Log, TEXT("Started next movie.") ); - return bVideoTracksLoaded; + + return true; } bool FAVPlayerMovieStreamer::FinishLoadingTracks() @@ -443,8 +418,8 @@ bool FAVPlayerMovieStreamer::FinishLoadingTracks() check( AVVideoTrack.nominalFrameRate ); VideoRate = 1.0f / AVVideoTrack.nominalFrameRate; - // Save the starting time. - StartTime = CACurrentMediaTime() - CMTimeGetSeconds(ResumeTime); + // Reset the starting time. + StartTime = 0.0; // Good to go. bLoadedAndReading = true; @@ -475,14 +450,23 @@ bool FAVPlayerMovieStreamer::CheckForNextFrameAndCopy() bool bHasNewFrame = false; // We need to synchronize the video playback with the audio. - // If the video frame is within tolerance (Ready), update the Texture Data. + // If the video frame is within tolerance (Ready), update the Texture. // If the video is Behind, throw it away and get the next one until we catch up with the ref time. - // If the video is Ahead, update the TextureData but don't retrieve more frames until time catches up. + // If the video is Ahead, update the Texture but don't retrieve more frames until time catches up. - + if(StartTime == 0.0) + { + // Now kick everything going at the same time + StartTime = CACurrentMediaTime() - CMTimeGetSeconds(ResumeTime); + + if(AudioPlayer != nil && !AudioPlayer.isPlaying) + { + [AudioPlayer play]; + } + } + while( SyncStatus != Ready ) { - double Delta; if( SyncStatus != Ahead ) { LatestSamples = [AVVideoOutput copyNextSampleBuffer]; @@ -498,17 +482,16 @@ bool FAVPlayerMovieStreamer::CheckForNextFrameAndCopy() // Get the time since playback began Cursor = CACurrentMediaTime() - StartTime; - CMTime caCurrentTime = CMTimeMake(Cursor * TIMESCALE, TIMESCALE); // Compute delta of video frame and current playback times - Delta = CMTimeGetSeconds(caCurrentTime) - CMTimeGetSeconds(FrameTimeStamp); + double Delta = Cursor - CMTimeGetSeconds(FrameTimeStamp); - if( Delta < 0 ) + if( Delta < 0.0 ) { - Delta *= -1; + Delta *= - 1.0; SyncStatus = Ahead; } - else + else { SyncStatus = Behind; } @@ -523,7 +506,7 @@ bool FAVPlayerMovieStreamer::CheckForNextFrameAndCopy() // Video ahead of audio: stay in Ahead state, exit loop break; } - else + else { // Video behind audio (Behind): stay in loop CFRelease(LatestSamples); @@ -547,17 +530,6 @@ bool FAVPlayerMovieStreamer::CheckForNextFrameAndCopy() uint32 SrcWidth = (uint32)Size.width; uint32 SrcHeight = (uint32)Size.height; - // Now that we have video information, ensure that we have texture data in the right dimensions - if (TextureData->GetWidth() != SrcWidth || TextureData->GetHeight() != SrcHeight) - { - check( SrcWidth > 0 && SrcHeight > 0 ); - - TArray TempData; - TempData.AddZeroed(SrcWidth * SrcHeight * 4); - TextureData->SetRawData(SrcWidth, SrcHeight, SrcWidth * 4, TempData); - check( TextureData->GetRawBytesPtr() != NULL ); - } - // Now that we have video information, check on texture allocation. If we don't have a texture yet, create one. if(!Texture.IsValid() || (Texture->GetWidth() != SrcWidth || Texture->GetHeight() != SrcHeight)) { @@ -580,13 +552,13 @@ bool FAVPlayerMovieStreamer::CheckForNextFrameAndCopy() Texture->UpdateRHI(); MovieViewport->SetTexture(Texture); } - - check( TextureData->GetBytesPerPixel() > 0 ); - - // Copy the video data - uint32 Len = TextureData->GetBytesPerPixel() * SrcHeight; - check( Len > 0 ); - FMemory::Memcpy(TextureData->GetRawBytesPtr(), pVideoData, Len); + + uint32 DataLen = SrcWidth * 4 * SrcHeight; + uint32 Stride; + + uint8* DestTextureData = (uint8*)RHILockTexture2D( Texture->GetTypedResource(), 0, RLM_WriteOnly, Stride, false ); + FMemory::Memcpy( DestTextureData, pVideoData, DataLen ); + RHIUnlockTexture2D( Texture->GetTypedResource(), 0, false ); // Re-lock and release the video data. CVPixelBufferUnlockBaseAddress( pPixelBuffer, kCVPixelBufferLock_ReadOnly ); @@ -687,8 +659,6 @@ void FAVPlayerMovieStreamer::Resume() //already resumed return; } - FScopeLock LockVideoTracksLoading(&VideoTracksLoadingLock); - - LoadMovie(MovieName); + + LoadMovieAsync(MovieName); } - diff --git a/Engine/Plugins/Runtime/AppleMoviePlayer/Source/AppleMoviePlayer/Private/AppleMovieStreamer.h b/Engine/Plugins/Runtime/AppleMoviePlayer/Source/AppleMoviePlayer/Private/AppleMovieStreamer.h index cc53c19017bd..e2587e5d035b 100644 --- a/Engine/Plugins/Runtime/AppleMoviePlayer/Source/AppleMoviePlayer/Private/AppleMovieStreamer.h +++ b/Engine/Plugins/Runtime/AppleMoviePlayer/Source/AppleMoviePlayer/Private/AppleMovieStreamer.h @@ -49,7 +49,6 @@ private: /** Texture and viewport data for displaying to Slate */ TSharedPtr MovieViewport; - TSharedPtr TextureData; TSharedPtr Texture; // The list of pending movies @@ -94,5 +93,5 @@ private: void ReleaseMovie(); - bool LoadMovie(FString MovieName); + bool LoadMovieAsync(FString MovieName); }; diff --git a/Engine/Plugins/Runtime/Firebase/Source/Firebase.upl.xml b/Engine/Plugins/Runtime/Firebase/Source/Firebase.upl.xml index 0c0cc3ed0e38..c279cc311774 100644 --- a/Engine/Plugins/Runtime/Firebase/Source/Firebase.upl.xml +++ b/Engine/Plugins/Runtime/Firebase/Source/Firebase.upl.xml @@ -67,7 +67,11 @@ apply plugin: 'com.google.gms.google-services' - + + + + + diff --git a/Engine/Plugins/Runtime/Firebase/Source/Java/notifications/EpicFirebaseMessagingService.java b/Engine/Plugins/Runtime/Firebase/Source/Java/notifications/EpicFirebaseMessagingService.java index 3c3f189bc314..d9a3d21dd943 100644 --- a/Engine/Plugins/Runtime/Firebase/Source/Java/notifications/EpicFirebaseMessagingService.java +++ b/Engine/Plugins/Runtime/Firebase/Source/Java/notifications/EpicFirebaseMessagingService.java @@ -12,7 +12,6 @@ import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; import android.text.TextUtils; -import com.epicgames.fortnite.R; import com.epicgames.ue4.GameActivity; import com.epicgames.ue4.GameApplication; import com.epicgames.ue4.LocalNotificationReceiver; diff --git a/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/Abilities/GameplayAbility.cpp b/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/Abilities/GameplayAbility.cpp index 9722b724bb31..c2ff6f9ca3c0 100644 --- a/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/Abilities/GameplayAbility.cpp +++ b/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/Abilities/GameplayAbility.cpp @@ -562,6 +562,7 @@ bool UGameplayAbility::IsEndAbilityValid(const FGameplayAbilitySpecHandle Handle // Ending an AbilityState may cause this to be invoked again if (bIsActive == false && GetInstancingPolicy() != EGameplayAbilityInstancingPolicy::NonInstanced) { + UE_LOG(LogAbilitySystem, Verbose, TEXT("IsEndAbilityValid returning false on Ability %s due to EndAbility being called multiple times"), *GetName()); return false; } @@ -569,6 +570,7 @@ bool UGameplayAbility::IsEndAbilityValid(const FGameplayAbilitySpecHandle Handle UAbilitySystemComponent* AbilityComp = ActorInfo ? ActorInfo->AbilitySystemComponent.Get() : nullptr; if (AbilityComp == nullptr) { + UE_LOG(LogAbilitySystem, Verbose, TEXT("IsEndAbilityValid returning false on Ability %s due to AbilitySystemComponent being invalid"), *GetName()); return false; } @@ -578,6 +580,7 @@ bool UGameplayAbility::IsEndAbilityValid(const FGameplayAbilitySpecHandle Handle if (!bIsSpecActive) { + UE_LOG(LogAbilitySystem, Verbose, TEXT("IsEndAbilityValid returning false on Ability %s due spec not being active"), *GetName()); return false; } @@ -590,6 +593,7 @@ void UGameplayAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, const { if (ScopeLockCount > 0) { + UE_LOG(LogAbilitySystem, Verbose, TEXT("Attempting to end Ability %s but ScopeLockCount was greater than 0, adding end to the WaitingToExecute Array"), *GetName()); WaitingToExecute.Add(FPostLockDelegate::CreateUObject(this, &UGameplayAbility::EndAbility, Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled)); return; } diff --git a/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/AbilitySystemComponent_Abilities.cpp b/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/AbilitySystemComponent_Abilities.cpp index 3b668c38c66e..d53ee0279951 100644 --- a/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/AbilitySystemComponent_Abilities.cpp +++ b/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/AbilitySystemComponent_Abilities.cpp @@ -443,7 +443,8 @@ void UAbilitySystemComponent::OnRemoveAbility(FGameplayAbilitySpec& Spec) auto& TriggeredAbilityMap = (TriggerData.TriggerSource == EGameplayAbilityTriggerSource::GameplayEvent) ? GameplayEventTriggeredAbilities : OwnedTagTriggeredAbilities; - if (TriggeredAbilityMap.Contains(EventTag)) + if (ensureMsgf(TriggeredAbilityMap.Contains(EventTag), + TEXT("%s::%s not found in TriggeredAbilityMap while removing, TriggerSource: %d"), *Spec.Ability->GetName(), *EventTag.ToString(), (int32)TriggerData.TriggerSource)) { TriggeredAbilityMap[EventTag].Remove(Spec.Handle); if (TriggeredAbilityMap[EventTag].Num() == 0) @@ -1920,6 +1921,14 @@ int32 UAbilitySystemComponent::HandleGameplayEvent(FGameplayTag EventTag, const { TArray TriggeredAbilityHandles = GameplayEventTriggeredAbilities[CurrentTag]; + // FORT-152163 - Tracking cases where GameplayEventTriggeredAbilities has an AbilityHandle that is not in ActivatableAbilities + // We suspect that triggering one ability may be causing others in the array to be invalidated + // If we do not ensure here, but ensure in TriggerAbilityFromGameplayEvent(), then it will confirm our suspicion + for (auto AbilityHandle : TriggeredAbilityHandles) + { + ensureMsgf(FindAbilitySpecFromHandle(AbilityHandle), TEXT("Stale ability handle in GameplayEventTriggeredAbilities: %s"), *EventTag.ToString()); + } + for (auto AbilityHandle : TriggeredAbilityHandles) { if (TriggerAbilityFromGameplayEvent(AbilityHandle, AbilityActorInfo.Get(), EventTag, Payload, *this)) diff --git a/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/AttributeSet.cpp b/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/AttributeSet.cpp index 5173e909d65b..55ccbede3922 100644 --- a/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/AttributeSet.cpp +++ b/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/AttributeSet.cpp @@ -682,7 +682,7 @@ void FAttributeSetInitterDiscreteLevels::InitAttributeSetDefaults(UAbilitySystem const FAttributeSetDefaultsCollection* Collection = Defaults.Find(GroupName); if (!Collection) { - ABILITY_LOG(Warning, TEXT("Unable to find DefaultAttributeSet Group %s. Failing back to Defaults"), *GroupName.ToString()); + ABILITY_LOG(Warning, TEXT("Unable to find DefaultAttributeSet Group %s. Falling back to Defaults"), *GroupName.ToString()); Collection = Defaults.Find(FName(TEXT("Default"))); if (!Collection) { @@ -729,7 +729,7 @@ void FAttributeSetInitterDiscreteLevels::ApplyAttributeDefault(UAbilitySystemCom const FAttributeSetDefaultsCollection* Collection = Defaults.Find(GroupName); if (!Collection) { - ABILITY_LOG(Warning, TEXT("Unable to find DefaultAttributeSet Group %s. Failing back to Defaults"), *GroupName.ToString()); + ABILITY_LOG(Warning, TEXT("Unable to find DefaultAttributeSet Group %s. Falling back to Defaults"), *GroupName.ToString()); Collection = Defaults.Find(FName(TEXT("Default"))); if (!Collection) { diff --git a/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/GameplayEffect.cpp b/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/GameplayEffect.cpp index 3950526e5592..a3a71a2690b5 100644 --- a/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/GameplayEffect.cpp +++ b/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/GameplayEffect.cpp @@ -997,7 +997,7 @@ void FGameplayEffectSpec::SetLevel(float InLevel) SetDuration(DefCalcDuration, false); } - FString ContextString = FString::Printf(TEXT("FGameplayEffectSpec::SetLevel from effect %s"), *Def->GetName()); + FString ContextString = Def->GetName(); Period = Def->Period.GetValueAtLevel(InLevel, &ContextString); ChanceToApplyToTarget = Def->ChanceToApplyToTarget.GetValueAtLevel(InLevel, &ContextString); } @@ -1874,7 +1874,7 @@ FActiveGameplayEffectsContainer::FActiveGameplayEffectsContainer() , PendingGameplayEffectHead(nullptr) { PendingGameplayEffectNext = &PendingGameplayEffectHead; - bUseDeltaStructSerialization = true; + SetDeltaSerializationEnabled(true); } FActiveGameplayEffectsContainer::~FActiveGameplayEffectsContainer() diff --git a/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/GameplayEffectAggregatorLibrary.cpp b/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/GameplayEffectAggregatorLibrary.cpp index 5c216775b8f4..8d90ec9fc035 100644 --- a/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/GameplayEffectAggregatorLibrary.cpp +++ b/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Private/GameplayEffectAggregatorLibrary.cpp @@ -6,7 +6,7 @@ #include "AbilitySystemComponent.h" /** Custom functions. The idea here is that we may want to mix and match these (if FAggregatorEvaluateMetaData starts to hold more than just the qualifier functions) */ -void QualifierFunc_MostNegativeMod_AllPostiiveMods(const FAggregatorEvaluateParameters& EvalParameters, const FAggregator* Aggregator) +void QualifierFunc_MostNegativeMod_AllPositiveMods(const FAggregatorEvaluateParameters& EvalParameters, const FAggregator* Aggregator) { // We want to inhibit all qualified negative effects except for the most severe. We want to leave positive modifiers alone const FAggregatorMod* MostNegativeMod = nullptr; @@ -60,4 +60,4 @@ void QualifierFunc_MostNegativeMod_AllPostiiveMods(const FAggregatorEvaluatePara } /** static FAggregatorEvaluateMetaDatas that use the above functions */ -FAggregatorEvaluateMetaData FAggregatorEvaluateMetaDataLibrary::MostNegativeMod_AllPostiiveMods(QualifierFunc_MostNegativeMod_AllPostiiveMods); \ No newline at end of file +FAggregatorEvaluateMetaData FAggregatorEvaluateMetaDataLibrary::MostNegativeMod_AllPositiveMods(QualifierFunc_MostNegativeMod_AllPositiveMods); \ No newline at end of file diff --git a/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/Abilities/Tasks/AbilityTask_MoveToLocation.h b/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/Abilities/Tasks/AbilityTask_MoveToLocation.h index 1e1629558474..2ce8c642eed4 100644 --- a/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/Abilities/Tasks/AbilityTask_MoveToLocation.h +++ b/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/Abilities/Tasks/AbilityTask_MoveToLocation.h @@ -18,7 +18,7 @@ class UGameplayTasksComponent; /** * TODO: - * -Implement replicated time so that this can work as a simulated task for Join In Prgorss clients. + * -Implement replicated time so that this can work as a simulated task for Join In Progress clients. */ diff --git a/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/GameplayEffect.h b/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/GameplayEffect.h index ad7c9a8cebaa..00ab7731035e 100644 --- a/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/GameplayEffect.h +++ b/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/GameplayEffect.h @@ -624,7 +624,7 @@ enum class EGameplayEffectStackingExpirationPolicy : uint8 /** The current stack count will be decremented by 1 and the duration refreshed. The GE is not "reapplied", just continues to exist with one less stacks. */ RemoveSingleStackAndRefreshDuration, - /** The duration of the gameplay effect is refreshed. This essentially makes the effect infinite in duration. This can be used to manually handle stack decrements via XXX callback */ + /** The duration of the gameplay effect is refreshed. This essentially makes the effect infinite in duration. This can be used to manually handle stack decrements via OnStackCountChange callback */ RefreshDuration, }; diff --git a/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/GameplayEffectAggregatorLibrary.h b/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/GameplayEffectAggregatorLibrary.h index 95fbfa28c95d..90d414200c3a 100644 --- a/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/GameplayEffectAggregatorLibrary.h +++ b/Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/GameplayEffectAggregatorLibrary.h @@ -6,5 +6,5 @@ struct GAMEPLAYABILITIES_API FAggregatorEvaluateMetaDataLibrary { - static FAggregatorEvaluateMetaData MostNegativeMod_AllPostiiveMods; + static FAggregatorEvaluateMetaData MostNegativeMod_AllPositiveMods; }; \ No newline at end of file diff --git a/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/IOSReplayKit.Build.cs b/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/IOSReplayKit.Build.cs index ed66ac8474d4..962be2303691 100644 --- a/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/IOSReplayKit.Build.cs +++ b/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/IOSReplayKit.Build.cs @@ -1,4 +1,4 @@ -// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. using System.IO; diff --git a/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Private/IOSReplayKit.cpp b/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Private/IOSReplayKit.cpp index 92ac3ca6cf81..cdf122b2a17d 100644 --- a/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Private/IOSReplayKit.cpp +++ b/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Private/IOSReplayKit.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "IOSReplayKit.h" diff --git a/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Private/IOSReplayKitControl.cpp b/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Private/IOSReplayKitControl.cpp index 495eab9b16e9..7bb3f1f3a3a5 100644 --- a/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Private/IOSReplayKitControl.cpp +++ b/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Private/IOSReplayKitControl.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "IOSReplayKitControl.h" #include "IOSReplayKit.h" diff --git a/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Private/ReplayKitRecorder.cpp b/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Private/ReplayKitRecorder.cpp index eea7e6bbaad2..32f04e6920e4 100644 --- a/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Private/ReplayKitRecorder.cpp +++ b/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Private/ReplayKitRecorder.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "ReplayKitRecorder.h" diff --git a/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Private/ReplayKitRecorder.h b/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Private/ReplayKitRecorder.h index 81c1ab258840..4a7c77dc0189 100644 --- a/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Private/ReplayKitRecorder.h +++ b/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Private/ReplayKitRecorder.h @@ -1,4 +1,4 @@ -// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "CoreTypes.h" diff --git a/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Public/IOSReplayKit.h b/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Public/IOSReplayKit.h index d770ea9b34b8..a6c79956e336 100644 --- a/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Public/IOSReplayKit.h +++ b/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Public/IOSReplayKit.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Public/IOSReplayKitControl.h b/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Public/IOSReplayKitControl.h index 93100b9f6566..ab78b4aa16f9 100644 --- a/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Public/IOSReplayKitControl.h +++ b/Engine/Plugins/Runtime/IOSReplayKit/Source/IOSReplayKit/Public/IOSReplayKitControl.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Plugins/Runtime/Oculus/OculusVR/Source/OculusHMD/Private/OculusFunctionLibrary.cpp b/Engine/Plugins/Runtime/Oculus/OculusVR/Source/OculusHMD/Private/OculusFunctionLibrary.cpp index 50c99c19c467..cbb1bbff6a4f 100644 --- a/Engine/Plugins/Runtime/Oculus/OculusVR/Source/OculusHMD/Private/OculusFunctionLibrary.cpp +++ b/Engine/Plugins/Runtime/Oculus/OculusVR/Source/OculusHMD/Private/OculusFunctionLibrary.cpp @@ -134,8 +134,7 @@ void UOculusFunctionLibrary::SetCPUAndGPULevels(int CPULevel, int GPULevel) OculusHMD::FOculusHMD* OculusHMD = GetOculusHMD(); if (OculusHMD != nullptr && OculusHMD->IsHMDActive()) { - ovrp_SetSystemCpuLevel2(CPULevel); - ovrp_SetSystemGpuLevel2(GPULevel); + OculusHMD->SetCPUAndGPULevel(CPULevel, GPULevel); } #endif // OCULUS_HMD_SUPPORTED_PLATFORMS } diff --git a/Engine/Plugins/Runtime/Oculus/OculusVR/Source/OculusHMD/Private/OculusHMD.cpp b/Engine/Plugins/Runtime/Oculus/OculusVR/Source/OculusHMD/Private/OculusHMD.cpp index 1cfd5fb0ef38..eb4bec5548f2 100644 --- a/Engine/Plugins/Runtime/Oculus/OculusVR/Source/OculusHMD/Private/OculusHMD.cpp +++ b/Engine/Plugins/Runtime/Oculus/OculusVR/Source/OculusHMD/Private/OculusHMD.cpp @@ -2760,6 +2760,15 @@ namespace OculusHMD return PerformanceStats; } + void FOculusHMD::SetCPUAndGPULevel(int CPULevel, int GPULevel) + { + CheckInGameThread(); + Settings->CPULevel = CPULevel; + Settings->GPULevel = GPULevel; + ovrp_SetSystemCpuLevel2(Settings->CPULevel); + ovrp_SetSystemGpuLevel2(Settings->GPULevel); + } + void FOculusHMD::SetTiledMultiResLevel(ETiledMultiResLevel multiresLevel) { diff --git a/Engine/Plugins/Runtime/Oculus/OculusVR/Source/OculusHMD/Private/OculusHMD.h b/Engine/Plugins/Runtime/Oculus/OculusVR/Source/OculusHMD/Private/OculusHMD.h index 9a2144b6c6f5..53c54963c882 100644 --- a/Engine/Plugins/Runtime/Oculus/OculusVR/Source/OculusHMD/Private/OculusHMD.h +++ b/Engine/Plugins/Runtime/Oculus/OculusVR/Source/OculusHMD/Private/OculusHMD.h @@ -328,6 +328,7 @@ public: void StartRHIFrame_RenderThread(); // Called from PreRenderViewFamily_RenderThread void FinishRHIFrame_RHIThread(); // Called from FinishRendering_RHIThread + void SetCPUAndGPULevel(int CPULevel, int GPULevel); void SetTiledMultiResLevel(ETiledMultiResLevel multiresLevel); void SetColorScaleAndOffset(FLinearColor ColorScale, FLinearColor ColorOffset, bool bApplyToAllLayers); diff --git a/Engine/Plugins/Runtime/ReplicationGraph/Source/Private/ReplicationGraph.cpp b/Engine/Plugins/Runtime/ReplicationGraph/Source/Private/ReplicationGraph.cpp index d4dabc4d47f7..75bf68ac2168 100644 --- a/Engine/Plugins/Runtime/ReplicationGraph/Source/Private/ReplicationGraph.cpp +++ b/Engine/Plugins/Runtime/ReplicationGraph/Source/Private/ReplicationGraph.cpp @@ -731,13 +731,12 @@ int32 UReplicationGraph::ServerReplicateActors(float DeltaSeconds) #endif const float TimeBetweenUpdates = TargetUpdatesPerSecond > 0 ? (1.f / (float)TargetUpdatesPerSecond) : 0.f; - static float TimeLeft = TimeBetweenUpdates; - TimeLeft -= DeltaSeconds; - if (TimeLeft > 0.f) + TimeLeftUntilUpdate -= DeltaSeconds; + if (TimeLeftUntilUpdate > 0.f) { return 0; } - TimeLeft = TimeBetweenUpdates; + TimeLeftUntilUpdate = TimeBetweenUpdates; #endif SCOPED_NAMED_EVENT(UReplicationGraph_ServerReplicateActors, FColor::Green); @@ -1482,11 +1481,29 @@ int64 UReplicationGraph::ReplicateSingleActor(AActor* Actor, FConnectionReplicat UE_LOG(LogReplicationGraph, Display, TEXT("UReplicationGraph::ReplicateSingleActor: %s. NetConnection: %s"), *Actor->GetName(), *NetConnection->Describe()); } - if (ActorInfo.Channel && ActorInfo.Channel->Closing) + // These checks will happen anyway in UActorChannel::ReplicateActor, but we need to be able to detect them to prevent crashes. + // We could consider removing the actor from RepGraph if we hit these cases, but we don't have a good way to notify + // game code or the Net Driver. + if (!ensureMsgf(Actor, TEXT("Null Actor! Channel = %s"), *DescribeSafe(ActorInfo.Channel))) { - // We are waiting for the client to ack this actor channel's close bunch. return 0; } + else if (!ensureMsgf(IsActorValidForReplication(Actor), TEXT("Actor not valid for replication! Actor = %s, Channel = %s"), *Actor->GetFullName(), *DescribeSafe(ActorInfo.Channel))) + { + return 0; + } + if (LIKELY(ActorInfo.Channel)) + { + if (UNLIKELY(ActorInfo.Channel->Closing)) + { + // We are waiting for the client to ack this actor channel's close bunch. + return 0; + } + else if (!ensureMsgf(ActorInfo.Channel->Actor == Actor, TEXT("Mismatched channel actors! Channel = %s, Replicating Actor = %s"), *ActorInfo.Channel->Describe(), *Actor->GetFullName())) + { + return 0; + } + } ActorInfo.LastRepFrameNum = FrameNum; ActorInfo.NextReplicationFrameNum = FrameNum + ActorInfo.ReplicationPeriodFrame; @@ -1534,6 +1551,8 @@ int64 UReplicationGraph::ReplicateSingleActor(AActor* Actor, FConnectionReplicat int64 BitsWritten = 0; const double StartingReplicateActorTimeSeconds = GReplicateActorTimeSeconds; + + ensureMsgf(ActorInfo.Channel->Actor != nullptr, TEXT("Invalid ActorChannel for %s | Channel status: pooled:%u closing:%d"), *Actor->GetName(), ActorInfo.Channel->bPooled, ActorInfo.Channel->Closing); if (UNLIKELY(ActorInfo.bTearOff)) { @@ -1561,7 +1580,10 @@ int64 UReplicationGraph::ReplicateSingleActor(AActor* Actor, FConnectionReplicat } } - CSVTracker.PostReplicateActor(ActorClass, DeltaReplicateActorTimeSeconds, BitsWritten); + const bool bIsTrafficActorDiscovery = ActorDiscoveryMaxBitsPerFrame > 0 && (ActorInfo.Channel && ActorInfo.Channel->SpawnAcked == false); + const bool bIsActorDiscoveryBudgetFull = bIsTrafficActorDiscovery && (ConnectionManager.QueuedBitsForActorDiscovery >= ActorDiscoveryMaxBitsPerFrame); + + CSVTracker.PostReplicateActor(ActorClass, DeltaReplicateActorTimeSeconds, BitsWritten, bIsTrafficActorDiscovery && !bIsActorDiscoveryBudgetFull); // ---------------------------- // Dependent actors @@ -1595,16 +1617,13 @@ int64 UReplicationGraph::ReplicateSingleActor(AActor* Actor, FConnectionReplicat } // Optional budget for actor discovery traffic - if (ActorDiscoveryMaxBitsPerFrame > 0 && (ActorInfo.Channel && ActorInfo.Channel->SpawnAcked == false) ) + if (!bIsActorDiscoveryBudgetFull) { - if (ConnectionManager.QueuedBitsForActorDiscovery < ActorDiscoveryMaxBitsPerFrame) - { - ConnectionManager.QueuedBitsForActorDiscovery += BitsWritten; + ConnectionManager.QueuedBitsForActorDiscovery += BitsWritten; - // Remove the discovery traffic from the regular traffic - NetConnection->QueuedBits -= BitsWritten; - BitsWritten = 0; - } + // Remove the discovery traffic from the regular traffic + NetConnection->QueuedBits -= BitsWritten; + BitsWritten = 0; } return BitsWritten; @@ -2194,6 +2213,7 @@ void UNetReplicationGraphConnection::NotifyResetDestructionInfo() void UNetReplicationGraphConnection::NotifyClientVisibleLevelNamesAdd(FName LevelName, UWorld* StreamingWorld) { + RG_QUICK_SCOPE_CYCLE_COUNTER(UNetReplicationGraphConnection_NotifyClientVisibleLevelNamesAdd); // Undormant every actor in this world for this connection. if (StreamingWorld && StreamingWorld->PersistentLevel) { @@ -2294,6 +2314,17 @@ void UReplicationGraphNode::NotifyResetAllNetworkActors() } } +void UReplicationGraphNode::RemoveChildNode(UReplicationGraphNode* ChildNode) +{ + ensure(ChildNode != nullptr); + + int32 Removed = AllChildNodes.Remove(ChildNode); + if (Removed > 0) + { + ChildNode->TearDown(); + } +} + void UReplicationGraphNode::TearDown() { for (UReplicationGraphNode* Node : AllChildNodes) @@ -3616,7 +3647,7 @@ void UReplicationGraphNode_GridCell::ConditionalCopyDormantActors(FActorRepListR void UReplicationGraphNode_GridCell::OnStaticActorNetDormancyChange(FActorRepListType Actor, FGlobalActorReplicationInfo& GlobalInfo, ENetDormancy NewValue, ENetDormancy OldValue) { - UE_CLOG(CVar_RepGraph_LogActorRemove>0, LogReplicationGraph, Display, TEXT("UReplicationGraphNode_Simple2DSpatializationLeaf::OnNetDormancyChange. %s on %s. Old: %d, New: %d"), *Actor->GetPathName(), *GetPathName(), NewValue, OldValue); + UE_CLOG(CVar_RepGraph_LogNetDormancyDetails>0, LogReplicationGraph, Display, TEXT("UReplicationGraphNode_GridCell::OnNetDormancyChange. %s on %s. Old: %d, New: %d"), *Actor->GetPathName(), *GetPathName(), NewValue, OldValue); const bool bCurrentDormant = NewValue > DORM_Awake; const bool bPreviousDormant = OldValue > DORM_Awake; diff --git a/Engine/Plugins/Runtime/ReplicationGraph/Source/Public/ReplicationGraph.h b/Engine/Plugins/Runtime/ReplicationGraph/Source/Public/ReplicationGraph.h index df83ee6c5b36..50c776d3a7bf 100644 --- a/Engine/Plugins/Runtime/ReplicationGraph/Source/Public/ReplicationGraph.h +++ b/Engine/Plugins/Runtime/ReplicationGraph/Source/Public/ReplicationGraph.h @@ -114,7 +114,7 @@ public: return NewNode; } - void ToggleHighFrequencyPawns(); + void RemoveChildNode(UReplicationGraphNode* OutChildNode); protected: @@ -914,6 +914,9 @@ private: /** Separate bandwidth cap for traffic used when opening actor channels. Ignored if set to 0 */ int32 ActorDiscoveryMaxBitsPerFrame; + /** Internal time used to track when the next update should occur based on frequency settings. */ + float TimeLeftUntilUpdate = 0.f; + UNetReplicationGraphConnection* CreateClientConnectionManagerInternal(UNetConnection* Connection); friend class AReplicationGraphDebugActor; @@ -969,7 +972,7 @@ public: int32 QueuedBitsForActorDiscovery = 0; /** Returns connection graph nodes. This is const so that you do not mutate the array itself. You should use AddConnectionGraphNode/RemoveConnectionGraphNode. */ - const TArray& GetConnectionGraphNodes() { return ConnectionGraphNodes; } + const TArray& GetConnectionGraphNodes() const { return ConnectionGraphNodes; } virtual void NotifyAddDormantDestructionInfo(AActor* Actor) override; diff --git a/Engine/Plugins/Runtime/ReplicationGraph/Source/Public/ReplicationGraphTypes.h b/Engine/Plugins/Runtime/ReplicationGraph/Source/Public/ReplicationGraphTypes.h index 795ae5443046..9c07010bf7f1 100644 --- a/Engine/Plugins/Runtime/ReplicationGraph/Source/Public/ReplicationGraphTypes.h +++ b/Engine/Plugins/Runtime/ReplicationGraph/Source/Public/ReplicationGraphTypes.h @@ -74,7 +74,7 @@ enum class EActorRepListTypeFlags : uint8 }; // Tests if an actor is valid for replication: not pending kill, etc. Says nothing about wanting to replicate or should replicate, etc. -FORCEINLINE bool IsActorValidForReplication(const FActorRepListType& In) { return !In->IsPendingKill() && !In->IsPendingKillPending(); } +FORCEINLINE bool IsActorValidForReplication(const FActorRepListType& In) { return !In->IsActorBeingDestroyed() && !In->IsPendingKillOrUnreachable(); } // Tests if an actor is valid for replication gathering. Meaning, it can be gathered from the replication graph and considered for replication. FORCEINLINE bool IsActorValidForReplicationGather(const FActorRepListType& In) @@ -1140,15 +1140,18 @@ struct FNewReplicatedActorInfo { explicit FNewReplicatedActorInfo(const FActorRepListType& InActor) : Actor(InActor), Class(InActor->GetClass()) { - ULevel* Level = Cast(GetActor()->GetOuter()); - if (Level && Level->IsPersistentLevel() == false) - { - StreamingLevelName = Level->GetOutermost()->GetFName(); - } + StreamingLevelName = GetStreamingLevelNameOfActor(Actor); } AActor* GetActor() const { return Actor; } + static FORCEINLINE FName GetStreamingLevelNameOfActor(const AActor* Actor) + { + ULevel* Level = Actor ? Cast(Actor->GetOuter()) : nullptr; + return (Level && Level->IsPersistentLevel() == false) ? Level->GetOutermost()->GetFName() : NAME_None; + } + + FActorRepListType Actor; FName StreamingLevelName; UClass* Class; @@ -1273,7 +1276,10 @@ CSV_DECLARE_CATEGORY_EXTERN(ReplicationGraphNumReps); /** Helper struct for tracking finer grained ReplicationGraph stats through the CSV profiler. Intention is that it is setup/configured in the UReplicationGraph subclasses */ struct FReplicationGraphCSVTracker { - FReplicationGraphCSVTracker() : EverythingElse(TEXT("Other")), EverythingElse_FastPath(TEXT("OtherFastPath")) + FReplicationGraphCSVTracker() + : EverythingElse(TEXT("Other")) + , EverythingElse_FastPath(TEXT("OtherFastPath")) + , ActorDiscovery(TEXT("ActorDiscovery")) { ResetTrackedClasses(); } @@ -1299,7 +1305,7 @@ struct FReplicationGraphCSVTracker ImplicitClassTracker.Set(BaseActorClass, NewData); } - void PostReplicateActor(UClass* ActorClass, const double Time, const int64 Bits) + void PostReplicateActor(UClass* ActorClass, const double Time, const int64 Bits, const bool bIsActorDiscovery) { #if REPGRAPH_CSV_TRACKER if (!bIsCapturing) @@ -1307,23 +1313,35 @@ struct FReplicationGraphCSVTracker return; } + FTrackedData* TrackedData(nullptr); + if (FTrackerItem* Item = ExplicitClassTracker.FindByKey(ActorClass)) { - Item->Data.BitsAccumulated += Bits; - Item->Data.CPUTimeAccumulated += Time; - Item->Data.NumReplications++; + TrackedData = &Item->Data; } else if (FTrackedData* Data = ImplicitClassTracker.GetChecked(ActorClass).Get()) { - Data->BitsAccumulated += Bits; - Data->CPUTimeAccumulated += Time; - Data->NumReplications++; + TrackedData = Data; } else { - EverythingElse.BitsAccumulated += Bits; - EverythingElse.CPUTimeAccumulated += Time; - EverythingElse.NumReplications++; + TrackedData = &EverythingElse; + } + + // When opening actor channels keep all traffic in a separate bucket + if (bIsActorDiscovery) + { + ActorDiscovery.BitsAccumulated += Bits; + ActorDiscovery.CPUTimeAccumulated += Time; + + // But keep the number of replicated classes unique + TrackedData->NumReplications++; + } + else + { + TrackedData->BitsAccumulated += Bits; + TrackedData->CPUTimeAccumulated += Time; + TrackedData->NumReplications++; } #endif } @@ -1381,6 +1399,7 @@ struct FReplicationGraphCSVTracker ImplicitClassTracker.Set(AActor::StaticClass(), TSharedPtr()); // forces caching of "no tracking" for all other classes EverythingElse.Reset(); EverythingElse_FastPath.Reset(); + ActorDiscovery.Reset(); } void EndReplicationFrame() @@ -1410,6 +1429,7 @@ struct FReplicationGraphCSVTracker PushStats(Profiler, EverythingElse); PushStats(Profiler, EverythingElse_FastPath); + PushStats(Profiler, ActorDiscovery); } #endif } @@ -1464,13 +1484,16 @@ private: TArray> ExplicitClassTracker_FastPath; FTrackedData EverythingElse_FastPath; + + FTrackedData ActorDiscovery; + bool bIsCapturing = false; #if REPGRAPH_CSV_TRACKER void PushStats(FCsvProfiler* Profiler, FTrackedData& Data) { const float Bytes = (float)((Data.BitsAccumulated+7) >> 3); - const float KBytes = Bytes / 1024.f; + const float KBytes = Bytes / 1000.f; Profiler->RecordCustomStat(Data.StatName, CSV_CATEGORY_INDEX(ReplicationGraphKBytes), KBytes, ECsvCustomStatOp::Set); Profiler->RecordCustomStat(Data.StatName, CSV_CATEGORY_INDEX(ReplicationGraphMS), static_cast(Data.CPUTimeAccumulated) * 1000.f, ECsvCustomStatOp::Set); diff --git a/Engine/Plugins/Runtime/WebBrowserNativeProxy/Source/WebBrowserNativeProxy/Private/WebBrowserNativeProxyModule.cpp b/Engine/Plugins/Runtime/WebBrowserNativeProxy/Source/WebBrowserNativeProxy/Private/WebBrowserNativeProxyModule.cpp index c0b768b84a49..9185e36bc264 100644 --- a/Engine/Plugins/Runtime/WebBrowserNativeProxy/Source/WebBrowserNativeProxy/Private/WebBrowserNativeProxyModule.cpp +++ b/Engine/Plugins/Runtime/WebBrowserNativeProxy/Source/WebBrowserNativeProxy/Private/WebBrowserNativeProxyModule.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "WebBrowserNativeProxyModule.h" #include "Modules/ModuleManager.h" diff --git a/Engine/Plugins/Runtime/WebBrowserNativeProxy/Source/WebBrowserNativeProxy/Public/WebBrowserNativeProxyModule.h b/Engine/Plugins/Runtime/WebBrowserNativeProxy/Source/WebBrowserNativeProxy/Public/WebBrowserNativeProxyModule.h index ea6a0701058a..71e29f6ed4cc 100644 --- a/Engine/Plugins/Runtime/WebBrowserNativeProxy/Source/WebBrowserNativeProxy/Public/WebBrowserNativeProxyModule.h +++ b/Engine/Plugins/Runtime/WebBrowserNativeProxy/Source/WebBrowserNativeProxy/Public/WebBrowserNativeProxyModule.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Plugins/Runtime/WebBrowserNativeProxy/Source/WebBrowserNativeProxy/WebBrowserNativeProxy.build.cs b/Engine/Plugins/Runtime/WebBrowserNativeProxy/Source/WebBrowserNativeProxy/WebBrowserNativeProxy.build.cs index 489462e7dbb1..58141cabf1b9 100644 --- a/Engine/Plugins/Runtime/WebBrowserNativeProxy/Source/WebBrowserNativeProxy/WebBrowserNativeProxy.build.cs +++ b/Engine/Plugins/Runtime/WebBrowserNativeProxy/Source/WebBrowserNativeProxy/WebBrowserNativeProxy.build.cs @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. namespace UnrealBuildTool.Rules { diff --git a/Engine/Plugins/Runtime/WebMMoviePlayer/Source/WebMMoviePlayer/Private/WebMMoviePlayer.cpp b/Engine/Plugins/Runtime/WebMMoviePlayer/Source/WebMMoviePlayer/Private/WebMMoviePlayer.cpp index 2395e6ef9166..0716eddf805c 100644 --- a/Engine/Plugins/Runtime/WebMMoviePlayer/Source/WebMMoviePlayer/Private/WebMMoviePlayer.cpp +++ b/Engine/Plugins/Runtime/WebMMoviePlayer/Source/WebMMoviePlayer/Private/WebMMoviePlayer.cpp @@ -5,7 +5,7 @@ #include "MoviePlayer.h" #include "Modules/ModuleManager.h" -#if WITH_WEBM_LIBS && !PLATFORM_WINDOWS +#if WITH_WEBM_LIBS && !PLATFORM_WINDOWS && !PLATFORM_MAC #define WITH_WEBM_STARTUP_MOVIES 1 #else #define WITH_WEBM_STARTUP_MOVIES 0 diff --git a/Engine/Plugins/Runtime/WebMMoviePlayer/Source/WebMMoviePlayer/Private/WebMMovieStreamer.cpp b/Engine/Plugins/Runtime/WebMMoviePlayer/Source/WebMMoviePlayer/Private/WebMMovieStreamer.cpp index adeb01985f49..0b6e7eb451d4 100644 --- a/Engine/Plugins/Runtime/WebMMoviePlayer/Source/WebMMoviePlayer/Private/WebMMovieStreamer.cpp +++ b/Engine/Plugins/Runtime/WebMMoviePlayer/Source/WebMMoviePlayer/Private/WebMMovieStreamer.cpp @@ -190,9 +190,9 @@ void FWebMMovieStreamer::ForceCompletion() void FWebMMovieStreamer::ReleaseAcquiredResources() { - Samples.Reset(); VideoDecoder.Reset(); AudioDecoder.Reset(); + Samples.Reset(); Container.Reset(); SlateVideoTexture.Reset(); diff --git a/Engine/Plugins/Runtime/WindowsMixedReality/Source/MixedRealityInteropLibrary/MixedRealityInteropLibrary.Build.cs b/Engine/Plugins/Runtime/WindowsMixedReality/Source/MixedRealityInteropLibrary/MixedRealityInteropLibrary.Build.cs index 1ef72509f059..c24e06245e42 100644 --- a/Engine/Plugins/Runtime/WindowsMixedReality/Source/MixedRealityInteropLibrary/MixedRealityInteropLibrary.Build.cs +++ b/Engine/Plugins/Runtime/WindowsMixedReality/Source/MixedRealityInteropLibrary/MixedRealityInteropLibrary.Build.cs @@ -23,5 +23,6 @@ public class MixedRealityInteropLibrary : ModuleRules PublicAdditionalLibraries.Add("MixedRealityInterop.lib"); // Delay-load the DLL, so we can load it from the right place first PublicDelayLoadDLLs.Add("MixedRealityInterop.dll"); - } + RuntimeDependencies.Add(PluginDirectory + "/Binaries/ThirdParty/MixedRealityInteropLibrary/" + Target.Platform.ToString() + "/MixedRealityInterop.dll"); + } } diff --git a/Engine/Plugins/Runtime/WindowsMixedReality/Source/WindowsMixedRealityHMD/Private/WindowsMixedRealityHMD.cpp b/Engine/Plugins/Runtime/WindowsMixedReality/Source/WindowsMixedRealityHMD/Private/WindowsMixedRealityHMD.cpp index 3c75bbd8f602..ef143babbe42 100644 --- a/Engine/Plugins/Runtime/WindowsMixedReality/Source/WindowsMixedRealityHMD/Private/WindowsMixedRealityHMD.cpp +++ b/Engine/Plugins/Runtime/WindowsMixedReality/Source/WindowsMixedRealityHMD/Private/WindowsMixedRealityHMD.cpp @@ -19,8 +19,9 @@ #include "Windows/WindowsPlatformMisc.h" #include "Misc/MessageDialog.h" -// Holographic Remoting is only supported in Windows 10 version 1803 or better -#define MIN_WIN_10_VERSION_FOR_WMR 1803 +// Holographic Remoting is only supported in Windows 10 version 1809 or better +// Originally we were supporting 1803, but there were rendering issues specific to that version so for now we only support 1809 +#define MIN_WIN_10_VERSION_FOR_WMR 1809 //--------------------------------------------------- // Windows Mixed Reality HMD Plugin @@ -108,28 +109,34 @@ namespace WindowsMixedReality // Get the base directory of this plugin FString BaseDir = IPluginManager::Get().FindPlugin("WindowsMixedReality")->GetBaseDir(); - // Add on the relative location of the third party dll and load it - FString LibraryPath; + FString EngineDir = FPaths::EngineDir(); + FString BinariesSubDir = FPlatformProcess::GetBinariesSubdirectory(); -#if PLATFORM_64BITS - LibraryPath = FPaths::Combine(*BaseDir, TEXT("Binaries/ThirdParty/MixedRealityInteropLibrary/Win64/MixedRealityInterop.dll")); -#else - LibraryPath = FPaths::Combine(*BaseDir, TEXT("Binaries/ThirdParty/MixedRealityInteropLibrary/Win32/MixedRealityInterop.dll")); -#endif // PLATFORM_64BITS + FString PerceptionSimulationDLLPath = EngineDir / "Binaries" / BinariesSubDir / "Microsoft.Perception.Simulation.dll"; + FString HolographicStreamerDesktopDLLPath = EngineDir / "Binaries" / BinariesSubDir / "HolographicStreamerDesktop.dll"; + FString MRInteropLibraryPath = BaseDir / "Binaries/ThirdParty/MixedRealityInteropLibrary" / BinariesSubDir / "MixedRealityInterop.dll"; - void* MixedRealityInteropLibraryHandle = !LibraryPath.IsEmpty() ? FPlatformProcess::GetDllHandle(*LibraryPath) : nullptr; + // Load these dependencies first or MixedRealityInteropLibraryHandle fails to load since it doesn't look in the correct path for its dependencies automatically + void* PerceptionSimulationDLLHandle = FPlatformProcess::GetDllHandle(*PerceptionSimulationDLLPath); + void* HolographicStreamerDesktopDLLHandle = FPlatformProcess::GetDllHandle(*HolographicStreamerDesktopDLLPath); - if (MixedRealityInteropLibraryHandle) + // Then finally try to load the WMR Interop Library + void* MixedRealityInteropLibraryHandle = !MRInteropLibraryPath.IsEmpty() ? FPlatformProcess::GetDllHandle(*MRInteropLibraryPath) : nullptr; + + FString OSVersionLabel; + FString OSSubVersionLabel; + FWindowsPlatformMisc::GetOSVersions(OSVersionLabel, OSSubVersionLabel); + // GetOSVersion returns the Win10 release version in the OSVersion rather than the OSSubVersion, so parse it out ourselves + OSSubVersionLabel = OSVersionLabel; + bool bHasSupportedWindowsVersion = OSSubVersionLabel.RemoveFromStart("Windows 10 (Release ") && OSSubVersionLabel.RemoveFromEnd(")") && (FCString::Atoi(*OSSubVersionLabel) >= MIN_WIN_10_VERSION_FOR_WMR); + if (MixedRealityInteropLibraryHandle && bHasSupportedWindowsVersion) { HMD = new MixedRealityInterop(); } else { - FString OSVersionLabel; - FString OSSubVersionLabel; - FWindowsPlatformMisc::GetOSVersions(OSVersionLabel, OSSubVersionLabel); FText ErrorText = FText::Format(FTextFormat(NSLOCTEXT("WindowsMixedRealityHMD", "MixedRealityInteropLibraryError", - "Failed to load Windows Mixed Reality Interop Library.\nNote: UE4 only supports Windows Mixed Reality on Windows 10 Release {0} or higher. Current version: {1}")), + "Failed to load Windows Mixed Reality Interop Library, or this version of Windows is not supported. \nNote: UE4 only supports Windows Mixed Reality on Windows 10 Release {0} or higher. Current version: {1}")), FText::FromString(FString::FromInt(MIN_WIN_10_VERSION_FOR_WMR)), FText::FromString(OSVersionLabel)); FMessageDialog::Open(EAppMsgType::Ok, ErrorText); UE_LOG(LogCore, Error, TEXT("%s"), *ErrorText.ToString()); @@ -395,33 +402,28 @@ namespace WindowsMixedReality gameWindowWidth = windowRect.right - windowRect.left; gameWindowHeight = windowRect.bottom - windowRect.top; - } - } - // Restore windows focus to game window to preserve keyboard/mouse input. - if ((currentWornState == EHMDWornState::Type::Worn) && GEngine) - { - HWND gameHWND = (HWND)GEngine->GameViewport->GetWindow()->GetNativeWindow()->GetOSWindowHandle(); + // Restore windows focus to game window to preserve keyboard/mouse input. + if ((currentWornState == EHMDWornState::Type::Worn) && GEngine) + { + // Set mouse focus to center of game window so any clicks interact with the game. + if (mouseLockedToCenter) + { + CenterMouse(windowRect); + } - // Set mouse focus to center of game window so any clicks interact with the game. - if (mouseLockedToCenter) - { - RECT windowRect; - GetWindowRect(gameHWND, &windowRect); + if (GetCapture() != gameHWND) + { + // Keyboard input + SetForegroundWindow(gameHWND); - CenterMouse(windowRect); - } + // Mouse input + SetCapture(gameHWND); + SetFocus(gameHWND); - if (GetCapture() != gameHWND) - { - // Keyboard input - SetForegroundWindow(gameHWND); - - // Mouse input - SetCapture(gameHWND); - SetFocus(gameHWND); - - FSlateApplication::Get().SetAllUserFocusToGameViewport(); + FSlateApplication::Get().SetAllUserFocusToGameViewport(); + } + } } } diff --git a/Engine/Shaders/Private/GameplayMediaEncoderShaders.usf b/Engine/Shaders/Private/GameplayMediaEncoderShaders.usf index 6b2494c2bbcd..227ad1acdb45 100644 --- a/Engine/Shaders/Private/GameplayMediaEncoderShaders.usf +++ b/Engine/Shaders/Private/GameplayMediaEncoderShaders.usf @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. /*========================================================================================= GameplayMediaEncoderShaders.usf: utility shaders for the GameplayMediaEncoder module diff --git a/Engine/Shaders/Private/LandscapeProceduralCS.usf b/Engine/Shaders/Private/LandscapeProceduralCS.usf new file mode 100644 index 000000000000..3f24fa28cd4f --- /dev/null +++ b/Engine/Shaders/Private/LandscapeProceduralCS.usf @@ -0,0 +1,90 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +#include "Common.ush" + +uint ComponentSize; + +struct FLandscapeProceduralWeightmapExtractLayersComponentData +{ + int2 ComponentVertexPosition; // Section Base of the component converted to vertex + uint DestinationPaintLayerIndex; // correspond to which layer info object index the data should be stored in the texture 2d array + uint WeightmapChannelToProcess; // correspond to which RGBA channel to process + int2 AtlasTexturePositionOutput; // This represent the location we will write layer information +}; + +StructuredBuffer InExtractLayersComponentsData; +Texture2D InComponentWeightMaps; +RWTexture2DArray OutAtlasPaintLayers; + +[numthreads(THREADGROUP_SIZEX, THREADGROUP_SIZEY, 1)] +void ComputeWeightmapPerPaintLayer(uint3 DispatchThreadId : SV_DispatchThreadID) +{ + if (DispatchThreadId.x < ComponentSize && DispatchThreadId.y < ComponentSize) + { + FLandscapeProceduralWeightmapExtractLayersComponentData ComponentData = InExtractLayersComponentsData[DispatchThreadId.z]; + int2 CurrentTexel = DispatchThreadId.xy + ComponentData.ComponentVertexPosition; + + float4 LayerBlend = InComponentWeightMaps.Load(int3(CurrentTexel, 0)); + OutAtlasPaintLayers[int3(DispatchThreadId.xy + ComponentData.AtlasTexturePositionOutput, ComponentData.DestinationPaintLayerIndex)] = LayerBlend[ComponentData.WeightmapChannelToProcess]; + } +} + +struct FLandscapeProceduralWeightmapPackLayersComponentData +{ + int4 ComponenVertexPositionX; // Section Base of the component converted to vertex for each rgba value (as a texture can contain data from different component) + int4 ComponenVertexPositionY; // Section Base of the component converted to vertex for each rgba value (as a texture can contain data from different component) + int4 SourcePaintLayerIndex; // correspond to which layer info object index the data should be stored in the texture 2d array + int4 WeightmapChannelToProcess; // correspond to which RGBA channel to process +}; + +StructuredBuffer InPackLayersComponentsData; +Buffer InWeightmapWeightBlendMode; +Buffer InWeightmapTextureOutputOffset; +Texture2DArray InAtlasPaintLayers; +RWTexture2D OutComponentWeightMaps; + +float ComputeWeightmapChannel(int ChannelIndexToProcess, uint2 TexelPosition, uint SourcePaintLayerIndex) +{ + float Channel = 0; + + if (ChannelIndexToProcess != -1) + { + Channel = InAtlasPaintLayers.Load(int4(TexelPosition, SourcePaintLayerIndex, 0)); + float AllLayerWeightSum = 0; + + float Unused = 0.0; + float ArraySize = 0.0; + InAtlasPaintLayers.GetDimensions(Unused, Unused, ArraySize); + + for (int i = 0; i < ArraySize; ++i) + { + if (InWeightmapWeightBlendMode[i] == 1.0) // Only include the one that are weight blended + { + AllLayerWeightSum += InAtlasPaintLayers.Load(int4(TexelPosition, i, 0)); + } + } + + Channel = AllLayerWeightSum == 0 ? Channel : clamp(Channel / AllLayerWeightSum, 0.0, 1.0); + } + + return Channel; +} + +[numthreads(THREADGROUP_SIZEX, THREADGROUP_SIZEY, 1)] +void PackPaintLayerToWeightmap(uint3 DispatchThreadId : SV_DispatchThreadID) +{ + if (DispatchThreadId.x < ComponentSize && DispatchThreadId.y < ComponentSize) + { + FLandscapeProceduralWeightmapPackLayersComponentData ComponentData = InPackLayersComponentsData[DispatchThreadId.z]; + + float4 Channels; + + for (int i = 0; i < 4; ++i) + { + Channels[i] = ComputeWeightmapChannel(ComponentData.WeightmapChannelToProcess[i], DispatchThreadId.xy + int2(ComponentData.ComponenVertexPositionX[i], ComponentData.ComponenVertexPositionY[i]), ComponentData.SourcePaintLayerIndex[i]); + } + + float2 TextureOutputOffset = InWeightmapTextureOutputOffset[DispatchThreadId.z]; + OutComponentWeightMaps[DispatchThreadId.xy + TextureOutputOffset] = Channels; + } +} \ No newline at end of file diff --git a/Engine/Shaders/Private/LandscapeProceduralPS.usf b/Engine/Shaders/Private/LandscapeProceduralPS.usf index dc5442c9c447..188aa76d4ce2 100644 --- a/Engine/Shaders/Private/LandscapeProceduralPS.usf +++ b/Engine/Shaders/Private/LandscapeProceduralPS.usf @@ -2,39 +2,52 @@ #include "Common.ush" -Texture2D ReadHeightmapTexture1; -Texture2D ReadHeightmapTexture2; -SamplerState ReadHeightmapTexture1Sampler; -SamplerState ReadHeightmapTexture2Sampler; -float2 LayerInfo; // x == weight, y == visibility -float4 OutputConfig; // x == ApplyLayerModifiers, y == OutputAsDelta, z == Use ReadHeightmapTexture2 and out Delta with ReadHeightmapTexture1, w == Output Normals -float2 HeightmapTextureSize; // x == source heightmap width, y == source heightmap height -float3 LandscapeGridScale; // x == LS Actor DrawScale.X, y == LS Actor DrawScale.y, z == LS Actor DrawScale.z / 128.0f (ZSCALE) +Texture2D ReadTexture1; +Texture2D ReadTexture2; // This one is optional, it's valid when OutputConfig.z == 1 +SamplerState ReadTexture1Sampler; +SamplerState ReadTexture2Sampler; +float2 LayerInfo; // x == weight, y == visibility +float4 OutputConfig; // x == ApplyLayerModifiers, y == OutputAsDelta or Output weightmap as substractive/additive, z == Use ReadTexture2 and out Delta with ReadTexture1, w == Output Normals or Output weightmap as normalized weight blend +float2 TextureSize; // x == read texture 1 width, y == read texture 1 height +float3 LandscapeGridScale; // x == LS Actor DrawScale.X, y == LS Actor DrawScale.y, z == LS Actor DrawScale.z / 128.0f (ZSCALE) float CurrentMipComponentVertexCount; +float2 CurrentMipTextureSize; +float2 ParentMipTextureSize; + +float ExtractHeight(float2 InPackedHeight) +{ + return float(((int)round(InPackedHeight.r * 255.0) << 8) | (int)round(InPackedHeight.g * 255.0)); +} + +float2 PackHeight(float InExtractedHeight) +{ + InExtractedHeight = clamp(InExtractedHeight, 0.0f, 65536.0f); + int iHeight = (int)InExtractedHeight; + return float2((float)((iHeight - (iHeight & 255)) >> 8) / 255.0, (float)(iHeight & 255) / 255.0); +} float3 SampleTextureYNormal(float2 InTextureCoordinates, float2 InTexelSize, bool InUp) { float2 SamplingUV = InTextureCoordinates; SamplingUV.y = InUp ? SamplingUV.y - InTexelSize.y : SamplingUV.y + InTexelSize.y; - float2 PosSample = ReadHeightmapTexture1.SampleLevel(ReadHeightmapTexture1Sampler, SamplingUV, 0).rg; - float PosHeight = float(((int)round(PosSample.r * 255.0) << 8) | (int)round(PosSample.g * 255.0)) - 32768.0; - return float3(SamplingUV * HeightmapTextureSize * LandscapeGridScale.xy, PosHeight * LandscapeGridScale.z * TERRAIN_ZSCALE); + float2 PosSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SamplingUV, 0).rg; + float PosHeight = ExtractHeight(PosSample) - 32768.0; + return float3(SamplingUV * TextureSize * LandscapeGridScale.xy, PosHeight * LandscapeGridScale.z * TERRAIN_ZSCALE); } float3 SampleTextureXNormal(float2 InTextureCoordinates, float2 InTexelSize, bool InLeft) { float2 SamplingUV = InTextureCoordinates; SamplingUV.x = InLeft ? SamplingUV.x - InTexelSize.x : SamplingUV.x + InTexelSize.x; - float2 PosSample = ReadHeightmapTexture1.SampleLevel(ReadHeightmapTexture1Sampler, SamplingUV, 0).rg; - float PosHeight = float(((int)round(PosSample.r * 255.0) << 8) | (int)round(PosSample.g * 255.0)) - 32768.0; - return float3(SamplingUV * HeightmapTextureSize * LandscapeGridScale.xy, PosHeight * LandscapeGridScale.z * TERRAIN_ZSCALE); + float2 PosSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SamplingUV, 0).rg; + float PosHeight = ExtractHeight(PosSample) - 32768.0; + return float3(SamplingUV * TextureSize * LandscapeGridScale.xy, PosHeight * LandscapeGridScale.z * TERRAIN_ZSCALE); } -void PSMain(float2 InTextureCoordinates : TEXCOORD0, - out float4 OutColor : SV_Target0) +void PSHeightmapMain(float2 InTextureCoordinates : TEXCOORD0, out float4 OutColor : SV_Target0) { - float4 SourceColor = ReadHeightmapTexture1.Sample(ReadHeightmapTexture1Sampler, InTextureCoordinates); - float Height = float(((int)round(SourceColor.r * 255.0) << 8) | (int)round(SourceColor.g * 255.0)); + float4 SourceColor = ReadTexture1.Sample(ReadTexture1Sampler, InTextureCoordinates); + float Height = ExtractHeight(SourceColor.rg); // Perform calculation 0 based Height -= 32768.0f; @@ -48,9 +61,8 @@ void PSMain(float2 InTextureCoordinates : TEXCOORD0, // Output using 2nd heightmap if (OutputConfig.z == 1.0) { - float2 BaseColor = ReadHeightmapTexture2.Sample(ReadHeightmapTexture2Sampler, InTextureCoordinates).rg; - float BaseHeight = float(((int)round(BaseColor.r * 255.0) << 8) | (int)round(BaseColor.g * 255.0)); - + float2 BaseColor = ReadTexture2.Sample(ReadTexture2Sampler, InTextureCoordinates).rg; + float BaseHeight = ExtractHeight(BaseColor.rg); Height = OutputConfig.y == 1 ? BaseHeight + (Height - BaseHeight) : BaseHeight + Height; } else @@ -62,9 +74,7 @@ void PSMain(float2 InTextureCoordinates : TEXCOORD0, } } - Height = clamp(Height, 0.0f, 65536.0f); - int iHeight = (int)Height; - float2 PackedHeight = float2((float)((iHeight - (iHeight & 255)) >> 8) / 255.0, (float)(iHeight & 255) / 255.0); + float2 PackedHeight = PackHeight(Height); OutColor.r = PackedHeight.x; OutColor.g = PackedHeight.y; @@ -74,14 +84,14 @@ void PSMain(float2 InTextureCoordinates : TEXCOORD0, // Output normals if (OutputConfig.w == 1.0) { - float2 TexelSize = 1.0 / HeightmapTextureSize; + float2 TexelSize = 1.0 / TextureSize; bool IsMinBorderTexelX = InTextureCoordinates.x <= TexelSize.x; bool IsMinBorderTexelY = InTextureCoordinates.y <= TexelSize.y; bool IsMaxBorderTexelX = InTextureCoordinates.x >= TexelSize.x * min(CurrentMipComponentVertexCount - 1, 1.0); bool IsMaxBorderTexelY = InTextureCoordinates.y >= TexelSize.y * min(CurrentMipComponentVertexCount - 1, 1.0); - float3 CurrentPos = float3(InTextureCoordinates * HeightmapTextureSize * LandscapeGridScale.xy, (Height - 32768.0f) * LandscapeGridScale.z * TERRAIN_ZSCALE); + float3 CurrentPos = float3(InTextureCoordinates * TextureSize * LandscapeGridScale.xy, (Height - 32768.0f) * LandscapeGridScale.z * TERRAIN_ZSCALE); float3 FinalNormal = 0.0; if (IsMinBorderTexelX) // left border @@ -179,11 +189,156 @@ void PSMain(float2 InTextureCoordinates : TEXCOORD0, } } -float2 CurrentMipTextureSize; -float2 ParentMipTextureSize; +void PSHeightmapMainMips(float2 InTextureCoordinates : TEXCOORD0, out float4 OutColor : SV_Target0) +{ + bool IsMinBorderTexelX = false; + bool IsMinBorderTexelY = false; + bool IsMaxBorderTexelX = false; + bool IsMaxBorderTexelY = false; -void PSMainMips(float2 InTextureCoordinates : TEXCOORD0, - out float4 OutColor : SV_Target0) + // Special case of 1 texel size component + if (CurrentMipComponentVertexCount == 1) + { + if (CurrentMipTextureSize.x >= CurrentMipTextureSize.y) // x size 1 texel + { + IsMinBorderTexelY = true; + IsMinBorderTexelX = (InTextureCoordinates.x * CurrentMipTextureSize.x) <= 1.0; + IsMaxBorderTexelX = (InTextureCoordinates.x * CurrentMipTextureSize.x) >= CurrentMipTextureSize.x - 1.0; + } + else + { + IsMinBorderTexelX = true; + IsMinBorderTexelY = (InTextureCoordinates.y * CurrentMipTextureSize.y) <= 1.0; + IsMaxBorderTexelY = (InTextureCoordinates.y * CurrentMipTextureSize.y) >= CurrentMipTextureSize.y - 1.0; + } + } + else + { + float2 CurrentMipQuadTexelSize = 1.0 / CurrentMipComponentVertexCount; + float2 LandscapeQuadUV = frac(InTextureCoordinates * CurrentMipTextureSize / CurrentMipComponentVertexCount); + + IsMinBorderTexelX = LandscapeQuadUV.x <= CurrentMipQuadTexelSize.x; + IsMinBorderTexelY = LandscapeQuadUV.y <= CurrentMipQuadTexelSize.y; + IsMaxBorderTexelX = LandscapeQuadUV.x >= (CurrentMipQuadTexelSize.x * max(CurrentMipComponentVertexCount - 1, 1.0)); + IsMaxBorderTexelY = LandscapeQuadUV.y >= (CurrentMipQuadTexelSize.y * max(CurrentMipComponentVertexCount - 1, 1.0)); + } + + float2 ParentMipTexelSize = 1.0 / ParentMipTextureSize; + float2 SourceUV = InTextureCoordinates - (ParentMipTexelSize * 0.5); + float HeightResult = 0; + float2 NormalResult = 0; + float Alpha = 0.5; + OutColor = 0; // Default to 0 + + if (IsMinBorderTexelX) // on left border + { + float4 SourceSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SourceUV, 0); + float4 DownSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SourceUV + float2(0.0, ParentMipTexelSize.y), 0); + float SourceHeight = ExtractHeight(SourceSample.rg); + float DownHeight = ExtractHeight(DownSample.rg); + + if (IsMinBorderTexelY) // on top border + { + HeightResult = SourceHeight; + NormalResult = float2(SourceSample.ba); + } + else if (IsMaxBorderTexelY) // on bottom border + { + HeightResult = DownHeight; + NormalResult = float2(DownSample.ba); + } + else + { + HeightResult = clamp(round(lerp(SourceHeight, DownHeight, Alpha)), 0.0, 65536.0); + NormalResult.x = lerp(SourceSample.b, DownSample.b, Alpha); + NormalResult.y = lerp(SourceSample.a, DownSample.a, Alpha); + } + } + else if (IsMaxBorderTexelX) // on right border + { + float4 RightSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SourceUV + float2(ParentMipTexelSize.x, 0.0), 0); + float4 DownRightSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SourceUV + ParentMipTexelSize, 0); + float RightHeight = ExtractHeight(RightSample.rg); + float DownRightHeight = ExtractHeight(DownRightSample.rg); + + if (IsMinBorderTexelY) // on top border + { + HeightResult = RightHeight; + NormalResult = float2(RightSample.ba); + } + else if (IsMaxBorderTexelY) // on bottom border + { + HeightResult = DownRightHeight; + NormalResult = float2(DownRightSample.ba); + } + else + { + HeightResult = clamp(round(lerp(RightHeight, DownRightHeight, Alpha)), 0.0, 65536.0);; + NormalResult.x = lerp(RightSample.b, DownRightSample.b, Alpha); + NormalResult.y = lerp(RightSample.a, DownRightSample.a, Alpha); + } + } + else // center texel + { + float4 SourceSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SourceUV, 0); + float4 DownSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SourceUV + float2(0.0, ParentMipTexelSize.y), 0); + float4 RightSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SourceUV + float2(ParentMipTexelSize.x, 0.0), 0); + float4 DownRightSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SourceUV + ParentMipTexelSize, 0); + + float SourceHeight = ExtractHeight(SourceSample.rg); + float DownHeight = ExtractHeight(DownSample.rg); + float RightHeight = ExtractHeight(RightSample.rg); + float DownRightHeight = ExtractHeight(DownRightSample.rg); + + if (IsMinBorderTexelY) // on top border + { + HeightResult = clamp(round(lerp(SourceHeight, RightHeight, Alpha)), 0.0, 65536.0); + NormalResult.x = lerp(SourceSample.b, RightSample.b, Alpha); + NormalResult.y = lerp(SourceSample.a, RightSample.a, Alpha); + } + else if (IsMaxBorderTexelY) // on bottom border + { + HeightResult = clamp(round(lerp(DownHeight, DownRightHeight, Alpha)), 0.0, 65536.0); + NormalResult.x = lerp(DownSample.b, DownRightSample.b, Alpha); + NormalResult.y = lerp(DownSample.a, DownRightSample.a, Alpha); + } + else + { + float SourceToRightHeight = lerp(SourceHeight, RightHeight, Alpha); + float DownToDownRightHeight = lerp(DownHeight, DownRightHeight, Alpha); + + HeightResult = clamp(round(lerp(SourceToRightHeight, DownToDownRightHeight, Alpha)), 0.0, 65536.0); + NormalResult.x = lerp(lerp(SourceSample.b, RightSample.b, Alpha), lerp(DownSample.b, DownRightSample.b, Alpha), Alpha); + NormalResult.y = lerp(lerp(SourceSample.a, RightSample.a, Alpha), lerp(DownSample.a, DownRightSample.a, Alpha), Alpha); + } + } + + float2 PackedHeightResult = PackHeight(HeightResult); + OutColor = float4(PackedHeightResult.xy, NormalResult.xy); +} + +float4 WeightmapLayerWeightBlend; // each xyzw == 1 texture channel + +void PSWeightmapMain(float2 InTextureCoordinates : TEXCOORD0, out float4 OutColor : SV_Target0) +{ + float4 SourceColor = ReadTexture1.Sample(ReadTexture1Sampler, InTextureCoordinates); + + // Output as Layer, so apply Layer info + SourceColor = OutputConfig.x == 1.0 ? SourceColor * LayerInfo.x * LayerInfo.y : SourceColor; + + // Output using 2nd heightmap + if (OutputConfig.z == 1.0) + { + float4 BaseColor = ReadTexture2.Sample(ReadTexture2Sampler, InTextureCoordinates); + + // Apply addtive/substractive logic + SourceColor = OutputConfig.y == 0.0f ? clamp(BaseColor + SourceColor, 0.0, 1.0) : clamp(BaseColor - SourceColor, 0.0, 1.0); + } + + OutColor = SourceColor; +} + +void PSWeightmapMainMips(float2 InTextureCoordinates : TEXCOORD0, out float4 OutColor : SV_Target0) { bool IsMinBorderTexelX = false; bool IsMinBorderTexelY = false; @@ -221,103 +376,30 @@ void PSMainMips(float2 InTextureCoordinates : TEXCOORD0, float2 SourceUV = InTextureCoordinates - (ParentMipTexelSize * 0.5); float2 HeightResult = 0; float2 NormalResult = 0; + float Alpha = 0.5; OutColor = 0; // Default to 0 if (IsMinBorderTexelX) // on left border { - float4 SourceSample = ReadHeightmapTexture1.SampleLevel(ReadHeightmapTexture1Sampler, SourceUV, 0); - float4 DownSample = ReadHeightmapTexture1.SampleLevel(ReadHeightmapTexture1Sampler, SourceUV + float2(0.0, ParentMipTexelSize.y), 0); - float SourceHeight = clamp(float(((int)round(SourceSample.r * 255.0) << 8) | (int)round(SourceSample.g * 255.0)), 0.0, 65536.0); - float DownHeight = clamp(float(((int)round(DownSample.r * 255.0) << 8) | (int)round(DownSample.g * 255.0)), 0.0, 65536.0); + float4 SourceSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SourceUV, 0); + float4 DownSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SourceUV + float2(0.0, ParentMipTexelSize.y), 0); - if (IsMinBorderTexelY) // on top border - { - HeightResult = SourceHeight; - NormalResult = float2(SourceSample.ba); - } - else if (IsMaxBorderTexelY) // on bottom border - { - HeightResult = DownHeight; - NormalResult = float2(DownSample.ba); - } - else - { - float FracTopDown = 0.5; - HeightResult = clamp(round(lerp(SourceHeight, DownHeight, FracTopDown)), 0.0, 65536.0); - NormalResult.x = lerp(SourceSample.b, DownSample.b, FracTopDown); - NormalResult.y = lerp(SourceSample.a, DownSample.a, FracTopDown); - } + OutColor = IsMinBorderTexelY ? SourceSample : (IsMaxBorderTexelY ? DownSample : lerp(SourceSample, DownSample, Alpha)); } else if (IsMaxBorderTexelX) // on right border { - float4 RightSample = ReadHeightmapTexture1.SampleLevel(ReadHeightmapTexture1Sampler, SourceUV + float2(ParentMipTexelSize.x, 0.0), 0); - float4 DownRightSample = ReadHeightmapTexture1.SampleLevel(ReadHeightmapTexture1Sampler, SourceUV + ParentMipTexelSize, 0); - float RightHeight = clamp(float(((int)round(RightSample.r * 255.0) << 8) | (int)round(RightSample.g * 255.0)), 0.0, 65536.0); - float DownRightHeight = clamp(float(((int)round(DownRightSample.r * 255.0) << 8) | (int)round(DownRightSample.g * 255.0)), 0.0, 65536.0); + float4 RightSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SourceUV + float2(ParentMipTexelSize.x, 0.0), 0); + float4 DownRightSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SourceUV + ParentMipTexelSize, 0); - if (IsMinBorderTexelY) // on top border - { - HeightResult = RightHeight; - NormalResult = float2(RightSample.ba); - } - else if (IsMaxBorderTexelY) // on bottom border - { - HeightResult = DownRightHeight; - NormalResult = float2(DownRightSample.ba); - } - else - { - float FracTopDown = 0.5; - HeightResult = clamp(round(lerp(RightHeight, DownRightHeight, FracTopDown)), 0.0, 65536.0);; - NormalResult.x = lerp(RightSample.b, DownRightSample.b, FracTopDown); - NormalResult.y = lerp(RightSample.a, DownRightSample.a, FracTopDown); - } + OutColor = IsMinBorderTexelY ? RightSample : (IsMaxBorderTexelY ? DownRightSample : lerp(RightSample, DownRightSample, Alpha)); } else // center texel { - float4 SourceSample = ReadHeightmapTexture1.SampleLevel(ReadHeightmapTexture1Sampler, SourceUV, 0); - float4 DownSample = ReadHeightmapTexture1.SampleLevel(ReadHeightmapTexture1Sampler, SourceUV + float2(0.0, ParentMipTexelSize.y), 0); - float4 RightSample = ReadHeightmapTexture1.SampleLevel(ReadHeightmapTexture1Sampler, SourceUV + float2(ParentMipTexelSize.x, 0.0), 0); - float4 DownRightSample = ReadHeightmapTexture1.SampleLevel(ReadHeightmapTexture1Sampler, SourceUV + ParentMipTexelSize, 0); + float4 SourceSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SourceUV, 0); + float4 DownSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SourceUV + float2(0.0, ParentMipTexelSize.y), 0); + float4 RightSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SourceUV + float2(ParentMipTexelSize.x, 0.0), 0); + float4 DownRightSample = ReadTexture1.SampleLevel(ReadTexture1Sampler, SourceUV + ParentMipTexelSize, 0); - float SourceHeight = clamp(float(((int)round(SourceSample.r * 255.0) << 8) | (int)round(SourceSample.g * 255.0)), 0.0, 65536.0); - float DownHeight = clamp(float(((int)round(DownSample.r * 255.0) << 8) | (int)round(DownSample.g * 255.0)), 0.0, 65536.0); - float RightHeight = clamp(float(((int)round(RightSample.r * 255.0) << 8) | (int)round(RightSample.g * 255.0)), 0.0, 65536.0); - float DownRightHeight = clamp(float(((int)round(DownRightSample.r * 255.0) << 8) | (int)round(DownRightSample.g * 255.0)), 0.0, 65536.0); - - if (IsMinBorderTexelY) // on top border - { - float FracSourceToRight = 0.5; - HeightResult = clamp(round(lerp(SourceHeight, RightHeight, FracSourceToRight)), 0.0, 65536.0); - NormalResult.x = lerp(SourceSample.b, RightSample.b, FracSourceToRight); - NormalResult.y = lerp(SourceSample.a, RightSample.a, FracSourceToRight); - } - else if (IsMaxBorderTexelY) // on bottom border - { - float FracDownToDownRight = 0.5; - HeightResult = clamp(round(lerp(DownHeight, DownRightHeight, FracDownToDownRight)), 0.0, 65536.0); - NormalResult.x = lerp(DownSample.b, DownRightSample.b, FracDownToDownRight); - NormalResult.y = lerp(DownSample.a, DownRightSample.a, FracDownToDownRight); - } - else - { - float FracSourceToRight = 0.5; - float FracDownToDownRight = 0.5; - - float SourceToRightHeight = lerp(SourceHeight, RightHeight, 0.5); - float DownToDownRightHeight = lerp(DownHeight, DownRightHeight, 0.5); - - float SourceToRightDeltaHeight = RightHeight - 32768.0; - float DownToDownRightDeltaHeight = DownRightHeight - 32768.0; - - float FracTopDown = 0.5; - - HeightResult = clamp(round(lerp(SourceToRightHeight, DownToDownRightHeight, 0.5)), 0.0, 65536.0); - NormalResult.x = lerp(lerp(SourceSample.b, RightSample.b, FracSourceToRight), lerp(DownSample.b, DownRightSample.b, FracDownToDownRight), FracTopDown); - NormalResult.y = lerp(lerp(SourceSample.a, RightSample.a, FracSourceToRight), lerp(DownSample.a, DownRightSample.a, FracDownToDownRight), FracTopDown); - } + OutColor = IsMinBorderTexelY ? lerp(SourceSample, RightSample, Alpha) : (IsMaxBorderTexelY ? lerp(DownSample, DownRightSample, Alpha) : lerp(lerp(SourceSample, RightSample, Alpha), lerp(DownSample, DownRightSample, Alpha), Alpha)); } - - float2 PackedHeightResult = float2((float)(((int)HeightResult - ((int)HeightResult & 255)) >> 8) / 255.0, (float)((int)HeightResult & 255) / 255.0); - OutColor = float4(PackedHeightResult.x, PackedHeightResult.y, NormalResult.x, NormalResult.y); } \ No newline at end of file diff --git a/Engine/Shaders/Private/MobileOpacityShaders.usf b/Engine/Shaders/Private/MobileOpacityShaders.usf index 55c1a16f52c1..3bb947fefe66 100644 --- a/Engine/Shaders/Private/MobileOpacityShaders.usf +++ b/Engine/Shaders/Private/MobileOpacityShaders.usf @@ -5,10 +5,13 @@ MobileOpacityShaders.usf: Renders opacity to alpha channel. Mobile only. =============================================================================*/ #include "Common.ush" + +// Reroute MobileSceneTextures uniform buffer references to the base pass uniform buffer +#define MobileSceneTextures MobileBasePass.SceneTextures + #include "/Engine/Generated/Material.ush" #include "/Engine/Generated/VertexFactory.ush" - #define VertexFactoryGetInterpolants VertexFactoryGetInterpolantsVSToPS void MainVS( diff --git a/Engine/Shaders/Private/PathTracing/Material/PathTracingMaterialCommon.ush b/Engine/Shaders/Private/PathTracing/Material/PathTracingMaterialCommon.ush index 8ec684312b28..499178c38892 100644 --- a/Engine/Shaders/Private/PathTracing/Material/PathTracingMaterialCommon.ush +++ b/Engine/Shaders/Private/PathTracing/Material/PathTracingMaterialCommon.ush @@ -42,7 +42,7 @@ bool IsPureSpecular(FMaterialClosestHitPayload HitInfo) bool IsPureSpecularReflection(FMaterialClosestHitPayload HitInfo) { - return IsPureSpecular(HitInfo) && HitInfo.BlendingMode == 0; + return IsPureSpecular(HitInfo) && HitInfo.BlendingMode == 0 && HitInfo.Metallic == 1; } bool IsPureSpecularTransmission(FMaterialClosestHitPayload HitInfo) diff --git a/Engine/Shaders/Private/PathTracing/Material/PathTracingSpecularReflection.ush b/Engine/Shaders/Private/PathTracing/Material/PathTracingSpecularReflection.ush index 28329d42693c..416f68254fd4 100644 --- a/Engine/Shaders/Private/PathTracing/Material/PathTracingSpecularReflection.ush +++ b/Engine/Shaders/Private/PathTracing/Material/PathTracingSpecularReflection.ush @@ -15,17 +15,14 @@ void SpecularReflection_SampleMaterial( float3 N_World = GetWorldNormal(Payload); float3 SpecularColor = GetSpecularColor(Payload); - //OutDirection = reflect(RayDirection, N_World); // To reflect in world coords directly float3 V_World = normalize(-RayDirection); - float3 V_Local = WorldToTangent(V_World, N_World); + OutDirection = reflect(V_World, N_World); + + float NoV = saturate(dot(V_World, N_World)); + float VoH = NoV; + float NoL = NoV; + OutThroughput = F_Schlick(SpecularColor, VoH) / NoL; - float3 L_Local; - L_Local.x = -V_Local.x; - L_Local.y = -V_Local.y; - L_Local.z = V_Local.z; - OutDirection = TangentToWorld(L_Local, N_World); - - OutThroughput = SpecularColor; OutPdf = 1.0; } diff --git a/Engine/Shaders/Private/PostProcessMaterialShaders.usf b/Engine/Shaders/Private/PostProcessMaterialShaders.usf index e4ed8ddd260f..50013edfc3ba 100644 --- a/Engine/Shaders/Private/PostProcessMaterialShaders.usf +++ b/Engine/Shaders/Private/PostProcessMaterialShaders.usf @@ -211,7 +211,11 @@ void MainVS_ES2( UNROLL for (int CoordinateIndex = 0; CoordinateIndex < NUM_MATERIAL_TEXCOORDS; CoordinateIndex++) { +#if POST_PROCESS_AR_PASSTHROUGH SetUV(Output, CoordinateIndex, OutUV); +#else + SetUV(Output, CoordinateIndex, InPosition.xy); +#endif } UNROLL for (int CoordinateIndex = NUM_MATERIAL_TEXCOORDS; CoordinateIndex < NUM_TEX_COORD_INTERPOLATORS; CoordinateIndex++) @@ -249,8 +253,14 @@ void MainPS_ES2( // Grab emissive colour as output half3 EmissiveColor = GetMaterialEmissive(PixelMaterialInputs); - half4 FullSceneColor = half4(EmissiveColor, Parameters.BackupSceneColorAlpha); - + +#if MATERIAL_OUTPUT_OPACITY_AS_ALPHA + half Alpha = GetMaterialOpacity(PixelMaterialInputs); +#else + half Alpha = Parameters.BackupSceneColorAlpha; +#endif + half4 FullSceneColor = half4(EmissiveColor, Alpha); + #if POST_PROCESS_MATERIAL_BEFORE_TONEMAP #if OUTPUT_GAMMA_SPACE FullSceneColor.rgb = sqrt(FullSceneColor.rgb); diff --git a/Engine/Shaders/Private/RayTracing/RayTracingCommon.ush b/Engine/Shaders/Private/RayTracing/RayTracingCommon.ush index e58eba094bdd..0868879a9d98 100644 --- a/Engine/Shaders/Private/RayTracing/RayTracingCommon.ush +++ b/Engine/Shaders/Private/RayTracing/RayTracingCommon.ush @@ -159,6 +159,15 @@ FPackedMaterialClosestHitPayload PackRayTracingPayload(FMaterialClosestHitPayloa return Output; } +float3 UnpackRayTracingNormal(FPackedMaterialClosestHitPayload Input) +{ + float3 OutWorldNormal; + OutWorldNormal.x = f16tof32(Input.RadianceAndNormal[1] >> 16); + OutWorldNormal.y = f16tof32(Input.RadianceAndNormal[2]); + OutWorldNormal.z = f16tof32(Input.RadianceAndNormal[2] >> 16); + return OutWorldNormal; +} + FMaterialClosestHitPayload UnpackRayTracingPayload(FPackedMaterialClosestHitPayload Input, RayDesc Ray) { FMaterialClosestHitPayload Output = (FMaterialClosestHitPayload)0; diff --git a/Engine/Shaders/Private/RayTracing/RayTracingLightingCommon.ush b/Engine/Shaders/Private/RayTracing/RayTracingLightingCommon.ush index 4b78a601043a..9dc2316a7e00 100644 --- a/Engine/Shaders/Private/RayTracing/RayTracingLightingCommon.ush +++ b/Engine/Shaders/Private/RayTracing/RayTracingLightingCommon.ush @@ -18,6 +18,12 @@ bool IsHit(RayDesc Ray, FMaterialClosestHitPayload HitInfo) return t > Ray.TMin && t < Ray.TMax; } +bool IsHit(RayDesc Ray, FPackedMaterialClosestHitPayload HitInfo) +{ + float t = HitInfo.HitT; + return t > Ray.TMin && t < Ray.TMax; +} + float GetRoughnessFade(float Roughness, float MaxRoughness) { float RoughnessMaskScale = -2.0 / MaxRoughness; @@ -166,12 +172,129 @@ float3 SampleAreaLightDirection( return ShadowRayDirection; } -float3 ComputeDirectLighting( - in RayDesc Ray, - in FRayCone RayCone, - in FMaterialClosestHitPayload Payload, +FPackedMaterialClosestHitPayload TraceRayInternalPacked(in RaytracingAccelerationStructure TLAS, + in uint RayFlags, + in uint InstanceInclusionMask, + in uint RayContributionToHitGroupIndex, + in uint MultiplierForGeometryContributionToShaderIndex, + in uint MissShaderIndex, + in RayDesc Ray, + inout FRayCone RayCone) +{ + FPackedMaterialClosestHitPayload Payload = (FPackedMaterialClosestHitPayload)0; + Payload.RayCone = RayCone; + + TraceRay + ( + TLAS, + RayFlags, + InstanceInclusionMask, + RayContributionToHitGroupIndex, + MultiplierForGeometryContributionToShaderIndex, + MissShaderIndex, + Ray, + Payload + ); + + RayCone = Payload.RayCone; + return Payload; +} + +uint2 CullDirectLighting( + in RayDesc Ray, + in FRayCone RayCone, + in float3 WorldPosition, + in float3 WorldNormal, in RandomSequence RandSequence, - in bool bReflectedShadows, + in bool bReflectedShadows, + in float ShadowMaxNormalBias) +{ + + uint2 LightCullMask = (uint2)0; + + //#dxr_todo: think on the more efficient way of iterate through lights (split in light types, etc) + for (uint DirIndex = 0; DirIndex < RaytracingLightsDataPacked.Count; DirIndex++) + { + FDeferredLightData LightData = (FDeferredLightData)0; + uint Lit = 1; + + uint LightType = RaytracingLightsDataPacked.Type_LightProfileIndex_RectLightTextureIndex[DirIndex].x; + LightData.Position = RaytracingLightsDataPacked.LightPosition_InvRadius[DirIndex].xyz; + LightData.InvRadius = RaytracingLightsDataPacked.LightPosition_InvRadius[DirIndex].w; + LightData.Color = RaytracingLightsDataPacked.LightColor_SpecularScale[DirIndex].xyz; + LightData.FalloffExponent = RaytracingLightsDataPacked.Direction_FalloffExponent[DirIndex].w; + LightData.Direction = RaytracingLightsDataPacked.Direction_FalloffExponent[DirIndex].xyz; + LightData.Tangent = RaytracingLightsDataPacked.Tangent_SourceRadius[DirIndex].xyz; + LightData.SpotAngles = RaytracingLightsDataPacked.SpotAngles_SourceLength_SoftSourceRadius[DirIndex].xy; + LightData.SourceRadius = RaytracingLightsDataPacked.Tangent_SourceRadius[DirIndex].w; + LightData.SourceLength = RaytracingLightsDataPacked.SpotAngles_SourceLength_SoftSourceRadius[DirIndex].z; + LightData.SoftSourceRadius = RaytracingLightsDataPacked.SpotAngles_SourceLength_SoftSourceRadius[DirIndex].w; + LightData.SpecularScale = RaytracingLightsDataPacked.LightColor_SpecularScale[DirIndex].w; + LightData.RectLightBarnCosAngle = RaytracingLightsDataPacked.DistanceFadeMAD_RectLightBarnCosAngle_RectLightBarnLength[DirIndex].z; + LightData.RectLightBarnLength = RaytracingLightsDataPacked.DistanceFadeMAD_RectLightBarnCosAngle_RectLightBarnLength[DirIndex].w; + LightData.ContactShadowLength = 0.0; + LightData.DistanceFadeMAD = RaytracingLightsDataPacked.DistanceFadeMAD_RectLightBarnCosAngle_RectLightBarnLength[DirIndex].xy; + const uint LightProfileIndex = RaytracingLightsDataPacked.Type_LightProfileIndex_RectLightTextureIndex[DirIndex].y; + + + LightData.ShadowMapChannelMask = float4(0, 0, 0, 0); // #dxr_todo; + LightData.ShadowedBits = 0; // Not lit dynamic shadows + LightData.ContactShadowLengthInWS = false; + + LightData.bRadialLight = (LightType != LIGHT_TYPE_DIRECTIONAL); + LightData.bSpotLight = (LightType == LIGHT_TYPE_SPOT); + LightData.bRectLight = (LightType == LIGHT_TYPE_RECT); + + if (LightType == LIGHT_TYPE_DIRECTIONAL) + { + LightData.bInverseSquared = false; + } + else + { + LightData.bInverseSquared = LightData.FalloffExponent == 0; + } + + + float3 ShadowRayDirection; + // ToLight should not be normalized because its length is used to compute the shadow ray TMax + float3 ToLight = LightData.Position - WorldPosition; + float LightMask = 1.0; + + if (LightType == LIGHT_TYPE_DIRECTIONAL) + { + ShadowRayDirection = LightData.Direction; + ToLight = LightData.Direction * 100000.0f; + } + else + { + LightMask = GetLocalLightAttenuation(WorldPosition, LightData, ToLight, ShadowRayDirection); + + // Skip the light sample that does not contribute anything due to attenuation. + if (LightMask <= 0.0) + { + Lit = 0; + } + } + + // Skip the light sample pointing backwards + if (dot(WorldNormal, normalize(ToLight)) <= 0) + { + Lit = 0; + } + + LightCullMask[DirIndex/32] |= Lit << (DirIndex%32); + } + + return LightCullMask; +} + +float3 ComputeDirectLightingCulled( + in uint2 LightCullMask, + in RayDesc Ray, + in FRayCone RayCone, + in FMaterialClosestHitPayload Payload, + in RandomSequence RandSequence, + in bool bReflectedShadows, in float ShadowMaxNormalBias) { float3 DirectLighting = float3(0.0, 0.0, 0.0); @@ -197,43 +320,83 @@ float3 ComputeDirectLighting( GBufferData.DiffuseColor = Payload.DiffuseColor; FRectTexture RectTexture; - #if USE_SOURCE_TEXTURE_ARRAY - RectTexture.SourceTexture0 = RaytracingLightsData.RectLightTexture0; - RectTexture.SourceTexture1 = RaytracingLightsData.RectLightTexture1; - RectTexture.SourceTexture2 = RaytracingLightsData.RectLightTexture2; - RectTexture.SourceTexture3 = RaytracingLightsData.RectLightTexture3; - RectTexture.SourceTexture4 = RaytracingLightsData.RectLightTexture4; - RectTexture.SourceTexture5 = RaytracingLightsData.RectLightTexture5; - RectTexture.SourceTexture6 = RaytracingLightsData.RectLightTexture6; - RectTexture.SourceTexture7 = RaytracingLightsData.RectLightTexture7; +#if USE_SOURCE_TEXTURE_ARRAY + RectTexture.SourceTexture0 = RaytracingLightsDataPacked.RectLightTexture0; + RectTexture.SourceTexture1 = RaytracingLightsDataPacked.RectLightTexture1; + RectTexture.SourceTexture2 = RaytracingLightsDataPacked.RectLightTexture2; + RectTexture.SourceTexture3 = RaytracingLightsDataPacked.RectLightTexture3; + RectTexture.SourceTexture4 = RaytracingLightsDataPacked.RectLightTexture4; + RectTexture.SourceTexture5 = RaytracingLightsDataPacked.RectLightTexture5; + RectTexture.SourceTexture6 = RaytracingLightsDataPacked.RectLightTexture6; + RectTexture.SourceTexture7 = RaytracingLightsDataPacked.RectLightTexture7; RectTexture.SourceTextureIndex = 99; - #elif USE_SOURCE_TEXTURE - RectTexture = InitRectTexture(RaytracingLightsData.RectLightTexture0); - #else - RectTexture = (FRectTexture)0; - #endif +#elif USE_SOURCE_TEXTURE + RectTexture = InitRectTexture(RaytracingLightsDataPacked.RectLightTexture0); +#else + RectTexture = (FRectTexture)0; +#endif + + uint DirIndex = 0; //#dxr_todo: think on the more efficient way of iterate through lights (split in light types, etc) - for (uint DirIndex = 0; DirIndex < RaytracingLightsData.Count; DirIndex++) + //while(LightCullMask.x || LightCullMask.y) + while (WaveActiveAnyTrue(LightCullMask.x || LightCullMask.y)) { - FDeferredLightData LightData = (FDeferredLightData)0; + const bool Active = LightCullMask.x || LightCullMask.y; - uint LightType = RaytracingLightsData.Type[DirIndex]; - LightData.Position = RaytracingLightsData.LightPosition[DirIndex]; - LightData.InvRadius = RaytracingLightsData.LightInvRadius[DirIndex]; - LightData.Color = RaytracingLightsData.LightColor[DirIndex]; - LightData.FalloffExponent = RaytracingLightsData.LightFalloffExponent[DirIndex]; - LightData.Direction = RaytracingLightsData.Direction[DirIndex]; - LightData.Tangent = RaytracingLightsData.Tangent[DirIndex]; - LightData.SpotAngles = RaytracingLightsData.SpotAngles[DirIndex]; - LightData.SourceRadius = RaytracingLightsData.SourceRadius[DirIndex]; - LightData.SourceLength = RaytracingLightsData.SourceLength[DirIndex]; - LightData.SoftSourceRadius = RaytracingLightsData.SoftSourceRadius[DirIndex]; - LightData.SpecularScale = RaytracingLightsData.SpecularScale[DirIndex]; - LightData.RectLightBarnCosAngle = RaytracingLightsData.RectLightBarnCosAngle[DirIndex]; - LightData.RectLightBarnLength = RaytracingLightsData.RectLightBarnLength[DirIndex]; + if (Active) + { + if ((LightCullMask.x & 0x1) == 0) + { + uint Shift = LightCullMask.x ? firstbitlow(LightCullMask.x) : 32; + DirIndex += Shift; + LightCullMask.x >>= Shift; + + //fill in the top of LightCullMask.x + LightCullMask.x |= LightCullMask.y << (32 - Shift); + LightCullMask.y >>= Shift; + + //may need to try once more + if ((LightCullMask.x & 0x1) == 0) + { + uint Shift = LightCullMask.x ? firstbitlow(LightCullMask.x) : 32; + DirIndex += Shift; + LightCullMask.x >>= Shift; + + //fill in the top of LightCullMask.x + LightCullMask.x |= LightCullMask.y << (32 - Shift); + LightCullMask.y >>= Shift; + } + } + } + else + { + DirIndex = 0; + } + + FDeferredLightData LightData = (FDeferredLightData)0; + uint LightType = RaytracingLightsDataPacked.Type_LightProfileIndex_RectLightTextureIndex[DirIndex].x; + LightData.Position = RaytracingLightsDataPacked.LightPosition_InvRadius[DirIndex].xyz; + LightData.InvRadius = RaytracingLightsDataPacked.LightPosition_InvRadius[DirIndex].w; + LightData.Color = RaytracingLightsDataPacked.LightColor_SpecularScale[DirIndex].xyz; + LightData.FalloffExponent = RaytracingLightsDataPacked.Direction_FalloffExponent[DirIndex].w; + LightData.Direction = RaytracingLightsDataPacked.Direction_FalloffExponent[DirIndex].xyz; + LightData.Tangent = RaytracingLightsDataPacked.Tangent_SourceRadius[DirIndex].xyz; + LightData.SpotAngles = RaytracingLightsDataPacked.SpotAngles_SourceLength_SoftSourceRadius[DirIndex].xy; + LightData.SourceRadius = RaytracingLightsDataPacked.Tangent_SourceRadius[DirIndex].w; + LightData.SourceLength = RaytracingLightsDataPacked.SpotAngles_SourceLength_SoftSourceRadius[DirIndex].z; + LightData.SoftSourceRadius = RaytracingLightsDataPacked.SpotAngles_SourceLength_SoftSourceRadius[DirIndex].w; + LightData.SpecularScale = RaytracingLightsDataPacked.LightColor_SpecularScale[DirIndex].w; + LightData.RectLightBarnCosAngle = RaytracingLightsDataPacked.DistanceFadeMAD_RectLightBarnCosAngle_RectLightBarnLength[DirIndex].z; + LightData.RectLightBarnLength = RaytracingLightsDataPacked.DistanceFadeMAD_RectLightBarnCosAngle_RectLightBarnLength[DirIndex].w; LightData.ContactShadowLength = 0.0; - LightData.DistanceFadeMAD = RaytracingLightsData.DistanceFadeMAD[DirIndex]; + LightData.DistanceFadeMAD = RaytracingLightsDataPacked.DistanceFadeMAD_RectLightBarnCosAngle_RectLightBarnLength[DirIndex].xy; + + #if USE_SOURCE_TEXTURE_ARRAY + RectTexture.SourceTextureIndex = RaytracingLightsDataPacked.Type_LightProfileIndex_RectLightTextureIndex[DirIndex].z; + #endif + uint LightProfileIndex = RaytracingLightsDataPacked.Type_LightProfileIndex_RectLightTextureIndex[DirIndex].y; + LightData.ShadowMapChannelMask = float4(0, 0, 0, 0); // #dxr_todo; LightData.ShadowedBits = 0; // Not lit dynamic shadows LightData.ContactShadowLengthInWS = false; @@ -242,10 +405,6 @@ float3 ComputeDirectLighting( LightData.bSpotLight = (LightType == LIGHT_TYPE_SPOT); LightData.bRectLight = (LightType == LIGHT_TYPE_RECT); - #if USE_SOURCE_TEXTURE_ARRAY - RectTexture.SourceTextureIndex = RaytracingLightsData.RectLightTextureIndex[DirIndex]; - #endif - if (LightType == LIGHT_TYPE_DIRECTIONAL) { LightData.bInverseSquared = false; @@ -258,7 +417,7 @@ float3 ComputeDirectLighting( float4 LightAttenuation = 1.0f; float LightProfileMultiplier = 1.0; - uint LightProfileIndex = RaytracingLightsData.LightProfileIndex[DirIndex]; + if (LightProfileIndex >= 0) { LightProfileMultiplier = ComputeLightProfileMultiplier(WorldPosition, LightData.Position, LightData.Direction, LightProfileIndex); @@ -277,18 +436,6 @@ float3 ComputeDirectLighting( else { LightMask = GetLocalLightAttenuation(WorldPosition, LightData, ToLight, ShadowRayDirection); - - // Skip the light sample that does not contribute anything due to attenuation. - if (LightMask <= 0.0) - { - continue; - } - } - - // Skip the light sample pointing backwards - if (dot(Payload.WorldNormal, normalize(ToLight)) <= 0) - { - continue; } //#dxr_todo: check perf improvements forcing TMin == TMax when bReflectedShadows = false, to avoid tracing rays inside if statements @@ -306,21 +453,30 @@ float3 ComputeDirectLighting( ShadowRay.Origin = WorldPosition; ShadowRay.Direction = ShadowRayDirection; ShadowRay.TMin = 1e-4f; - ShadowRay.TMax = length(ToLight); + ShadowRay.TMax = Active ? length(ToLight) : ShadowRay.TMin; ApplyPositionBias(ShadowRay, Payload.WorldNormal, ShadowMaxNormalBias); float HitT = TraceShadowRay(ShadowRay, ShadowRayCone); - AmbientOcclusion = HitT < 0.0f; + AmbientOcclusion = HitT < 0.0f && Active; } - float3 LightContribution = GetDynamicLighting(WorldPosition, -CameraVector, GBufferData, AmbientOcclusion, GBufferData.ShadingModelID, LightData, LightAttenuation, 0.5, uint2(0, 0), RectTexture).xyz; - DirectLighting += LightContribution * LightProfileMultiplier; + if (Active) + { + float3 LightContribution = GetDynamicLighting(WorldPosition, -CameraVector, GBufferData, AmbientOcclusion, GBufferData.ShadingModelID, LightData, LightAttenuation, 0.5, uint2(0, 0), RectTexture).xyz; + DirectLighting += LightContribution * LightProfileMultiplier; + } + + DirIndex += 1; + LightCullMask.x >>= 1; + + LightCullMask.x |= LightCullMask.y << 31; + LightCullMask.y >>= 1; } return DirectLighting; } -void ApplyViewDependentMaterialPayloadModifications(RayDesc Ray, inout FMaterialClosestHitPayload Payload) +void ComputeBottomLayerMaterialProperties(RayDesc Ray, inout FMaterialClosestHitPayload Payload) { if (Payload.ShadingModelID == SHADINGMODELID_CLEAR_COAT) { @@ -347,7 +503,7 @@ void ApplyViewDependentMaterialPayloadModifications(RayDesc Ray, inout FMaterial } } -FMaterialClosestHitPayload TraceRayAndAccumulateResults( +FMaterialClosestHitPayload TraceRayResults( in RayDesc Ray, in RandomSequence RandSequence, in uint RayFlags, @@ -356,27 +512,42 @@ FMaterialClosestHitPayload TraceRayAndAccumulateResults( inout FRayCone RayCone, inout float3 Radiance) { - FMaterialClosestHitPayload Payload = TraceRayInternal( - TLAS, // AccelerationStructure + FPackedMaterialClosestHitPayload PackedPayload = TraceRayInternalPacked( + TLAS, // AccelerationStructure RayFlags, Mask, - RAY_TRACING_SHADER_SLOT_MATERIAL, // RayContributionToHitGroupIndex - RAY_TRACING_NUM_SHADER_SLOTS, // MultiplierForGeometryContributionToShaderIndex - 0, // MissShaderIndex - Ray, // RayDesc + RAY_TRACING_SHADER_SLOT_MATERIAL, // RayContributionToHitGroupIndex + RAY_TRACING_NUM_SHADER_SLOTS, // MultiplierForGeometryContributionToShaderIndex + 0, // MissShaderIndex + Ray, // RayDesc RayCone ); + FMaterialClosestHitPayload Payload = UnpackRayTracingPayload(PackedPayload, Ray); + return Payload; +} +void AccumulateResults( + in FMaterialClosestHitPayload Payload, + in RayDesc Ray, + in RandomSequence RandSequence, + in uint RayFlags, + in float ShadowMaxNormalBias, + in uint Mask, + inout FRayCone RayCone, + inout float3 Radiance) +{ if (IsHit(Ray, Payload)) { - ApplyViewDependentMaterialPayloadModifications(Ray, Payload); + uint2 LightCullMask = (uint2)0; + const float3 WorldPosition = Ray.Origin + Payload.HitT * Ray.Direction; + LightCullMask = CullDirectLighting(Ray, RayCone, WorldPosition, Payload.WorldNormal, RandSequence, ReflectedShadowsType > 0, ShadowMaxNormalBias); Payload.DiffuseColor = Payload.BaseColor - Payload.BaseColor * Payload.Metallic; Payload.SpecularColor = ComputeF0(Payload.Specular, Payload.BaseColor, Payload.Metallic); if (ShouldDoDirectLighting) { - float3 DirectLighting = ComputeDirectLighting(Ray, RayCone, Payload, RandSequence, ReflectedShadowsType > 0, ShadowMaxNormalBias); + float3 DirectLighting = ComputeDirectLightingCulled(LightCullMask, Ray, RayCone, Payload, RandSequence, ReflectedShadowsType > 0, ShadowMaxNormalBias); // Transform NaNs to black, transform negative colors to black. DirectLighting = -min(-DirectLighting, 0.0); @@ -390,6 +561,68 @@ FMaterialClosestHitPayload TraceRayAndAccumulateResults( Radiance += Payload.DiffuseColor * Payload.IndirectIrradiance; } } +} + +FMaterialClosestHitPayload TraceRayAndAccumulateResults( + in RayDesc Ray, + in RandomSequence RandSequence, + in uint RayFlags, + in float ShadowMaxNormalBias, + in uint Mask, + inout FRayCone RayCone, + inout float3 Radiance) +{ + FMaterialClosestHitPayload Payload = TraceRayResults( + Ray, + RandSequence, + RayFlags, + ShadowMaxNormalBias, + Mask, + RayCone, + Radiance); + + AccumulateResults( + Payload, + Ray, + RandSequence, + RayFlags, + ShadowMaxNormalBias, + Mask, + RayCone, + Radiance); return Payload; } + +FMaterialClosestHitPayload TraceRayAndAccumulateBottomLayerResults( + in RayDesc Ray, + in RandomSequence RandSequence, + in uint RayFlags, + in float ShadowMaxNormalBias, + in uint Mask, + inout FRayCone RayCone, + inout float3 Radiance) +{ + FMaterialClosestHitPayload BottomLayerPayload = TraceRayResults( + Ray, + RandSequence, + RayFlags, + ShadowMaxNormalBias, + Mask, + RayCone, + Radiance); + + ComputeBottomLayerMaterialProperties(Ray, BottomLayerPayload); + + AccumulateResults( + BottomLayerPayload, + Ray, + RandSequence, + RayFlags, + ShadowMaxNormalBias, + Mask, + RayCone, + Radiance); + + return BottomLayerPayload; +} diff --git a/Engine/Shaders/Private/RayTracing/RayTracingOcclusionRGS.usf b/Engine/Shaders/Private/RayTracing/RayTracingOcclusionRGS.usf index b646598407bc..4f80c29671ae 100644 --- a/Engine/Shaders/Private/RayTracing/RayTracingOcclusionRGS.usf +++ b/Engine/Shaders/Private/RayTracing/RayTracingOcclusionRGS.usf @@ -161,6 +161,18 @@ void OcclusionRGS() LocalSamplesPerPixel = 0.0; } + // Cheap solution for double sided foliage to have valid lighing through the denoiser + if (GBufferData.ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE) + { + #if LIGHT_TYPE == LIGHT_TYPE_DIRECTIONAL + const float3 LightDirection = LightParameters.Direction; + #else + const float3 LightDirection = normalize(LightParameters.Position - WorldPosition); + #endif + if (dot(LightDirection, WorldNormal) < 0) + WorldNormal = -WorldNormal; + } + LOOP for (uint SampleIndex = 0; SampleIndex < LocalSamplesPerPixel; ++SampleIndex) { @@ -197,6 +209,7 @@ void OcclusionRGS() } else if (dot(WorldNormal, Ray.Direction) <= 0.0) { + ClosestRayDistance = 0.001; continue; } diff --git a/Engine/Shaders/Private/RayTracing/RayTracingReflections.usf b/Engine/Shaders/Private/RayTracing/RayTracingReflections.usf index b10411647f5f..fbe967433e17 100644 --- a/Engine/Shaders/Private/RayTracing/RayTracingReflections.usf +++ b/Engine/Shaders/Private/RayTracing/RayTracingReflections.usf @@ -24,10 +24,10 @@ float2 SobolIndexToUniformUnitSquare(uint2 SobolRand) #define USE_SOURCE_TEXTURE 1 #define USE_SOURCE_TEXTURE_ARRAY 1 -#define LTCMatTexture RaytracingLightsData.LTCMatTexture -#define LTCMatSampler RaytracingLightsData.LTCMatSampler -#define LTCAmpTexture RaytracingLightsData.LTCAmpTexture -#define LTCAmpSampler RaytracingLightsData.LTCAmpSampler +#define LTCMatTexture RaytracingLightsDataPacked.LTCMatTexture +#define LTCMatSampler RaytracingLightsDataPacked.LTCMatSampler +#define LTCAmpTexture RaytracingLightsDataPacked.LTCAmpTexture +#define LTCAmpSampler RaytracingLightsDataPacked.LTCAmpSampler #define PreIntegratedGF ReflectionStruct.PreIntegratedGF #define PreIntegratedGFSampler ReflectionStruct.PreIntegratedGFSampler @@ -67,6 +67,7 @@ RaytracingAccelerationStructure TLAS; RWTexture2D ColorOutput; RWTexture2D RayHitDistanceOutput; +RWTexture2D RayImaginaryDepthOutput; // Material buffer and tile size are used for sorted materials. @@ -82,10 +83,46 @@ uint2 GetPixelCoord(uint2 DispatchThreadId) return DispatchThreadId * UpscaleFactor + uint2(SubPixelId & (UpscaleFactor - 1), SubPixelId / UpscaleFactor); } +// Generate a random direction to sample according to world normal and roughness. +float3 GenerateReflectionDirection(inout RandomSequence RandSequence, uint2 DispatchThreadId, uint BounceIndex, uint SamplePerPixel, float3 IncidentDirection, float3 WorldNormal, float Roughness) +{ + uint DummyVariable; + int RetryCount = 0; + float3 outDirection; + do + { + float2 RandSample = RandomSequence_GenerateSample2D(RandSequence, DummyVariable); + + // Sobol can only be used on the first sample of the first bounce + if (BounceIndex == 0 && SamplePerPixel == 1 && RetryCount == 0) + { + uint2 SobolFrame = ComputePixelUniqueSobolRandSample(DispatchThreadId); + RandSample = SobolIndexToUniformUnitSquare(SobolFrame); + } + + outDirection = GenerateReflectedRayDirection(IncidentDirection, WorldNormal, Roughness, RandSample); + } while (dot(WorldNormal, outDirection) < 0.0 && ++RetryCount < 16); + + return outDirection; +} + +// Helper function to ensure a reflection direction is not pointing inside a geometry. +// It uses the geometry smooth normal to reflect R out of it. +void FixSampleDirectionIfNeeded(float3 SmoothSurfaceNormal, inout float3 SampleDirection) +{ + if (dot(SmoothSurfaceNormal, SampleDirection) < 0.0) + { + // The sampling direction is pointing towards the surface, so revert it along the normal axis. + SampleDirection = SampleDirection - 2.0f * dot(SmoothSurfaceNormal, SampleDirection) * SmoothSurfaceNormal; + } +} [shader("raygeneration")] void RayTracingReflectionsRGS() { + uint DummyVariable; + const float ClearCoatSpecularColor = 0.04; // See ReflectionEnvironmentPixelShader.usf + FDeferredMaterialPayload DeferredMaterialPayload; DeferredMaterialPayload.SortKey = RAY_TRACING_DEFERRED_MATERIAL_KEY_INVALID; @@ -163,25 +200,27 @@ void RayTracingReflectionsRGS() float3 CameraOrigin = ReconstructWorldPositionFromDepth(UV, 0.0f); float3 WorldPosition = ReconstructWorldPositionFromDepth(UV, Depth); float3 CameraDirection = normalize(WorldPosition - CameraOrigin); - float3 WorldNormal = ScreenSpaceData.GBuffer.WorldNormal; - float Roughness = ScreenSpaceData.GBuffer.Roughness; - float3 SpecularColor = ScreenSpaceData.GBuffer.SpecularColor; float3 IncidentDirection = CameraDirection; - float ClearCoat = 1.0f; - float ClearCoatRoughness = 1.0f; - #if CLEAR_COAT_BOTTOM_NORMAL - float3 ClearCoatBottomNormal = WorldNormal; - #endif - + uint ShadingModelID = ScreenSpaceData.GBuffer.ShadingModelID; + float3 TopLayerWorldNormal = ScreenSpaceData.GBuffer.WorldNormal; + float TopLayerRoughness = ScreenSpaceData.GBuffer.Roughness; + float3 TopLayerSpecularColor = ScreenSpaceData.GBuffer.SpecularColor; + + float ClearCoat = 1.0f; + float BottomLayerRoughness = TopLayerRoughness; + float3 BottomLayerSpecularColor = TopLayerSpecularColor; + float3 BottomLayerWorldNormal = TopLayerWorldNormal; if (ShadingModelID == SHADINGMODELID_CLEAR_COAT) { - ClearCoat = ScreenSpaceData.GBuffer.CustomData.x; - ClearCoatRoughness = ScreenSpaceData.GBuffer.CustomData.y; #if CLEAR_COAT_BOTTOM_NORMAL - const float2 oct1 = ((float2(ScreenSpaceData.GBuffer.CustomData.a, ScreenSpaceData.GBuffer.CustomData.z) * 2) - (256.0/255.0)) + UnitVectorToOctahedron(ScreenSpaceData.GBuffer.WorldNormal); - ClearCoatBottomNormal = OctahedronToUnitVector(oct1); + const float2 oct1 = ((float2(ScreenSpaceData.GBuffer.CustomData.a, ScreenSpaceData.GBuffer.CustomData.z) * 2) - (256.0/255.0)) + UnitVectorToOctahedron(TopLayerWorldNormal); + BottomLayerWorldNormal = OctahedronToUnitVector(oct1); #endif + + ClearCoat = ScreenSpaceData.GBuffer.CustomData.x; // Clear coat weight + TopLayerRoughness = ScreenSpaceData.GBuffer.CustomData.y; // Clear coat roughness + TopLayerSpecularColor = ClearCoatSpecularColor; // Hard coded top layer specular color } FRayCone RayCone = (FRayCone)0; @@ -189,68 +228,27 @@ void RayTracingReflectionsRGS() float SurfaceCurvature = 0.0f; /* #todo_dxr assume no curvature */ RayCone = PropagateRayCone(RayCone, SurfaceCurvature, Depth); - uint DummyVariable; - - // Add one more 'fake bounce' and we will evaluate cubemap there - for (uint BounceIndex = 0; BounceIndex < MaxBounces + 1; ++BounceIndex) + // + // Reflections are trace by always following the top layer reflection vector. This is recursive and up to MaxBounces recursion. + // In the case of coated material, we also trace a reflection ray for the bottom layer which can have different normal and roughness. This is not recursive. + // We fix normal pointing away towards the geometry by assuming coated layer has smooth normal (assuming it is the mesh smooth normal). + // Fog is applied as a function of the top layer path, for both top and botto; refelctions combined (as an optimisation approximation). + // + for (uint BounceIndex = 0; BounceIndex < MaxBounces; ++BounceIndex) { - float NoV = saturate(dot(-IncidentDirection, WorldNormal)); - float FinalRoughness = Roughness; - if (ShadingModelID == SHADINGMODELID_CLEAR_COAT) - { - float F = EnvBRDF( 0.04, ClearCoatRoughness, NoV ).x; - F *= ClearCoat; - - float w = RandomSequence_GenerateSample1D(RandSequence, DummyVariable); - - if (w <= F) - { - SpecularColor = 0.04; - FinalRoughness = ClearCoatRoughness; - } - else - { - #if CLEAR_COAT_BOTTOM_NORMAL - WorldNormal = ClearCoatBottomNormal; - NoV = saturate(dot(-IncidentDirection, WorldNormal)); - #endif - PathThroughput *= EnvBRDF( SpecularColor, Roughness, NoV ); - } - } - else - { - // If it is not clear coat, and we're on the first bounce, the G F terms will be applied by ReflectionEnvironmentPixelShader - if (BounceIndex > 0) - PathThroughput *= EnvBRDF( SpecularColor, Roughness, NoV ); - } + // Prepare a ray to trace the top layer reflection + RayDesc TopLayerRay; + TopLayerRay.Origin = WorldPosition; + TopLayerRay.TMin = 0.0; + TopLayerRay.TMax = LocalMaxRayDistance; - RayDesc Ray; - Ray.Origin = WorldPosition; - Ray.TMin = 0.0; - Ray.TMax = LocalMaxRayDistance; - - int RetryCount = 0; - do + TopLayerRay.Direction = GenerateReflectionDirection(RandSequence, DispatchThreadId, BounceIndex, LocalSamplesPerPixel, IncidentDirection, TopLayerWorldNormal, TopLayerRoughness); + ApplyPositionBias(TopLayerRay, TopLayerWorldNormal, ReflectionMaxNormalBias); + if (BounceIndex == 0) { - float2 RandSample = RandomSequence_GenerateSample2D(RandSequence, DummyVariable); - - // Sobol can only be used on the first sample of the first bounce - if (BounceIndex == 0 && LocalSamplesPerPixel == 1 && RetryCount == 0) - { - uint2 SobolFrame = ComputePixelUniqueSobolRandSample(DispatchThreadId); - RandSample = SobolIndexToUniformUnitSquare(SobolFrame); - } - - Ray.Direction = GenerateReflectedRayDirection(IncidentDirection, WorldNormal, FinalRoughness, RandSample); - } while (dot(WorldNormal, Ray.Direction) < 0.0 && ++RetryCount < 16); - - ApplyPositionBias(Ray, WorldNormal, ReflectionMaxNormalBias); - - if (dot(WorldNormal, Ray.Direction) < 0.0) - { - // Refraction rays are handled separately - bIsValidSample = false; - break; + // This fixes black reflexions due to R pointing into the surface. + // It assumes that the clear coat relies on a smooth normal following the mesh surface. + FixSampleDirectionIfNeeded(ScreenSpaceData.GBuffer.WorldNormal, TopLayerRay.Direction); } //#dxr-todo: apply heuristics to do different shading computations depending on BounceIndex @@ -261,13 +259,13 @@ void RayTracingReflectionsRGS() { uint RayFlags = RAY_FLAG_CULL_BACK_FACING_TRIANGLES; TraceRay( - TLAS, // AccelerationStructure + TLAS, // AccelerationStructure RayFlags, - RAY_TRACING_MASK_OPAQUE, // InstanceInclusionMask -- NOTE: Reflection rays are traced only against opaque geometry. Blended geometry needs special handling (#dxr_todo). - RAY_TRACING_SHADER_SLOT_MATERIAL, // RayContributionToHitGroupIndex - RAY_TRACING_NUM_SHADER_SLOTS, // MultiplierForGeometryContributionToShaderIndex - 0, // MissShaderIndex - Ray, // RayDesc + RAY_TRACING_MASK_OPAQUE, // InstanceInclusionMask -- NOTE: Reflection rays are traced only against opaque geometry. Blended geometry needs special handling (#dxr_todo). + RAY_TRACING_SHADER_SLOT_MATERIAL, // RayContributionToHitGroupIndex + RAY_TRACING_NUM_SHADER_SLOTS, // MultiplierForGeometryContributionToShaderIndex + 0, // MissShaderIndex + TopLayerRay, // RayDesc DeferredMaterialPayload ); @@ -278,60 +276,58 @@ void RayTracingReflectionsRGS() if (DeferredMaterialPayload.SortKey < RAY_TRACING_DEFERRED_MATERIAL_KEY_RAY_MISS) { const float ShortRayLength = 1.0f; // 1cm is arbitrarily chosen - Ray.TMin = max(0, DeferredMaterialPayload.HitT - ShortRayLength * 0.5f); + TopLayerRay.TMin = max(0, DeferredMaterialPayload.HitT - ShortRayLength * 0.5f); // TMax can only be used if alpha masked materials are handled during initial ray tracing (material gathering) // Ray.TMax = DeferredMaterialPayload.HitT + ShortRayLength * 0.5f; } else { - Ray.TMax = 0; + TopLayerRay.TMax = 0; } } } - float3 Radiance = float3(0, 0, 0); + + + // Trace the top layer reflection + float3 TopLayerRadiance = float3(0, 0, 0); FMaterialClosestHitPayload Payload; + const uint RayFlags = 0; + Payload = TraceRayAndAccumulateResults(TopLayerRay, RandSequence, RayFlags, ReflectionMaxNormalBias, RAY_TRACING_MASK_OPAQUE, RayCone, TopLayerRadiance); + bool isTopLayerRayValid = Payload.HitT >= 0 || (bAllowSkySampling && ReflectionStruct.SkyLightParameters.y > 0); - // Force the ray to miss on the MaxBounces + 1 fake bounce, so that we fallback to cubemap there - if (BounceIndex < MaxBounces) - { - const uint RayFlags = 0; - Payload = TraceRayAndAccumulateResults(Ray, RandSequence, RayFlags, ReflectionMaxNormalBias, RAY_TRACING_MASK_OPAQUE, RayCone, Radiance); - } + // Compute some flags and values bool bApplyHeightFog = false; + bool bSkyWasHit = false; float3 OriginToCollider = 0.0f; - - // Force the ray to miss on the MaxBounces + 1 fake bounce, so that we fallback to cubemap there - float3 PathSegmentRadiance = 0.0f; - if (Payload.HitT >= 0 && BounceIndex < MaxBounces) + if (Payload.HitT >= 0) { - PathSegmentRadiance += Radiance; bIsValidSample = true; - if (BounceIndex == 0) { HitDistance = Payload.HitT; } bApplyHeightFog = HeightFog > 0; - OriginToCollider = Payload.WorldPos - Ray.Origin; + OriginToCollider = Payload.WorldPos - TopLayerRay.Origin; } else if (bAllowSkySampling && ReflectionStruct.SkyLightParameters.y > 0) { float SkyAverageBrightness = 1.0f; - float3 SkyLighting = GetSkyLightReflection(Ray.Direction, FinalRoughness, SkyAverageBrightness); - PathSegmentRadiance += SkyLighting; + float3 SkyLighting = GetSkyLightReflection(TopLayerRay.Direction, TopLayerRoughness, SkyAverageBrightness); + TopLayerRadiance = SkyLighting; bIsValidSample = true; + bSkyWasHit = true; // To stop the main path recusion early bApplyHeightFog = HeightFog > 0; const float SkyFarDistance = 100000.0f; - OriginToCollider = Ray.Direction * SkyFarDistance; + OriginToCollider = TopLayerRay.Direction * SkyFarDistance; if (BounceIndex == 0) { - // To tell the denoiser the black input is valid even if no valid ray trace result was found. Uses a high ray hit distance + // To tell the denoiser the sky radiance is valid even if no valid ray trace result was found. Uses a high ray hit distance // so that the sample may be reused by another neighbor pixel in the denoise as if RoughnessFade in ]0;1[. HitDistance = 1.0e20; } @@ -343,40 +339,127 @@ void RayTracingReflectionsRGS() // Tell to the denoiser this sample reached outer bounds of the scene without any intersection. HitDistance = -1; } + TopLayerRadiance = 0; } + float3 BottomLayerRadiance = float3(0, 0, 0); + float3 TopLayerReflectionEventThroughput = float3(1, 1, 1); + float NoV = saturate(dot(-IncidentDirection, TopLayerWorldNormal)); + BRANCH + if (ShadingModelID == SHADINGMODELID_CLEAR_COAT) + { + // Trace reflection ray for the bottom layer + RayDesc BottomLayerRay; + BottomLayerRay.Origin = WorldPosition; + BottomLayerRay.TMin = 0.0; + BottomLayerRay.TMax = LocalMaxRayDistance; + BottomLayerRay.Direction = GenerateReflectionDirection(RandSequence, DispatchThreadId, BounceIndex, LocalSamplesPerPixel, IncidentDirection, BottomLayerWorldNormal, BottomLayerRoughness); + ApplyPositionBias(BottomLayerRay, BottomLayerWorldNormal, ReflectionMaxNormalBias); + if (BounceIndex == 0 && dot(ScreenSpaceData.GBuffer.WorldNormal, BottomLayerRay.Direction) < 0.0) + { + FixSampleDirectionIfNeeded(ScreenSpaceData.GBuffer.WorldNormal, BottomLayerRay.Direction); + } + + + FRayCone BottomLayerRayCone = (FRayCone)0; + FMaterialClosestHitPayload BottomLayerPayload = TraceRayAndAccumulateBottomLayerResults(BottomLayerRay, RandSequence, RayFlags, ReflectionMaxNormalBias, RAY_TRACING_MASK_OPAQUE, RayCone, BottomLayerRadiance); + + // Distance and sky: similar processing to main ray. + float BottomLayerHitDistance = -1; + bool IsBottomSampleValid = false; + if (BottomLayerPayload.HitT >= 0) + { + IsBottomSampleValid = true; + BottomLayerHitDistance = BottomLayerPayload.HitT; + } + else if (bAllowSkySampling && ReflectionStruct.SkyLightParameters.y > 0) + { + IsBottomSampleValid = true; + float SkyAverageBrightness = 1.0f; + float3 SkyLighting = GetSkyLightReflection(BottomLayerRay.Direction, BottomLayerRoughness, SkyAverageBrightness); + BottomLayerRadiance += SkyLighting; + BottomLayerHitDistance = 1.0e20; + } + + if (BounceIndex == 0) + { + HitDistance = IsBottomSampleValid ? min(bIsValidSample ? HitDistance : 1.0e27f, BottomLayerHitDistance) : HitDistance; + } + + + // Apply correct weights to the bottom and top layers (See ReflectionEnvironmentPixelShader) + { + float NoVBottom = saturate(dot(-IncidentDirection, BottomLayerWorldNormal)); + + float TopLayerFSChlick = EnvBRDF(TopLayerSpecularColor, TopLayerRoughness, NoV).x; + float BottomLayerFSChlick = EnvBRDF(BottomLayerSpecularColor, BottomLayerRoughness, NoVBottom).x; + + float TopLayerRefractionTransmittance = (1 - TopLayerFSChlick); + + float2 AB = PreIntegratedGF.SampleLevel(PreIntegratedGFSampler, float2(NoV, TopLayerRoughness), 0).rg; + float3 TopLayerSpecularLobIntegral = TopLayerSpecularColor * AB.x + AB.y * saturate(50.0 * TopLayerSpecularColor.g) * (1 - ClearCoat); + + // dxr-todo: double check the math when we can compare with path tracer. + // Matches what is found in ReflectionEnvironmentPixelShader.usf. + //PathSegmentRadiance.rgb = ClearCoat * (TopLayerRadiance * TopLayerSpecularLobIntegral * TopLayerRefractionTransmittance + TopLayerRadiance * TopLayerFSChlick); + TopLayerReflectionEventThroughput = ClearCoat * (TopLayerSpecularLobIntegral * TopLayerRefractionTransmittance + TopLayerFSChlick); + float BottomToTopRefractionThroughput = 1.0; // This is already evaluated in TraceRayAndAccumulateBottomLayerResults + BottomLayerRadiance = BottomLayerRadiance * BottomLayerFSChlick * BottomToTopRefractionThroughput; // Bottom layer refelciton accounts for its throughput. + } + } + else + { + // If it is not clear coat, and we are on the first bounce, the G F terms will be applied by ReflectionEnvironmentPixelShader + if (BounceIndex > 0) + TopLayerReflectionEventThroughput *= EnvBRDF(TopLayerSpecularColor, TopLayerRoughness, NoV); + } + + + // Apply height fog on the sample. (for clear coat: we apply the fog the same way on each layer reflection) // If there is a contribution on this path, apply the required fog on the sample. // Also accumulate the path contribution. // In case no contribution is accumulated, the ReflectionEnvironmentAndSky pass will fill it up using reflection volumes. - //#dxr - todo: when MaxBounces>1, we will need to take into account a path throughput affected from the fog trnasmittance. + //#dxr - todo: when MaxBounces>1, we will need to take into account a path throughput affected from the fog transmittance. float4 HeightFogInscatteringAndTransmittance = float4(0.0f, 0.0f, 0.0f, 1.0f); // no fog if (bApplyHeightFog) { HeightFogInscatteringAndTransmittance = CalculateHeightFog(OriginToCollider); } // Now accumulate the radiance coming through this path segment into the full path radiance. - PathSegmentRadiance.rgb = PathSegmentRadiance.rgb * HeightFogInscatteringAndTransmittance.a + HeightFogInscatteringAndTransmittance.rgb; + // In the case of clear coat material: the fog is applied once for both bottom and top layer using the main top layer refelction path. + float3 PathSegmentRadiance = TopLayerRadiance * TopLayerReflectionEventThroughput + BottomLayerRadiance; + PathSegmentRadiance = PathSegmentRadiance * HeightFogInscatteringAndTransmittance.a + HeightFogInscatteringAndTransmittance.rgb; PathRadiance += PathSegmentRadiance * PathThroughput; - PathThroughput *= HeightFogInscatteringAndTransmittance.a; - - IncidentDirection = Ray.Direction; - WorldPosition = Ray.Origin + Payload.HitT * Ray.Direction; - WorldNormal = Payload.WorldNormal; - Roughness = Payload.Roughness; - SpecularColor = Payload.SpecularColor; + // Update the path throughput according to fog and material top layer reflection event. + PathThroughput *= HeightFogInscatteringAndTransmittance.a * TopLayerReflectionEventThroughput; + + + // Setup next iteration + IncidentDirection = TopLayerRay.Direction; + WorldPosition = TopLayerRay.Origin + Payload.HitT * TopLayerRay.Direction; + ShadingModelID = Payload.ShadingModelID; + TopLayerWorldNormal = Payload.WorldNormal; + TopLayerRoughness = Payload.Roughness; + TopLayerSpecularColor = Payload.SpecularColor; + if (ShadingModelID == SHADINGMODELID_CLEAR_COAT) { - ClearCoat = Payload.CustomData.x; - ClearCoatRoughness = Payload.CustomData.y; + BottomLayerRoughness = TopLayerRoughness; + BottomLayerSpecularColor = TopLayerSpecularColor; + BottomLayerWorldNormal = TopLayerWorldNormal; #if CLEAR_COAT_BOTTOM_NORMAL - const float2 oct1 = ((float2(Payload.CustomData.a, Payload.CustomData.z) * 2) - (256.0/255.0)) + UnitVectorToOctahedron(Payload.WorldNormal); - ClearCoatBottomNormal = OctahedronToUnitVector(oct1); + const float2 oct1 = ((float2(Payload.CustomData.a, Payload.CustomData.z) * 2) - (256.0 / 255.0)) + UnitVectorToOctahedron(TopLayerWorldNormal); + BottomLayerWorldNormal = OctahedronToUnitVector(oct1); #endif + + ClearCoat = Payload.CustomData.x; // Clear coat weight + TopLayerRoughness = Payload.CustomData.y; // Clear coat roughness + TopLayerSpecularColor = ClearCoatSpecularColor; // Hard coded top layer specular color } - - if (all(PathThroughput < 0.1)) break; + + if (all(PathThroughput < 0.001) || bSkyWasHit || HitDistance<0.0f) break; } if (bIsValidSample) @@ -434,6 +517,18 @@ void RayTracingReflectionsRGS() ColorOutput[DispatchThreadId] = ClampToHalfFloatRange(ReflectedColor); RayHitDistanceOutput[DispatchThreadId] = HitDistance; + + // Imaginary depth computation for Nvidia's denoiser. + // TODO: shader permutation? + { + float3 OriginalWorldPos = ReconstructWorldPositionFromDepth(UV, Depth); + float3 CameraToPixel = normalize(OriginalWorldPos - View.WorldCameraOrigin.xyz); + + float4 ImaginaryWorldPos = float4(OriginalWorldPos + CameraToPixel * HitDistance, 1.0f); + float4 ImaginaryClipPos = mul(ImaginaryWorldPos, View.WorldToClip); + float ImaginarySVDepth = saturate(ImaginaryClipPos.z / ImaginaryClipPos.w); + RayImaginaryDepthOutput[DispatchThreadId] = ImaginarySVDepth; + } } } diff --git a/Engine/Shaders/Private/RayTracing/RayTracingTranslucency.usf b/Engine/Shaders/Private/RayTracing/RayTracingTranslucency.usf index 7e3e663a3bb3..d3a1cae715e7 100644 --- a/Engine/Shaders/Private/RayTracing/RayTracingTranslucency.usf +++ b/Engine/Shaders/Private/RayTracing/RayTracingTranslucency.usf @@ -12,13 +12,13 @@ float2 SobolIndexToUniformUnitSquare(uint2 SobolRand) } #define SUPPORT_CONTACT_SHADOWS 0 -#define USE_SOURCE_TEXTURE 0 -#define USE_SOURCE_TEXTURE_ARRAY 0 +#define USE_SOURCE_TEXTURE 1 +#define USE_SOURCE_TEXTURE_ARRAY 1 -#define LTCMatTexture RaytracingLightsData.LTCMatTexture -#define LTCMatSampler RaytracingLightsData.LTCMatSampler -#define LTCAmpTexture RaytracingLightsData.LTCAmpTexture -#define LTCAmpSampler RaytracingLightsData.LTCAmpSampler +#define LTCMatTexture RaytracingLightsDataPacked.LTCMatTexture +#define LTCMatSampler RaytracingLightsDataPacked.LTCMatSampler +#define LTCAmpTexture RaytracingLightsDataPacked.LTCAmpTexture +#define LTCAmpSampler RaytracingLightsDataPacked.LTCAmpSampler #define PreIntegratedGF ReflectionStruct.PreIntegratedGF #define PreIntegratedGFSampler ReflectionStruct.PreIntegratedGFSampler @@ -266,7 +266,6 @@ void RayTracingTranslucencyRGS() Ray.Direction = RefractedDirection; float SurfaceCurvature = 0.0f; /* #todo_dxr assume no curvature */ RayCone = PropagateRayCone(RayCone, SurfaceCurvature, Depth); - const float ReflectionMaxNormalBias = 1.0; } if (!bHasScattered) diff --git a/Engine/Shaders/Private/ReflectionEnvironmentPixelShader.usf b/Engine/Shaders/Private/ReflectionEnvironmentPixelShader.usf index 73952f34a984..b89a99bd0a1d 100644 --- a/Engine/Shaders/Private/ReflectionEnvironmentPixelShader.usf +++ b/Engine/Shaders/Private/ReflectionEnvironmentPixelShader.usf @@ -235,9 +235,14 @@ float3 ReflectionEnvironment(FScreenSpaceData ScreenSpaceData, float4 UVAndScree Color.a = 1 - SSR.a; #endif -#if ENABLE_CLEAR_COAT +#if RAY_TRACED_REFLECTIONS + float4 SavedColor = Color; // When a clear coat material is encountered, we save the reflection buffer color for it to not be affected by operations. +#endif if(GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT ) { +#if RAY_TRACED_REFLECTIONS + Color = float4(0, 0, 0, 0); // Clear coat reflection is expected to be computed on a black background +#endif const float ClearCoat = GBuffer.CustomData.x; Color = lerp( Color, float4(0,0,0,1), ClearCoat ); @@ -249,7 +254,6 @@ float3 ReflectionEnvironment(FScreenSpaceData ScreenSpaceData, float4 UVAndScree R = 2 * dot( V, ClearCoatUnderNormal ) * ClearCoatUnderNormal - V; #endif } -#endif float AO = GBuffer.GBufferAO * ScreenSpaceData.AmbientOcclusion; float RoughnessSq = GBuffer.Roughness * GBuffer.Roughness; @@ -268,13 +272,12 @@ float3 ReflectionEnvironment(FScreenSpaceData ScreenSpaceData, float4 UVAndScree uint NumCulledReflectionCaptures = 0; #endif - //bottom for clearcoat or the only reflection. + //Top of regular reflection or bottom layer of clear coat. Color.rgb += PreExposure * GatherRadiance(Color.a, WorldPosition, R, GBuffer.Roughness, BentNormal, IndirectIrradiance, GBuffer.ShadingModelID, NumCulledReflectionCaptures, DataStartIndex); BRANCH - if( GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT ) + if( GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT) { -#if ENABLE_CLEAR_COAT const float ClearCoat = GBuffer.CustomData.x; const float ClearCoatRoughness = GBuffer.CustomData.y; @@ -299,9 +302,10 @@ float3 ReflectionEnvironment(FScreenSpaceData ScreenSpaceData, float4 UVAndScree float3 TopLayerR = 2 * dot( V, N ) * N - V; Color.rgb += PreExposure * GatherRadiance(Color.a, WorldPosition, TopLayerR, ClearCoatRoughness, BentNormal, IndirectIrradiance, GBuffer.ShadingModelID, NumCulledReflectionCaptures, DataStartIndex); -#else - // When clear coat is disabled in ReflectionEnvironmentPS (both layers evaluated in ray traced reflections), do nothing here -#endif + + #if RAY_TRACED_REFLECTIONS + Color.rgb = SavedColor.rgb + Color.rgb * SavedColor.a; // Compose default clear coat reflection over regular refelction (using Premultiplied alpha where SaveColor.a=transmittance) + #endif } else { diff --git a/Engine/Shaders/Private/SceneData.ush b/Engine/Shaders/Private/SceneData.ush index 2cf2e2342b21..df67d12d5ccd 100644 --- a/Engine/Shaders/Private/SceneData.ush +++ b/Engine/Shaders/Private/SceneData.ush @@ -30,13 +30,14 @@ struct FPrimitiveSceneData float4 NonUniformScale; float3 LocalObjectBoundsMin; float3 LocalObjectBoundsMax; + float3 PreSkinnedLocalBounds; uint LightingChannelMask; uint LightmapDataIndex; int SingleCaptureIndex; }; // Stride of a single primitive's data in float4's, must match C++ -#define PRIMITIVE_SCENE_DATA_STRIDE 26 +#define PRIMITIVE_SCENE_DATA_STRIDE 27 // Fetch from scene primitive buffer FPrimitiveSceneData GetPrimitiveData(uint PrimitiveId) @@ -89,6 +90,8 @@ FPrimitiveSceneData GetPrimitiveData(uint PrimitiveId) PrimitiveData.SingleCaptureIndex = asint(View.PrimitiveSceneData[PrimitiveBaseOffset + 25].x); + PrimitiveData.PreSkinnedLocalBounds = View.PrimitiveSceneData[PrimitiveBaseOffset + 26].xyz; + return PrimitiveData; } diff --git a/Engine/Shaders/Private/ScreenSpaceDenoise/SSDDefinitions.ush b/Engine/Shaders/Private/ScreenSpaceDenoise/SSDDefinitions.ush index bb834151bbec..201fc7c52291 100644 --- a/Engine/Shaders/Private/ScreenSpaceDenoise/SSDDefinitions.ush +++ b/Engine/Shaders/Private/ScreenSpaceDenoise/SSDDefinitions.ush @@ -174,6 +174,7 @@ // [ Stackowiak 2015, "Stochastic Screen-Space Reflections" ] #define SAMPLE_SET_STACKOWIAK_4_SETS 8 + #define SAMPLE_SET_HEXAWEB 11 //------------------------------------------------------- GLOBAL CONFIGURATION OF ALL DENOISER SHADERS diff --git a/Engine/Shaders/Private/ScreenSpaceDenoise/SSDSignalAccumulator.ush b/Engine/Shaders/Private/ScreenSpaceDenoise/SSDSignalAccumulator.ush index d9a61b4c687c..d081752425a8 100644 --- a/Engine/Shaders/Private/ScreenSpaceDenoise/SSDSignalAccumulator.ush +++ b/Engine/Shaders/Private/ScreenSpaceDenoise/SSDSignalAccumulator.ush @@ -35,6 +35,13 @@ struct FSSDSignalAccumulator float HighestInvFrequency; }; +/** Informations about cluster of sample */ +struct FSSDSampleClusterInfo +{ + // Outter radius boundary of the cluster. + float OutterBoundaryRadius; +}; + FSSDSignalAccumulator CreateSignalAccumulator() { @@ -131,14 +138,14 @@ void AccumulateSampleInDomainBoundaries(inout FSSDSignalAccumulator Accumulator, void StartAccumulatingClusterInDRB( FSSFSampleSceneInfos RefSceneMetadata, inout FSSDSignalAccumulator Accumulator, - uint RingId) + FSSDSampleClusterInfo ClusterInfo) { - const uint ErrorCorrection = 2; + const float ErrorCorrection = 1; // Compute the bluring radius of the output pixel itself. float RefPixelWorldBluringRadius = AmendWorldBluringRadiusCausedByPixelSize(ComputeWorldBluringRadiusCausedByPixelSize(RefSceneMetadata)); - Accumulator.BorderingRadius = RefPixelWorldBluringRadius * float(RingId + ErrorCorrection); + Accumulator.BorderingRadius = RefPixelWorldBluringRadius * (ClusterInfo.OutterBoundaryRadius + ErrorCorrection); // TODO: could this be constant. Accumulator.HighestInvFrequency = Accumulator.BorderingRadius * 2; @@ -150,7 +157,7 @@ void StartAccumulatingClusterInDRB( Accumulator.CurrentTranslucency = 0.0; } - #if CONFIG_SGPR_HINT_OPTIMIZATION + #if CONFIG_SGPR_HINT_OPTIMIZATION && 0 { Accumulator.BorderingRadius = ToScalarMemory(Accumulator.BorderingRadius); Accumulator.HighestInvFrequency = ToScalarMemory(Accumulator.HighestInvFrequency); @@ -160,17 +167,15 @@ void StartAccumulatingClusterInDRB( void AccumulateSampleInDRB(inout FSSDSignalAccumulator Accumulator, FSSDSampleAccumulationInfos A) { + float ClampedInvFrequency = min(A.InvFrequency, Accumulator.HighestInvFrequency); + // Compare the sample's frequency with the bucket bordering radius. - float bBelongsToPrevious = saturate(A.InvFrequency - Accumulator.BorderingRadius + 0.5); - - //bBelongsToPrevious = 1; - //bBelongsToPrevious = A.Sample.LeavingRayCount != 0.0 ? 1 : 0; - - float ClampedInvFrequency = min(A.InvFrequency, Accumulator.HighestInvFrequency); - + float bBelongsToPrevious = saturate(ClampedInvFrequency - Accumulator.BorderingRadius + 0.5); + float bBelongsToCurrent = 1.0 - bBelongsToPrevious; + // Accumulate current bucket. { - float CurrentWeight = A.FinalWeight * (1.0 - bBelongsToPrevious); + float CurrentWeight = A.FinalWeight * bBelongsToCurrent; Accumulator.Current = Add(Accumulator.Current, Mul(A.Sample, CurrentWeight)); Accumulator.CurrentInvFrequency += A.Sample.AccumulatedSampleCount * ClampedInvFrequency * CurrentWeight; } @@ -183,10 +188,10 @@ void AccumulateSampleInDRB(inout FSSDSignalAccumulator Accumulator, FSSDSampleAc // TODO: could pass down a normalised version of A.Sample to avoid Rcp. float R = A.Sample.LeavingRayCount * SafeRcp(A.Sample.AccumulatedSampleCount); - //Accumulator.CurrentTranslucency += SampleTranslucency; - Accumulator.CurrentTranslucency += lerp(R, 1, SampleTranslucency); - //Accumulator.CurrentTranslucency += saturate(1 - (1 - SampleTranslucency) * (1 - R)); - //Accumulator.CurrentTranslucency += lerp(R, 1, SampleTranslucency); + float Translucency = lerp(R, 1, SampleTranslucency); + float TranslucencyWeight = 1; + + Accumulator.CurrentTranslucency += Translucency * TranslucencyWeight; } // Accumulate previous bucket. @@ -210,10 +215,10 @@ void DijestSampleClusterInDRB( } // Opacity of the current bucket. - float CurrentClusterOpacity = saturate(1 - Accumulator.CurrentTranslucency * rcp(SampleCount)); + float CurrentClusterOpacity = saturate(1 - Accumulator.CurrentTranslucency * rcp(float(SampleCount))); // Compute current and previous Coc radii. - float PreviousInvFrequency = Accumulator.PreviousInvFrequency * rcp(Accumulator.Previous.AccumulatedSampleCount); + float PreviousInvFrequency = Accumulator.PreviousInvFrequency * SafeRcp(Accumulator.Previous.AccumulatedSampleCount); float CurrentInvFrequency = Accumulator.CurrentInvFrequency * rcp(Accumulator.Current.AccumulatedSampleCount); // Whether current bucket is occluding previous bucket. @@ -224,8 +229,8 @@ void DijestSampleClusterInDRB( float PreviousBucketFactor = (Accumulator.Previous.AccumulatedSampleCount == 0.0) ? 0.0 : (1.0 - CurrentClusterOpacity * bOccludingCluster); // TODO: the big difference in sample count could lead to wrong opacity between sample clusters. - //if (PreviousBucketFactor < 1 && Accumulator.Previous.AccumulatedSampleCount > 0) - // PreviousBucketFactor *= Accumulator.Current.AccumulatedSampleCount * rcp(Accumulator.Previous.AccumulatedSampleCount); + //if (PreviousBucketFactor < 1) + // PreviousBucketFactor *= Accumulator.Current.AccumulatedSampleCount * SafeRcp(Accumulator.Previous.AccumulatedSampleCount); Accumulator.Previous = Add(Mul(Accumulator.Previous, PreviousBucketFactor), Accumulator.Current); Accumulator.PreviousInvFrequency = Accumulator.PreviousInvFrequency * PreviousBucketFactor + Accumulator.CurrentInvFrequency; @@ -238,9 +243,9 @@ void DijestSampleClusterInDRB( void StartAccumulatingCluster( FSSFSampleSceneInfos RefSceneMetadata, inout FSSDSignalAccumulator Accumulator, - uint RingId) + FSSDSampleClusterInfo ClusterInfo) { - StartAccumulatingClusterInDRB(RefSceneMetadata, /* inout */ Accumulator, RingId); + StartAccumulatingClusterInDRB(RefSceneMetadata, /* inout */ Accumulator, ClusterInfo); } /** Accumulate a sample withing the accumulator. */ diff --git a/Engine/Shaders/Private/ScreenSpaceDenoise/SSDSignalFramework.ush b/Engine/Shaders/Private/ScreenSpaceDenoise/SSDSignalFramework.ush index e13a68c5804b..df88f52676e3 100644 --- a/Engine/Shaders/Private/ScreenSpaceDenoise/SSDSignalFramework.ush +++ b/Engine/Shaders/Private/ScreenSpaceDenoise/SSDSignalFramework.ush @@ -507,15 +507,6 @@ float ComputeBilateralWeight( PositionBilateralWeight = saturate(1 - DistFromRefPlane * rcp(MaxWorldBluringDistance)); } - #elif CONFIG_VIEW_POSITION_BASED_BILATERAL == 3 && 1 - { - float3 V = RefSceneMetadata.TranslatedWorldPosition - NeighborSceneMetadata.TranslatedWorldPosition; - - if (dot(V, V) > MaxWorldBluringDistance * MaxWorldBluringDistance) - { - PositionBilateralWeight = 0; - } - } #elif CONFIG_VIEW_POSITION_BASED_BILATERAL == 3 { float3 V = RefSceneMetadata.TranslatedWorldPosition - NeighborSceneMetadata.TranslatedWorldPosition; @@ -524,10 +515,25 @@ float ComputeBilateralWeight( { PositionBilateralWeight = 0; } + } + #elif CONFIG_VIEW_POSITION_BASED_BILATERAL == 4 + { + float3 V = RefSceneMetadata.TranslatedWorldPosition - NeighborSceneMetadata.TranslatedWorldPosition; // TODO: extremely expensive with rcp()... PositionBilateralWeight = saturate(1 - dot(V, V) * rcp(MaxWorldBluringDistance * MaxWorldBluringDistance)); } + #elif CONFIG_VIEW_POSITION_BASED_BILATERAL == 5 + { + float3 V = RefSceneMetadata.TranslatedWorldPosition - NeighborSceneMetadata.TranslatedWorldPosition; + + float DistFromRefPlane = dot(RefSceneMetadata.WorldNormal, RefSceneMetadata.TranslatedWorldPosition - NeighborSceneMetadata.TranslatedWorldPosition); + + // TODO: extremely expensive with rcp()... + PositionBilateralWeight = ( + saturate(1 - dot(V, V) * rcp(MaxWorldBluringDistance * MaxWorldBluringDistance)) * + saturate(1 - 6 * abs(DistFromRefPlane) * rcp(MaxWorldBluringDistance))); + } #endif float NormalBilateralWeight = 1; @@ -585,22 +591,23 @@ float GetSignalWorldBluringRadius(FSSFSignalSample Sample, FSSFSampleSceneInfos return WORLD_RADIUS_MISS; } - // Compute the anysotropy. - float Anisotropy = ComputeAnisotropyInvFactor(SceneMetadata); - FSSDSignalDomainKnowledge DomainKnowledge = GetSignalDomainKnowledge(BatchedSignalId); + // Sometime, artists might put occluder very close to the area light compared to they area, that may lead to negative values. + // TODO: the correct way to fix this is to move this world bluring radius computation into the RGS, and have DistanceFromLight = Ray's MaxT. + const float ClosestLightDistance = 0.001; + BRANCH if (DomainKnowledge.LightType == LIGHT_TYPE_DIRECTIONAL) { - return DomainKnowledge.HitDistanceToWorldBluringRadius * Sample.PenumbraClosestHit * Anisotropy; + return DomainKnowledge.HitDistanceToWorldBluringRadius * Sample.PenumbraClosestHit; } else if (DomainKnowledge.LightType == LIGHT_TYPE_POINT || DomainKnowledge.LightType == LIGHT_TYPE_SPOT) { float3 PixelToLightWorldVector = (DomainKnowledge.Light.Position + View.PreViewTranslation) - SceneMetadata.TranslatedWorldPosition; float DistanceFromLight = length(PixelToLightWorldVector); - return DomainKnowledge.Light.SourceRadius * Sample.PenumbraClosestHit / (DistanceFromLight - Sample.PenumbraClosestHit) * Anisotropy; + return DomainKnowledge.Light.SourceRadius * Sample.PenumbraClosestHit / max(DistanceFromLight - Sample.PenumbraClosestHit, ClosestLightDistance); } else // if (DomainKnowledge.LightType == LIGHT_TYPE_RECT) { @@ -619,7 +626,7 @@ float GetSignalWorldBluringRadius(FSSFSignalSample Sample, FSSFSampleSceneInfos float SmallestLightDimension = min(LightDimensions.x, LightDimensions.y); // TODO: Sample.PenumbraClosestHit depends on the direction of the ray, but does not DistanceFromLight, witch is bad for large area light. - return SmallestLightDimension * Sample.PenumbraClosestHit / (DistanceFromLight - Sample.PenumbraClosestHit) * Anisotropy; + return SmallestLightDimension * Sample.PenumbraClosestHit / max(DistanceFromLight - Sample.PenumbraClosestHit, ClosestLightDistance); } } #elif CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_REFLECTIONS diff --git a/Engine/Shaders/Private/ScreenSpaceDenoise/SSDSpatialAccumulation.usf b/Engine/Shaders/Private/ScreenSpaceDenoise/SSDSpatialAccumulation.usf index f14a9aaf7512..733180d40cd3 100644 --- a/Engine/Shaders/Private/ScreenSpaceDenoise/SSDSpatialAccumulation.usf +++ b/Engine/Shaders/Private/ScreenSpaceDenoise/SSDSpatialAccumulation.usf @@ -7,9 +7,10 @@ /** Different possible stage for spatial accumulation. Matches */ #define STAGE_RECONSTRUCTION 0 - #define STAGE_REJECTION_PRE_CONVOLUTION 1 - #define STAGE_POST_FILTERING 2 - #define STAGE_FINAL_OUTPUT 3 + #define STAGE_PRE_CONVOLUTION 1 + #define STAGE_REJECTION_PRE_CONVOLUTION 2 + #define STAGE_POST_FILTERING 3 + #define STAGE_FINAL_OUTPUT 4 /** Policy to use to change the size of kernel. */ #define SAMPLE_COUNT_POLICY_DISABLED 0 @@ -40,7 +41,7 @@ // Configures all the pass for each individual signals. #if CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_MONOCHROMATIC_PENUMBRA //#define CONFIG_VIEW_POSITION_BASED_BILATERAL 2 - #define CONFIG_VIEW_POSITION_BASED_BILATERAL 3 + #define CONFIG_VIEW_POSITION_BASED_BILATERAL 5 #define CONFIG_MULTIPLEXED_SIGNALS_PER_SIGNAL_DOMAIN 1 #if DIM_STAGE == STAGE_RECONSTRUCTION @@ -54,6 +55,17 @@ #define CONFIG_SAMPLE_SET SAMPLE_SET_STACKOWIAK_4_SETS #define CONFIG_BILATERAL_DISTANCE_COMPUTATION SIGNAL_WORLD_FREQUENCY_PRECOMPUTED_BLURING_RADIUS + #define CONFIG_MAX_WITH_REF_DISTANCE 1 + #define CONFIG_OUTPUT_MODE OUTPUT_MODE_DRB + + #elif DIM_STAGE == STAGE_PRE_CONVOLUTION + // Input and output layout. + #define CONFIG_SIGNAL_INPUT_LAYOUT SIGNAL_BUFFER_LAYOUT_PENUMBRA_RECONSTRUCTION + #define CONFIG_SIGNAL_OUTPUT_LAYOUT SIGNAL_BUFFER_LAYOUT_PENUMBRA_RECONSTRUCTION + + #define CONFIG_SAMPLE_SET SAMPLE_SET_HEXAWEB + #define CONFIG_BILATERAL_DISTANCE_COMPUTATION SIGNAL_WORLD_FREQUENCY_PRECOMPUTED_BLURING_RADIUS + #define CONFIG_MAX_WITH_REF_DISTANCE 1 #define CONFIG_OUTPUT_MODE OUTPUT_MODE_DRB #elif DIM_STAGE == STAGE_REJECTION_PRE_CONVOLUTION @@ -264,6 +276,11 @@ #define CONFIG_BILATERAL_DISTANCE_COMPUTATION SIGNAL_WORLD_FREQUENCY_MIN_METADATA #endif +/** Whether neighbor bilateral distance should be maxed with reference one. */ +#ifndef CONFIG_MAX_WITH_REF_DISTANCE + #define CONFIG_MAX_WITH_REF_DISTANCE 0 +#endif + //------------------------------------------------------- INCLUDES @@ -402,33 +419,85 @@ void MainCS( FSSDSignalAccumulatorArray SignalAccumulators = CreateSignalAccumulatorArray(); { FSSDKernelConfig KernelConfig = CreateKernelConfig(); + + // Compile time. KernelConfig.SampleSet = CONFIG_SAMPLE_SET; KernelConfig.SampleSubSetId = CONFIG_SAMPLE_SUBSET; KernelConfig.BufferLayout = CONFIG_SIGNAL_INPUT_LAYOUT; KernelConfig.MultiplexedSignalsPerSignalDomain = CONFIG_MULTIPLEXED_SIGNALS_PER_SIGNAL_DOMAIN; KernelConfig.bUnroll = CONFIG_SAMPLE_SET != SAMPLE_SET_STACKOWIAK_4_SETS; - KernelConfig.bDescOrder = CONFIG_OUTPUT_MODE == OUTPUT_MODE_DRB && CONFIG_SAMPLE_SET == SAMPLE_SET_STACKOWIAK_4_SETS; + KernelConfig.bDescOrder = CONFIG_OUTPUT_MODE == OUTPUT_MODE_DRB; KernelConfig.BilateralDistanceComputation = CONFIG_BILATERAL_DISTANCE_COMPUTATION; KernelConfig.bNormalizeSample = CONFIG_NORMALIZE_INPUT != 0; + KernelConfig.bSampleKernelCenter = CONFIG_UPSCALE; + KernelConfig.bForceKernelCenterAccumulation = true; - FIX_UNROLL(MAX_SIGNAL_MULTIPLEXING) - for (uint MultiplexId = 0; MultiplexId < MAX_SIGNAL_MULTIPLEXING; MultiplexId++) { - KernelConfig.BufferColorSpace[MultiplexId] = CONFIG_INPUT_COLOR_SPACE; - KernelConfig.AccumulatorColorSpace[MultiplexId] = CONFIG_ACCUMULATION_COLOR_SPACE; + FIX_UNROLL(MAX_SIGNAL_MULTIPLEXING) + for (uint MultiplexId = 0; MultiplexId < MAX_SIGNAL_MULTIPLEXING; MultiplexId++) + { + KernelConfig.BufferColorSpace[MultiplexId] = CONFIG_INPUT_COLOR_SPACE; + KernelConfig.AccumulatorColorSpace[MultiplexId] = CONFIG_ACCUMULATION_COLOR_SPACE; + } } + // SGPRs KernelConfig.BufferSizeAndInvSize = View.BufferSizeAndInvSize; KernelConfig.BufferBilinearUVMinMax = View.BufferBilinearUVMinMax; KernelConfig.KernelSpreadFactor = KernelSpreadFactor; - - KernelConfig.bSampleKernelCenter = CONFIG_UPSCALE || KernelConfig.bDescOrder; - KernelConfig.bForceKernelCenterAccumulation = true; KernelConfig.BufferUV = SceneBufferUV; KernelConfig.RefSceneMetadata = RefSceneMetadata; + + // Set up reference distance for all signals. + #if CONFIG_MAX_WITH_REF_DISTANCE + { + KernelConfig.bMaxWithRefBilateralDistance = true; + + FIX_UNROLL(MAX_SIGNAL_MULTIPLEXING) + for (uint MultiplexId = 0; MultiplexId < MAX_SIGNAL_MULTIPLEXING; MultiplexId++) + { + if (KernelConfig.BilateralDistanceComputation == SIGNAL_WORLD_FREQUENCY_PRECOMPUTED_BLURING_RADIUS) + { + KernelConfig.RefBilateralDistance[MultiplexId] = RefSamples.Array[MultiplexId].WorldBluringRadius; + } + else + { + const uint BatchedSignalId = ComputeSignalBatchIdFromSignalMultiplexId(KernelConfig, MultiplexId); + + KernelConfig.RefBilateralDistance[MultiplexId] = GetSignalWorldBluringRadius(RefSamples.Array[MultiplexId], RefSceneMetadata, BatchedSignalId); + } + } + } + #endif + + // When doing history rejection preconvolution may have invalid ref sample, in witch case need to force take neighborhood to have a clamping box. + #if DIM_STAGE == STAGE_REJECTION_PRE_CONVOLUTION && CONFIG_UPSCALE + { + KernelConfig.bForceAllAccumulation = RefSamples.Array[0].AccumulatedSampleCount == 0; + KernelConfig.SampleSet = SAMPLE_SET_3X3_PLUS; + } + #endif + + #if CONFIG_SAMPLE_SET == SAMPLE_SET_HEXAWEB + { + KernelConfig.RingCount = 1; + KernelConfig.KernelSpreadFactor = 8; + + // TODO: could be improved. + //KernelConfig.bMinSamplePairInvFrequency = true; + + float2 E = float2( + InterleavedGradientNoise(DispatchThreadId, 0), + InterleavedGradientNoise(DispatchThreadId, 1)); + + // Add a bit of jittering to hide low sample. + KernelConfig.bSampleKernelCenter = false; + KernelConfig.BufferUV += View.ViewSizeAndInvSize.zw * (E - 0.5) * (KernelConfig.KernelSpreadFactor * (0.5 * rcp(2))); + } + #endif // When not upscaling, manually force accumulate the sample of the kernel. - if (!KernelConfig.bSampleKernelCenter) + if (!KernelConfig.bSampleKernelCenter && !KernelConfig.bDescOrder) { FIX_UNROLL(MAX_SIGNAL_MULTIPLEXING) for (uint SignalMultiplexId = 0; SignalMultiplexId < MAX_SIGNAL_MULTIPLEXING; SignalMultiplexId++) @@ -457,14 +526,6 @@ void MainCS( } } - // When doing history rejection preconvolution may have invalid ref sample, in witch case need to force take neighborhood to have a clamping box. - #if DIM_STAGE == STAGE_REJECTION_PRE_CONVOLUTION && CONFIG_UPSCALE - { - KernelConfig.bForceAllAccumulation = RefSamples.Array[0].AccumulatedSampleCount == 0; - KernelConfig.SampleSet = SAMPLE_SET_3X3_PLUS; - } - #endif - if (CONFIG_SAMPLE_SET == SAMPLE_SET_STACKOWIAK_4_SETS) { KernelConfig.SampleCount = clamp(RequestedSampleCount * rcp(kStackowiakSampleSetCount), 1, MaxSampleCount); @@ -531,6 +592,21 @@ void MainCS( SignalInput_Textures_3, /* inout */ SignalAccumulators); } + + // Manually sample the center of the kernel after any accumulation when accumulating in descending order. + if (!KernelConfig.bSampleKernelCenter && KernelConfig.bDescOrder) + { + // Remove any jitter the kernel may have. Won't have ant VGPR cost when no jittering, because KernelConfig.BufferUV == SceneBufferUV. + KernelConfig.BufferUV = SceneBufferUV; + + SampleAndAccumulateCenterSampleAsItsOwnCluster( + KernelConfig, + SignalInput_Textures_0, + SignalInput_Textures_1, + SignalInput_Textures_2, + SignalInput_Textures_3, + /* inout */ SignalAccumulators); + } } // Color processing of the signal to reduce highlight flickering. @@ -573,6 +649,11 @@ void MainCS( for (uint MultiplexId = 0; MultiplexId < CONFIG_SIGNAL_BATCH_SIZE; MultiplexId++) { OutputSamples.Array[MultiplexId] = SignalAccumulators.Array[MultiplexId].Moment1; + + // Output the minimal inverse frequency as new world bluring radius for subsequent passes. + // TODO: store this in its own signal to avoid *= N; *= rcp(N); + OutputSamples.Array[MultiplexId].WorldBluringRadius = + OutputSamples.Array[MultiplexId].AccumulatedSampleCount * SignalAccumulators.Array[MultiplexId].MinInvFrequency; } } #elif CONFIG_OUTPUT_MODE == OUTPUT_MODE_2MOMMENT_SUM @@ -606,15 +687,6 @@ void MainCS( OutputSamples.Array[MultiplexId].WorldBluringRadius = 0; } } - - if (0) - { - DebugOutput[DispatchThreadId] = float4( - SignalAccumulators.Array[0].Previous.AccumulatedSampleCount, - SignalAccumulators.Array[0].CurrentTranslucency, - SignalAccumulators.Array[0].Previous.LeavingRayCount * SafeRcp(SignalAccumulators.Array[0].Previous.AccumulatedSampleCount), - SignalAccumulators.Array[0].Current.LeavingRayCount * SafeRcp(SignalAccumulators.Array[0].Current.AccumulatedSampleCount)); - } } #else #error Unknown output mode. @@ -630,7 +702,7 @@ void MainCS( float CurrentSampleCount = OutputSamples.Array[MultiplexId].AccumulatedSampleCount; float NewSampleCount = min(CurrentSampleCount, 128); - OutputSamples.Array[MultiplexId] = Mul(OutputSamples.Array[MultiplexId], NewSampleCount / CurrentSampleCount); + OutputSamples.Array[MultiplexId] = Mul(OutputSamples.Array[MultiplexId], CurrentSampleCount > 0 ? NewSampleCount / CurrentSampleCount : 0); } } diff --git a/Engine/Shaders/Private/ScreenSpaceDenoise/SSDSpatialKernel.ush b/Engine/Shaders/Private/ScreenSpaceDenoise/SSDSpatialKernel.ush index 4a86310555a1..e55b7825bedd 100644 --- a/Engine/Shaders/Private/ScreenSpaceDenoise/SSDSpatialKernel.ush +++ b/Engine/Shaders/Private/ScreenSpaceDenoise/SSDSpatialKernel.ush @@ -65,6 +65,9 @@ struct FSSDKernelConfig // Selects how the world distance should be computed for bilateral rejection at compile time. uint BilateralDistanceComputation; + // Number of ring for a disk kernel. + uint RingCount; + // Compile time configuration whether want do LOOP or UNROLL // false by default to expose in user code when the shader byte code might potentially be big. bool bUnroll; @@ -81,6 +84,13 @@ struct FSSDKernelConfig // Whether a sample should be normalised to 1 before accmulation. bool bNormalizeSample; + // Whether should min sample frequency of pair of samples + // [ Jimenez 2014, "Next Generation Post Processing in Call of Duty: Advanced Warfare" ] + bool bMinSamplePairInvFrequency; + + // Whether the bilateral distance should be maxed with reference bilateral distance. + bool bMaxWithRefBilateralDistance; + // The color space that has been encoded in the buffer. uint BufferColorSpace[MAX_SIGNAL_MULTIPLEXING]; @@ -123,6 +133,9 @@ struct FSSDKernelConfig // Runtime selection of a track of sample. uint SampleTrackId; + + // Reference meta data. + float RefBilateralDistance[MAX_SIGNAL_MULTIPLEXING]; }; FSSDKernelConfig CreateKernelConfig() @@ -132,25 +145,32 @@ FSSDKernelConfig CreateKernelConfig() KernelConfig.SampleSubSetId = 0; KernelConfig.BufferLayout = SIGNAL_BUFFER_LAYOUT_UNINITIALIZED; KernelConfig.MultiplexedSignalsPerSignalDomain = MAX_SIGNAL_MULTIPLEXING; + KernelConfig.RingCount = 0; KernelConfig.bUnroll = false; KernelConfig.bSampleKernelCenter = false; KernelConfig.bPreviousFrameMetadata = false; KernelConfig.BilateralDistanceComputation = SIGNAL_WORLD_FREQUENCY_MIN_METADATA; KernelConfig.bDescOrder = false; KernelConfig.bNormalizeSample = false; + KernelConfig.bMinSamplePairInvFrequency = false; + KernelConfig.bMaxWithRefBilateralDistance = false; - FIX_UNROLL(MAX_SIGNAL_MULTIPLEXING) - for (uint MultiplexId = 0; MultiplexId < MAX_SIGNAL_MULTIPLEXING; MultiplexId++) { - KernelConfig.BufferColorSpace[MultiplexId] = STANDARD_BUFFER_COLOR_SPACE; - KernelConfig.AccumulatorColorSpace[MultiplexId] = STANDARD_BUFFER_COLOR_SPACE; + FIX_UNROLL(MAX_SIGNAL_MULTIPLEXING) + for (uint MultiplexId = 0; MultiplexId < MAX_SIGNAL_MULTIPLEXING; MultiplexId++) + { + KernelConfig.BufferColorSpace[MultiplexId] = STANDARD_BUFFER_COLOR_SPACE; + KernelConfig.AccumulatorColorSpace[MultiplexId] = STANDARD_BUFFER_COLOR_SPACE; + } } + // SGPRs. KernelConfig.BufferSizeAndInvSize = float4(0, 0, 0, 0); KernelConfig.BufferBilinearUVMinMax = float4(0, 0, 0, 0); KernelConfig.BufferMipLevel = 0.0; KernelConfig.KernelSpreadFactor = 1; + // VGPRs. KernelConfig.BoxKernelRadius = 1; KernelConfig.SampleCount = 1; KernelConfig.BufferUV = 0.0; @@ -158,6 +178,15 @@ FSSDKernelConfig CreateKernelConfig() KernelConfig.bForceKernelCenterAccumulation = false; KernelConfig.bForceAllAccumulation = false; KernelConfig.SampleTrackId = 0; + + { + FIX_UNROLL(MAX_SIGNAL_MULTIPLEXING) + for (uint MultiplexId = 0; MultiplexId < MAX_SIGNAL_MULTIPLEXING; MultiplexId++) + { + KernelConfig.RefBilateralDistance[MultiplexId] = 0.0; + } + } + return KernelConfig; } @@ -300,6 +329,11 @@ void AccumulateSampledMultiplexedSignals( FinalWorldBluringDistance = SignalConvolutionBluringRadius; } + if (KernelConfig.bMaxWithRefBilateralDistance) + { + FinalWorldBluringDistance = min(FinalWorldBluringDistance, KernelConfig.RefBilateralDistance[SignalMultiplexId]); + } + float BilateralWeight = ComputeBilateralWeight( FinalWorldBluringDistance, KernelConfig.RefSceneMetadata, @@ -373,10 +407,75 @@ void SampleAndAccumulateMultiplexedSignals( bIsOutsideFrustum); } // SampleAndAccumulateMultiplexedSignals() +void SampleAndAccumulateMultiplexedSignalsPair( + FSSDKernelConfig KernelConfig, + Texture2D SignalBuffer0, + Texture2D SignalBuffer1, + Texture2D SignalBuffer2, + Texture2D SignalBuffer3, + inout FSSDSignalAccumulatorArray Accumulators, + float2 SampleBufferUV[2], + float KernelSampleWeight) +{ + FSSFSampleSceneInfos SampleSceneMetadata[2]; + FSSDSignalArray MultiplexedSamples[2]; + bool bIsOutsideFrustum[2]; + + FIX_UNROLL(2) + for (uint PairFetchId = 0; PairFetchId < 2; PairFetchId++) + { + // Stores in SGPR whether this sample is outside the viewport, to avoid VGPR pressure to keep SampleBufferUV after texture fetches. + bIsOutsideFrustum[PairFetchId] = IsOutsideViewport(KernelConfig, SampleBufferUV[PairFetchId]); + + SampleMultiplexedSignals( + KernelConfig, + SignalBuffer0, + SignalBuffer1, + SignalBuffer2, + SignalBuffer3, + SampleBufferUV[PairFetchId], + /* out */ SampleSceneMetadata[PairFetchId], + /* out */ MultiplexedSamples[PairFetchId]); + } + + // Take the min inverse frequency per signal if desired. + if (KernelConfig.bMinSamplePairInvFrequency) + { + FIX_UNROLL(MAX_SIGNAL_MULTIPLEXING) + for (uint SignalMultiplexId = 0; SignalMultiplexId < MAX_SIGNAL_MULTIPLEXING; SignalMultiplexId++) + { + float MinInvFrequency = min( + MultiplexedSamples[0].Array[SignalMultiplexId].WorldBluringRadius, + MultiplexedSamples[1].Array[SignalMultiplexId].WorldBluringRadius); + + FLATTEN + if (MinInvFrequency > 0) + { + MultiplexedSamples[0].Array[SignalMultiplexId].WorldBluringRadius = MinInvFrequency; + MultiplexedSamples[1].Array[SignalMultiplexId].WorldBluringRadius = MinInvFrequency; + } + } + } + + FIX_UNROLL(2) + for (uint PairAccumulateId = 0; PairAccumulateId < 2; PairAccumulateId++) + { + AccumulateSampledMultiplexedSignals( + KernelConfig, + /* inout */ Accumulators, + SampleBufferUV[PairAccumulateId], + SampleSceneMetadata[PairAccumulateId], + MultiplexedSamples[PairAccumulateId], + KernelSampleWeight, + /* bForceSample = */ false, + bIsOutsideFrustum[PairAccumulateId]); + } +} // SampleAndAccumulateMultiplexedSignalsPair() + void StartAccumulatingCluster( FSSDKernelConfig KernelConfig, inout FSSDSignalAccumulatorArray Accumulators, - uint RingId) + FSSDSampleClusterInfo ClusterInfo) { FIX_UNROLL(MAX_SIGNAL_MULTIPLEXING) for (uint SignalMultiplexId = 0; SignalMultiplexId < MAX_SIGNAL_MULTIPLEXING; SignalMultiplexId++) @@ -384,7 +483,7 @@ void StartAccumulatingCluster( StartAccumulatingCluster( KernelConfig.RefSceneMetadata, /* inout */ Accumulators.Array[SignalMultiplexId], - RingId); + ClusterInfo); } } @@ -399,6 +498,35 @@ void DijestAccumulatedClusterSamples(inout FSSDSignalAccumulatorArray Accumulato } } +void SampleAndAccumulateCenterSampleAsItsOwnCluster( + FSSDKernelConfig KernelConfig, + Texture2D SignalBuffer0, + Texture2D SignalBuffer1, + Texture2D SignalBuffer2, + Texture2D SignalBuffer3, + inout FSSDSignalAccumulatorArray Accumulators) +{ + const uint RingId = 0; + + FSSDSampleClusterInfo ClusterInfo; + ClusterInfo.OutterBoundaryRadius = (RingId + 1) * KernelConfig.KernelSpreadFactor; + + StartAccumulatingCluster(KernelConfig, Accumulators, ClusterInfo); + + SampleAndAccumulateMultiplexedSignals( + KernelConfig, + SignalBuffer0, + SignalBuffer1, + SignalBuffer2, + SignalBuffer3, + /* inout */ Accumulators, + KernelConfig.BufferUV, + /* KernelSampleWeight = */ 1.0, + /* bForceSample = */ KernelConfig.bForceKernelCenterAccumulation); + + DijestAccumulatedClusterSamples(Accumulators, RingId, /* SampleCount = */ 1); +} + //------------------------------------------------------- EASY CONVOLUTIONS @@ -710,7 +838,10 @@ void ConvolveStackowiakKernel( // TODO } - StartAccumulatingCluster(KernelConfig, /* inout */ Accumulators, CurrentRingId); + FSSDSampleClusterInfo ClusterInfo; + ClusterInfo.OutterBoundaryRadius = (CurrentRingId + 1) * KernelConfig.KernelSpreadFactor; + + StartAccumulatingCluster(KernelConfig, /* inout */ Accumulators, ClusterInfo); // Processes the samples in batches so that the compiler can do lattency hidding. LOOP @@ -763,8 +894,11 @@ void ConvolveStackowiakKernel( CurrentRingId -= 1; NextClusterBoundary -= CurrentRingId * StocasticSamplesPerCluster; + FSSDSampleClusterInfo ClusterInfo; + ClusterInfo.OutterBoundaryRadius = (CurrentRingId + 1) * KernelConfig.KernelSpreadFactor; + // Prepare the accumulators for new cluster. - StartAccumulatingCluster(KernelConfig, /* inout */ Accumulators, CurrentRingId); + StartAccumulatingCluster(KernelConfig, /* inout */ Accumulators, ClusterInfo); } } // for (uint SampleBatchId = 0; SampleBatchId < kSamplingBatchSize; SampleBatchId++) } // for (uint BatchId = 0; BatchId < BatchCountCount; BatchId++) @@ -806,6 +940,233 @@ void ConvolveStackowiakKernel( } // ConvolveStackowiakKernel() +//------------------------------------------------------- DISK + +// Returns the position of the sample on the unit circle (radius = 1) for a given ring. +float2 GetDiskSampleOnUnitCirle(uint RingId, uint RingSampleIteration, uint RingSampleId) +{ + RingId -= 1; // TODO. + + float SampleRingPos = RingSampleId; + + // Do not allign all j == 0 samples of the different ring on the X axis to increase minimal distance between all + // samples, that reduce variance to clean by post filtering. + #if 1 + SampleRingPos += (RingId - 2 * (RingId / 2)) * 0.5; + #endif + + #if 1 + SampleRingPos += (RingId + 1) * 0.2; + #endif + + float SampleAngle = PI * SampleRingPos / float(RingSampleIteration); + + return float2(cos(SampleAngle), sin(SampleAngle)); +} + +// Returns the rotation matrix to use between sample of the ring. +float2x2 GetSampleRotationMatrix(uint RingSampleIteration) +{ + float RotationAngle = PI / float(RingSampleIteration); + + float C = cos(RotationAngle); + float S = sin(RotationAngle); + + return float2x2( + float2( C, S), + float2(-S, C)); +} + +// Returns the total number of sampling iteration for a given ring id. +uint GetRingSamplingPairCount(const uint SampleSet, uint RingId) +{ + if (SampleSet == SAMPLE_SET_HEXAWEB) + { + return RingId * 3; + } + + // This number of sample is carefully chosen to have exact number of sample a square shaped ring (SquarePos). + return RingId * 4; +} + +// Returns the total number of sample of the kernel. +uint GetDiskKernelSampleCount(const uint SampleSet, uint RingCount) +{ + if (SampleSet == SAMPLE_SET_HEXAWEB) + { + return 1 + 3 * RingCount * (RingCount + 1); + } + + // Depends on GetRingSamplingPairCount(). + return 1 + 4 * RingCount * (RingCount + 1); +} + +// Transform at compile time a 2 dimensional batch's constant into sample pair constant, by using rotation invariance. +float2 SampleConstFromBatchConst(const uint BatchSampleId, float2 BatchConst) +{ + /** + * Y + * ^ + * | + * 1 | + * | + * | 0 + * | + * - - - - - - O - - - - > X + */ + if (BatchSampleId == 1) + return float2(-BatchConst.y, BatchConst.x); + return BatchConst; +} + + + +// Gather a ring into the accumulator. +void GatherRingSamples( + FSSDKernelConfig KernelConfig, + Texture2D SignalBuffer0, + Texture2D SignalBuffer1, + Texture2D SignalBuffer2, + Texture2D SignalBuffer3, + inout FSSDSignalAccumulatorArray Accumulators, + const uint RingId) +{ + // Number of sample iteration for this ring. + const uint RingSamplePairCount = GetRingSamplingPairCount(KernelConfig.SampleSet, RingId); + + // Number of sample pair to process per batch. + // TODO: Could potentially do 4 using symetries? Might be unpracticable because of VGPR pressure. + const uint SamplePairBatchSize = (KernelConfig.SampleSet == SAMPLE_SET_HEXAWEB) ? 1 : 2; + + // Number of batch to process. + const uint BatchCount = RingSamplePairCount / SamplePairBatchSize; + + // Distance of the ring from the center of the kernel in sample count. + const uint RingDistance = uint(RingId + 0); + + // Generate at compile time sample rotation matrix. + const float2x2 SampleRotationMatrix = GetSampleRotationMatrix(RingSamplePairCount); + + // Generates at compile time first sample location on circle (radius = 1). + const float2 FirstCircleUnitPos = GetDiskSampleOnUnitCirle(RingId, RingSamplePairCount, /* BatchId = */ 0); + + // Position of the first sample on circle with radius according to KernelRadius. + float2 FirstCircleSamplePosOffset = (RingDistance * FirstCircleUnitPos) * KernelConfig.KernelSpreadFactor; + + // Setup iteratable SGPR + float2 CurrentCircleUnitPos = FirstCircleUnitPos; + float2 CurrentCircleSamplePosOffset = FirstCircleSamplePosOffset; + + #if CONFIG_SGPR_HINT_OPTIMIZATION + { + CurrentCircleUnitPos = ToScalarMemory(CurrentCircleUnitPos); + CurrentCircleSamplePosOffset = ToScalarMemory(CurrentCircleSamplePosOffset); + } + #endif + + // Loops through all batch of samples to process. + LOOP + for (uint BatchId = 0; BatchId < BatchCount; BatchId++) + { + // Rotate the samples position along the ring. + CurrentCircleUnitPos = mul(CurrentCircleUnitPos, SampleRotationMatrix); + CurrentCircleSamplePosOffset = mul(CurrentCircleSamplePosOffset, SampleRotationMatrix); + + #if CONFIG_SGPR_HINT_OPTIMIZATION + { + CurrentCircleUnitPos = ToScalarMemory(CurrentCircleUnitPos); + CurrentCircleSamplePosOffset = ToScalarMemory(CurrentCircleSamplePosOffset); + } + #endif + + // Sample in batch of multiple pair to increase texture fetch concurency, to have better + // lattency hidding. + UNROLL + for (uint BatchSampleId = 0; BatchSampleId < SamplePairBatchSize; BatchSampleId++) + { + float2 CircleSamplePosOffset = SampleConstFromBatchConst(BatchSampleId, CurrentCircleSamplePosOffset); + + float2 SampleUVPair[2]; + SampleUVPair[0] = KernelConfig.BufferUV + CircleSamplePosOffset * KernelConfig.BufferSizeAndInvSize.zw; + SampleUVPair[1] = KernelConfig.BufferUV - CircleSamplePosOffset * KernelConfig.BufferSizeAndInvSize.zw; + + SampleAndAccumulateMultiplexedSignalsPair( + KernelConfig, + SignalBuffer0, + SignalBuffer1, + SignalBuffer2, + SignalBuffer3, + /* inout */ Accumulators, + SampleUVPair, + /* KernelWeight = */ 1.0); + } // for (uint BatchSampleId = 0; BatchSampleId < SamplePairBatchSize; BatchSampleId++) + } // for (uint BatchId = 0; BatchId < BatchCount; BatchId++) +} + +void ConvolveDiskKernel( + FSSDKernelConfig KernelConfig, + Texture2D SignalBuffer0, + Texture2D SignalBuffer1, + Texture2D SignalBuffer2, + Texture2D SignalBuffer3, + inout FSSDSignalAccumulatorArray Accumulators) +{ + // Accumulate the center of the kernel. + if (KernelConfig.bSampleKernelCenter && !KernelConfig.bDescOrder) + { + SampleAndAccumulateCenterSampleAsItsOwnCluster( + KernelConfig, + SignalBuffer0, + SignalBuffer1, + SignalBuffer2, + SignalBuffer3, + /* inout */ Accumulators); + } + + // Accumulate each ring. Use LOOP, because FXC is going through its pace otherwise. + #if 1 + LOOP + #else + UNROLL + #endif + for ( + uint RingId = (KernelConfig.bDescOrder ? KernelConfig.RingCount : 1); + (KernelConfig.bDescOrder ? RingId > 0 : RingId <= KernelConfig.RingCount); + RingId += (KernelConfig.bDescOrder ? ~0u : 1)) + { + const uint RingSamplePairCount = GetRingSamplingPairCount(KernelConfig.SampleSet, RingId); + + FSSDSampleClusterInfo ClusterInfo; + ClusterInfo.OutterBoundaryRadius = (RingId + 1) * KernelConfig.KernelSpreadFactor; + + StartAccumulatingCluster(KernelConfig, /* inout */ Accumulators, ClusterInfo); + + GatherRingSamples( + KernelConfig, + SignalBuffer0, + SignalBuffer1, + SignalBuffer2, + SignalBuffer3, + /* inout */ Accumulators, + RingId); + + DijestAccumulatedClusterSamples(/* inout */ Accumulators, RingId, RingSamplePairCount * 2); + } // for (uint RingId = 0; RingId < KernelConfig.RingCount; RingId++) + + // Accumulate the center of the kernel. + if (KernelConfig.bSampleKernelCenter && KernelConfig.bDescOrder) + { + SampleAndAccumulateCenterSampleAsItsOwnCluster( + KernelConfig, + SignalBuffer0, + SignalBuffer1, + SignalBuffer2, + SignalBuffer3, + /* inout */ Accumulators); + } +} + + //------------------------------------------------------- MAIN ENTRY POINTS void AccumulateKernel( @@ -820,16 +1181,13 @@ void AccumulateKernel( { if (KernelConfig.bSampleKernelCenter) { - SampleAndAccumulateMultiplexedSignals( + SampleAndAccumulateCenterSampleAsItsOwnCluster( KernelConfig, SignalBuffer0, SignalBuffer1, SignalBuffer2, SignalBuffer3, - /* inout */ Accumulators, - KernelConfig.BufferUV, - /* KernelSampleWeight = */ 1, - /* bForceSample = */ true); + /* inout */ Accumulators); } } else if (KernelConfig.SampleSet == SAMPLE_SET_2X2_BILINEAR) @@ -868,4 +1226,14 @@ void AccumulateKernel( SignalBuffer3, /* inout */ Accumulators); } + else if (KernelConfig.SampleSet == SAMPLE_SET_HEXAWEB) + { + ConvolveDiskKernel( + KernelConfig, + SignalBuffer0, + SignalBuffer1, + SignalBuffer2, + SignalBuffer3, + /* inout */ Accumulators); + } } // AccumulateKernel() diff --git a/Engine/Shaders/Private/ScreenSpaceDenoise/SSDTemporalAccumulation.usf b/Engine/Shaders/Private/ScreenSpaceDenoise/SSDTemporalAccumulation.usf index 3651f21d88e1..2283633e0d77 100644 --- a/Engine/Shaders/Private/ScreenSpaceDenoise/SSDTemporalAccumulation.usf +++ b/Engine/Shaders/Private/ScreenSpaceDenoise/SSDTemporalAccumulation.usf @@ -612,7 +612,7 @@ void TemporallyAccumulate( float NeightborMinBluringRadius = SignalAccumulators.Array[BatchedSignalId].MinInvFrequency; CurrentFrameSample.WorldBluringRadius = min(CurrentFrameSample.WorldBluringRadius, NeightborMinBluringRadius * CurrentFrameSample.AccumulatedSampleCount); - #if CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_MONOCHROMATIC_PENUMBRA && 1 + #if CONFIG_SIGNAL_PROCESSING == SIGNAL_PROCESSING_MONOCHROMATIC_PENUMBRA && 0 DebugOutput[DispatchThreadId] = float4( NeightborMinBluringRadius, NeightborMinBluringRadius == WORLD_RADIUS_MISS, diff --git a/Engine/Shaders/Public/ShaderVersion.ush b/Engine/Shaders/Public/ShaderVersion.ush index c8d7062f13f7..93596f695050 100644 --- a/Engine/Shaders/Public/ShaderVersion.ush +++ b/Engine/Shaders/Public/ShaderVersion.ush @@ -3,4 +3,4 @@ // in Platform.ush (which should be included in any shader) it allows to invalidate the shader DDC. // // If you are merging streams and there is a conflict with this GUID you should make a new GUID rather than taking one or the other. -// GUID = B0A8821C-374F-4F39-92B6-CABBF1F4DA32 +// GUID = C15DFE5B413C0C4E6C49949A06F0AE74 \ No newline at end of file diff --git a/Engine/Source/Developer/AllDesktopTargetPlatform/Private/AllDesktopTargetPlatform.h b/Engine/Source/Developer/AllDesktopTargetPlatform/Private/AllDesktopTargetPlatform.h index cf2d96aa9415..d3da179b992f 100644 --- a/Engine/Source/Developer/AllDesktopTargetPlatform/Private/AllDesktopTargetPlatform.h +++ b/Engine/Source/Developer/AllDesktopTargetPlatform/Private/AllDesktopTargetPlatform.h @@ -82,7 +82,7 @@ public: { } - virtual bool GenerateStreamingInstallManifest(const TMultiMap& ChunkMap, const TSet& ChunkIDsInUse) const override + virtual bool GenerateStreamingInstallManifest(const TMultiMap& PakchunkMap, const TSet& PakchunkIndicesInUse) const override { return true; } diff --git a/Engine/Source/Developer/Android/AndroidTargetPlatform/Private/AndroidTargetPlatform.h b/Engine/Source/Developer/Android/AndroidTargetPlatform/Private/AndroidTargetPlatform.h index b6ae29382678..3d960e6af29e 100644 --- a/Engine/Source/Developer/Android/AndroidTargetPlatform/Private/AndroidTargetPlatform.h +++ b/Engine/Source/Developer/Android/AndroidTargetPlatform/Private/AndroidTargetPlatform.h @@ -144,7 +144,7 @@ public: virtual void GetAllDevices( TArray& OutDevices ) const override; - virtual bool GenerateStreamingInstallManifest(const TMultiMap& ChunkMap, const TSet& ChunkIDsInUse) const override + virtual bool GenerateStreamingInstallManifest(const TMultiMap& PakchunkMap, const TSet& PakchunkIndicesInUse) const override { return true; } diff --git a/Engine/Source/Developer/Apple/MetalShaderFormat/Private/MetalBackend.cpp b/Engine/Source/Developer/Apple/MetalShaderFormat/Private/MetalBackend.cpp index 2fd0a43d5e52..5cd6abcdde5c 100644 --- a/Engine/Source/Developer/Apple/MetalShaderFormat/Private/MetalBackend.cpp +++ b/Engine/Source/Developer/Apple/MetalShaderFormat/Private/MetalBackend.cpp @@ -1,5 +1,5 @@ // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. -// . +// .. #include "MetalBackend.h" #include "MetalShaderFormat.h" @@ -1464,7 +1464,7 @@ protected: { bool bIsStructuredBuffer = (inst->type->inner_type->is_record() || !strncmp(inst->type->name, "RWStructuredBuffer<", 19) || !strncmp(inst->type->name, "StructuredBuffer<", 17)); bool bIsByteAddressBuffer = (!strncmp(inst->type->name, "RWByteAddressBuffer", 19) || !strncmp(inst->type->name, "ByteAddressBuffer", 17)); - if (Buffers.AtomicVariables.find(inst) != Buffers.AtomicVariables.end() || bIsStructuredBuffer || bIsByteAddressBuffer || inst->invariant || (inst->type->components() == 3) || inst->type->inner_type->components() == 3 || Backend.Version <= 2) + if (Buffers.AtomicVariables.find(inst) != Buffers.AtomicVariables.end() || bIsStructuredBuffer || bIsByteAddressBuffer || inst->invariant || ((inst->type->components() == 3) || (Backend.TypedMode == EMetalTypeBufferMode2DSRV || Backend.TypedMode == EMetalTypeBufferModeTBSRV) && inst->type->is_image()) || inst->type->inner_type->components() == 3 || Backend.Version <= 2) { bInsertSideTable |= true; } @@ -2034,7 +2034,8 @@ protected: } break; case ir_txs: - ralloc_asprintf_append(buffer, "int2((int)"); + print_type_pre(tex->type); + ralloc_asprintf_append(buffer, "((int)"); break; default: break; @@ -2310,7 +2311,57 @@ protected: { tex->sampler->accept(this); ralloc_asprintf_append(buffer, ".read("); - tex->coordinate->accept(this); + + if (tex->sampler->type->sampler_array) + { + // Need to split the coordinate + char const* CoordSwizzle = ""; + char const* IndexSwizzle = "y"; + switch(tex->sampler->type->sampler_dimensionality) + { + case GLSL_SAMPLER_DIM_1D: + { + break; + } + case GLSL_SAMPLER_DIM_2D: + case GLSL_SAMPLER_DIM_RECT: + { + CoordSwizzle = "y"; + IndexSwizzle = "z"; + break; + } + case GLSL_SAMPLER_DIM_3D: + { + CoordSwizzle = "yz"; + IndexSwizzle = "w"; + break; + } + case GLSL_SAMPLER_DIM_CUBE: + { + CoordSwizzle = "yz"; + IndexSwizzle = "w"; + break; + } + case GLSL_SAMPLER_DIM_BUF: + case GLSL_SAMPLER_DIM_EXTERNAL: + default: + { + check(0); + break; + } + } + + ralloc_asprintf_append(buffer, "("); + tex->coordinate->accept(this); + + ralloc_asprintf_append(buffer, ").x%s, (uint)(", CoordSwizzle); + tex->coordinate->accept(this); + ralloc_asprintf_append(buffer, ").%s", IndexSwizzle); + } + else + { + tex->coordinate->accept(this); + } if (tex->sampler->type->sampler_ms) { @@ -2482,6 +2533,7 @@ protected: { ralloc_asprintf_append(buffer, tex->sampler->type->sampler_shadow ? "depth_cube_array::" : "texture_cube_array::"); } + else { tex->sampler->accept(this); ralloc_asprintf_append(buffer, "."); @@ -2498,6 +2550,65 @@ protected: tex->lod_info.lod->accept(this); } ralloc_asprintf_append(buffer, ")"); + + if (tex->type->vector_elements == 3) + { + switch(tex->sampler->type->sampler_dimensionality) + { + case GLSL_SAMPLER_DIM_1D: + { + break; + } + case GLSL_SAMPLER_DIM_2D: + case GLSL_SAMPLER_DIM_RECT: + { + if (tex->sampler->type->sampler_array) + { + ralloc_asprintf_append(buffer, ", (int)"); + tex->sampler->accept(this); + ralloc_asprintf_append(buffer, ".get_array_size()"); + } + else + { + check(0); + } + break; + } + case GLSL_SAMPLER_DIM_3D: + { + ralloc_asprintf_append(buffer, ", (int)"); + tex->sampler->accept(this); + ralloc_asprintf_append(buffer, ".get_depth("); + if (tex->lod_info.lod) + { + tex->lod_info.lod->accept(this); + } + ralloc_asprintf_append(buffer, ")"); + break; + } + case GLSL_SAMPLER_DIM_CUBE: + { + if (tex->sampler->type->sampler_array) + { + ralloc_asprintf_append(buffer, tex->sampler->type->sampler_shadow ? ", depth_cube_array::get_array_size(" : ", texture_cube_array::get_array_size("); + tex->sampler->accept(this); + ralloc_asprintf_append(buffer, ")"); + } + else + { + check(0); + } + break; + } + case GLSL_SAMPLER_DIM_BUF: + case GLSL_SAMPLER_DIM_EXTERNAL: + default: + { + check(0); + break; + } + } + } } break; @@ -2685,7 +2796,59 @@ protected: ralloc_asprintf_append(buffer, "("); deref->image->accept(this); ralloc_asprintf_append(buffer, ".read("); - deref->image_index->accept(this); + + if (bIsArray) + { + // Need to split the coordinate + char const* CoordSwizzle = ""; + char const* IndexSwizzle = "y"; + switch(deref->image->type->sampler_dimensionality) + { + case GLSL_SAMPLER_DIM_1D: + { + break; + } + case GLSL_SAMPLER_DIM_2D: + case GLSL_SAMPLER_DIM_RECT: + { + CoordSwizzle = "y"; + IndexSwizzle = "z"; + break; + } + case GLSL_SAMPLER_DIM_3D: + { + CoordSwizzle = "yz"; + IndexSwizzle = "w"; + break; + } + case GLSL_SAMPLER_DIM_CUBE: + { + CoordSwizzle = "yz"; + IndexSwizzle = "w"; + break; + } + case GLSL_SAMPLER_DIM_BUF: + case GLSL_SAMPLER_DIM_EXTERNAL: + default: + { + check(0); + break; + } + } + + ralloc_asprintf_append(buffer, "("); + deref->image_index->accept(this); + + ralloc_asprintf_append(buffer, ").x%s, (uint)(", CoordSwizzle); + deref->image_index->accept(this); + ralloc_asprintf_append(buffer, ").%s", IndexSwizzle); + } + else + { + deref->image_index->accept(this); + } + + ralloc_asprintf_append(buffer, ")"); switch(dst_elements) { @@ -2987,7 +3150,8 @@ protected: // Temp = textureSize(T{, lod}); // Metal // int2 Temp = int2((int)T.get_width({lod}), (int)T.get_height({lod})); - ralloc_asprintf_append(buffer, "int2("); + print_type_pre(deref->type); + ralloc_asprintf_append(buffer, "((int)"); deref->image->accept(this); ralloc_asprintf_append(buffer, ".get_width("); @@ -3003,7 +3167,67 @@ protected: { deref->image_index->accept(this); } - ralloc_asprintf_append(buffer, "))"); + ralloc_asprintf_append(buffer, ")"); + + if (deref->type->vector_elements == 3) + { + switch(deref->image->type->sampler_dimensionality) + { + case GLSL_SAMPLER_DIM_1D: + { + break; + } + case GLSL_SAMPLER_DIM_2D: + case GLSL_SAMPLER_DIM_RECT: + { + if (deref->image->type->sampler_array) + { + ralloc_asprintf_append(buffer, ", (int)"); + deref->image->accept(this); + ralloc_asprintf_append(buffer, ".get_array_size()"); + } + else + { + check(0); + } + break; + } + case GLSL_SAMPLER_DIM_3D: + { + ralloc_asprintf_append(buffer, ", (int)"); + deref->image->accept(this); + ralloc_asprintf_append(buffer, ".get_depth("); + if (deref->image_index) + { + deref->image_index->accept(this); + } + ralloc_asprintf_append(buffer, ")"); + break; + } + case GLSL_SAMPLER_DIM_CUBE: + { + if (deref->image->type->sampler_array) + { + ralloc_asprintf_append(buffer, deref->image->type->sampler_shadow ? ", depth_cube_array::get_array_size(" : ", texture_cube_array::get_array_size("); + deref->image->accept(this); + ralloc_asprintf_append(buffer, ")"); + } + else + { + check(0); + } + break; + } + case GLSL_SAMPLER_DIM_BUF: + case GLSL_SAMPLER_DIM_EXTERNAL: + default: + { + check(0); + break; + } + } + } + ralloc_asprintf_append(buffer, ")"); } else { @@ -4747,10 +4971,18 @@ public: ralloc_asprintf_append(buffer, "#define __METAL_TYPED_BUFFER_READ_IMPL__ 0\n"); ralloc_asprintf_append(buffer, "#define __METAL_TYPED_BUFFER_RW_IMPL__ 0\n"); break; + case EMetalTypeBufferMode2DSRV: + ralloc_asprintf_append(buffer, "#define __METAL_TYPED_BUFFER_READ_IMPL__ 1\n"); + ralloc_asprintf_append(buffer, "#define __METAL_TYPED_BUFFER_RW_IMPL__ 0\n"); + break; case EMetalTypeBufferMode2D: ralloc_asprintf_append(buffer, "#define __METAL_TYPED_BUFFER_READ_IMPL__ 1\n"); ralloc_asprintf_append(buffer, "#define __METAL_TYPED_BUFFER_RW_IMPL__ 1\n"); break; + case EMetalTypeBufferModeTBSRV: + ralloc_asprintf_append(buffer, "#define __METAL_TYPED_BUFFER_READ_IMPL__ 3\n"); + ralloc_asprintf_append(buffer, "#define __METAL_TYPED_BUFFER_RW_IMPL__ 0\n"); + break; case EMetalTypeBufferModeTB: ralloc_asprintf_append(buffer, "#define __METAL_TYPED_BUFFER_READ_IMPL__ 3\n"); ralloc_asprintf_append(buffer, "#define __METAL_TYPED_BUFFER_RW_IMPL__ 3\n"); diff --git a/Engine/Source/Developer/Apple/MetalShaderFormat/Private/MetalBackend.h b/Engine/Source/Developer/Apple/MetalShaderFormat/Private/MetalBackend.h index efef977c5454..2f37824fc399 100644 --- a/Engine/Source/Developer/Apple/MetalShaderFormat/Private/MetalBackend.h +++ b/Engine/Source/Developer/Apple/MetalShaderFormat/Private/MetalBackend.h @@ -76,8 +76,10 @@ enum EMetalGPUSemantics enum EMetalTypeBufferMode { EMetalTypeBufferModeRaw = 0, // No typed buffers - EMetalTypeBufferMode2D = 2, // Buffer<> SRVs & RWBuffer<> UAVs are typed via 2D textures - EMetalTypeBufferModeTB = 3, // Buffer<> SRVs & RWBuffer<> UAVs are typed via texture-buffers + EMetalTypeBufferMode2DSRV = 1, // Buffer<> SRVs are typed via 2D textures, RWBuffer<> UAVs are raw buffers + EMetalTypeBufferModeTBSRV = 2, // Buffer<> SRVs are typed via texture-buffers, RWBuffer<> UAVs are raw buffers + EMetalTypeBufferMode2D = 3, // Buffer<> SRVs & RWBuffer<> UAVs are typed via 2D textures + EMetalTypeBufferModeTB = 4, // Buffer<> SRVs & RWBuffer<> UAVs are typed via texture-buffers }; // Metal supports 16 across all HW diff --git a/Engine/Source/Developer/Apple/MetalShaderFormat/Private/MetalShaderCompiler.cpp b/Engine/Source/Developer/Apple/MetalShaderFormat/Private/MetalShaderCompiler.cpp index 74cb4372c251..9748af6b8617 100644 --- a/Engine/Source/Developer/Apple/MetalShaderFormat/Private/MetalShaderCompiler.cpp +++ b/Engine/Source/Developer/Apple/MetalShaderFormat/Private/MetalShaderCompiler.cpp @@ -1679,6 +1679,14 @@ void BuildMetalShaderOutput( Defines += TEXT(" -D__METAL_TYPED_BUFFER_READ_IMPL__=0"); Defines += TEXT(" -D__METAL_TYPED_BUFFER_RW_IMPL__=0"); break; + case EMetalTypeBufferMode2DSRV: + Defines += TEXT(" -D__METAL_TYPED_BUFFER_READ_IMPL__=1"); + Defines += TEXT(" -D__METAL_TYPED_BUFFER_RW_IMPL__=0"); + break; + case EMetalTypeBufferModeTBSRV: + Defines += TEXT(" -D__METAL_TYPED_BUFFER_READ_IMPL__=3"); + Defines += TEXT(" -D__METAL_TYPED_BUFFER_RW_IMPL__=0"); + break; case EMetalTypeBufferMode2D: Defines += TEXT(" -D__METAL_TYPED_BUFFER_READ_IMPL__=1"); Defines += TEXT(" -D__METAL_TYPED_BUFFER_RW_IMPL__=1"); @@ -2058,10 +2066,12 @@ void CompileShader_Metal(const FShaderCompilerInput& _Input,FShaderCompilerOutpu if (bAppleTV) { MinOSVersion = TEXT("-mtvos-version-min=12.0"); + TypeMode = EMetalTypeBufferModeTBSRV; } else if (bIsMobile) { MinOSVersion = TEXT("-mios-version-min=12.0"); + TypeMode = EMetalTypeBufferModeTBSRV; } else { @@ -2076,10 +2086,12 @@ void CompileShader_Metal(const FShaderCompilerInput& _Input,FShaderCompilerOutpu if (bAppleTV) { MinOSVersion = TEXT("-mtvos-version-min=11.0"); + TypeMode = EMetalTypeBufferMode2DSRV; } else if (bIsMobile) { MinOSVersion = TEXT("-mios-version-min=11.0"); + TypeMode = EMetalTypeBufferMode2DSRV; } else { @@ -2094,10 +2106,12 @@ void CompileShader_Metal(const FShaderCompilerInput& _Input,FShaderCompilerOutpu if (bAppleTV) { MinOSVersion = TEXT("-mtvos-version-min=10.0"); + TypeMode = EMetalTypeBufferMode2DSRV; } else if (bIsMobile) { MinOSVersion = TEXT("-mios-version-min=10.0"); + TypeMode = EMetalTypeBufferMode2DSRV; } else { diff --git a/Engine/Source/Developer/Apple/MetalShaderFormat/Private/MetalUtils.cpp b/Engine/Source/Developer/Apple/MetalShaderFormat/Private/MetalUtils.cpp index 3b1687e8fc65..555a248e4e40 100644 --- a/Engine/Source/Developer/Apple/MetalShaderFormat/Private/MetalUtils.cpp +++ b/Engine/Source/Developer/Apple/MetalShaderFormat/Private/MetalUtils.cpp @@ -1253,6 +1253,12 @@ void FMetalCodeBackend::MovePackedUniformsToMain(exec_list* ir, _mesa_glsl_parse bIsBuffer = (!Var->type->is_sampler() && !Var->type->is_image()) || Var->type->sampler_buffer; break; } + case EMetalTypeBufferMode2DSRV: + case EMetalTypeBufferModeTBSRV: + { + bIsBuffer = (!Var->type->is_sampler() && !Var->type->is_image()) || (Var->type->sampler_buffer && (Var->type->is_image() || OutBuffers.AtomicVariables.find(Var) != OutBuffers.AtomicVariables.end() || bIsStructuredBuffer || bIsInvariant || bIsByteAddressBuffer)) || bIsVec3; + break; + } case EMetalTypeBufferMode2D: case EMetalTypeBufferModeTB: { @@ -3381,7 +3387,7 @@ struct FDeReferencePackedVarsVisitor final : public ir_rvalue_visitor { if (SwizzleValDeRefRecord->type->vector_elements > 1 && SwizzleValDeRefRecord->type->vector_elements < 4) { - auto* Var = GetVar(SwizzleValDeRefRecord); + auto* Var = GetVar(StructVar, SwizzleValDeRefRecord); Swizzle->val = new(State)ir_dereference_variable(Var); } } @@ -3393,7 +3399,7 @@ struct FDeReferencePackedVarsVisitor final : public ir_rvalue_visitor { if (DeRefRecord->type->vector_elements > 1 && DeRefRecord->type->vector_elements < 4) { - auto* Var = GetVar(DeRefRecord); + auto* Var = GetVar(StructVar, DeRefRecord); *RValuePtr = new(State)ir_dereference_variable(Var); } } @@ -3401,7 +3407,7 @@ struct FDeReferencePackedVarsVisitor final : public ir_rvalue_visitor { if (DeRefRecord->type->vector_elements > 1 && DeRefRecord->type->vector_elements < 4) { - auto* Var = GetVar(DeRefRecord); + auto* Var = GetVar(StructVar, DeRefRecord); *RValuePtr = new(State)ir_dereference_variable(Var); } } @@ -3409,8 +3415,9 @@ struct FDeReferencePackedVarsVisitor final : public ir_rvalue_visitor } std::map> Replaced; + std::map> Replacements; - ir_variable* GetVar(ir_dereference_record* ir) + ir_variable* GetVar(ir_variable* Orig, ir_dereference_record* ir) { ir_variable* Var = nullptr; for (auto& Pair : Replaced) @@ -3426,6 +3433,7 @@ struct FDeReferencePackedVarsVisitor final : public ir_rvalue_visitor { Var = new(State)ir_variable(ir->type, nullptr, ir_var_temporary); Replaced[ir] = Var; + Replacements[Var] = Orig; } return Var; } @@ -3440,15 +3448,28 @@ void FMetalCodeBackend::RemovePackedVarReferences(exec_list* ir, _mesa_glsl_pars { return; } - + ir_function_signature* Main = GetMainFunction(ir); + for (auto& Pair : Visitor.Replacements) + { + auto* NewVar = Pair.first; + auto* OldVar = Pair.second; + if (OldVar->mode == ir_var_uniform || OldVar->mode == ir_var_in || OldVar->mode == ir_var_out) + { + Main->body.push_head(NewVar); + } + else + { + OldVar->insert_after(NewVar); + } + } + for (auto& OuterPair : Visitor.Replaced) { auto* NewVar = OuterPair.second; auto* DeRefRecord = OuterPair.first; auto* NewAssignment = new(State)ir_assignment(new(State)ir_dereference_variable(NewVar), DeRefRecord); - Main->body.push_head(NewAssignment); - Main->body.push_head(NewVar); + NewVar->insert_after(NewAssignment); } } diff --git a/Engine/Source/Developer/AssetTools/Private/AssetTypeActions/AssetTypeActions_SkeletalMesh.cpp b/Engine/Source/Developer/AssetTools/Private/AssetTypeActions/AssetTypeActions_SkeletalMesh.cpp index 53129d5b28eb..bf7556eb7241 100644 --- a/Engine/Source/Developer/AssetTools/Private/AssetTypeActions/AssetTypeActions_SkeletalMesh.cpp +++ b/Engine/Source/Developer/AssetTools/Private/AssetTypeActions/AssetTypeActions_SkeletalMesh.cpp @@ -648,7 +648,7 @@ void FAssetTypeActions_SkeletalMesh::GetSourceFileLabels(const TArray& SkeletalMesh->AssetImportData->ExtractFilenames(SourceFilePaths); for (int32 SourceIndex = 0; SourceIndex < SourceFilePaths.Num(); ++SourceIndex) { - FText SourceIndexLabel = SourceIndex == 0 ? NSSkeletalMeshSourceFileLabels::GeoAndSkinningText() : SourceIndex == 1 ? NSSkeletalMeshSourceFileLabels::GeometryText() : NSSkeletalMeshSourceFileLabels::SkinningText(); + FText SourceIndexLabel = USkeletalMesh::GetSourceFileLabelFromIndex(SourceIndex); OutSourceFileLabels.Add(SourceIndexLabel.ToString()); } } diff --git a/Engine/Source/Developer/BlueprintCompilerCppBackend/Private/BlueprintCompilerCppBackendUMG.cpp b/Engine/Source/Developer/BlueprintCompilerCppBackend/Private/BlueprintCompilerCppBackendUMG.cpp index 9e9b8cf9f731..a6d3e95252a7 100644 --- a/Engine/Source/Developer/BlueprintCompilerCppBackend/Private/BlueprintCompilerCppBackendUMG.cpp +++ b/Engine/Source/Developer/BlueprintCompilerCppBackend/Private/BlueprintCompilerCppBackendUMG.cpp @@ -34,29 +34,41 @@ void FBackendHelperUMG::AdditionalHeaderIncludeForWidget(FEmitterLocalContext& C void FBackendHelperUMG::CreateClassSubobjects(FEmitterLocalContext& Context, bool bCreate, bool bInitialize) { - if (auto WidgetClass = Cast(Context.GetCurrentlyGeneratedClass())) + if (UWidgetBlueprintGeneratedClass* WidgetClass = Cast(Context.GetCurrentlyGeneratedClass())) { - if (WidgetClass->WidgetTree) - { - ensure(WidgetClass->WidgetTree->GetOuter() == Context.GetCurrentlyGeneratedClass()); - FEmitDefaultValueHelper::HandleClassSubobject(Context, WidgetClass->WidgetTree, FEmitterLocalContext::EClassSubobjectList::MiscConvertedSubobjects, bCreate, bInitialize); - } - for (auto Anim : WidgetClass->Animations) - { - ensure(Anim->GetOuter() == Context.GetCurrentlyGeneratedClass()); + // Currently nativization does not support widget templates. This method will need to be revised if that changes. + check(!WidgetClass->HasTemplate()); + + // Child widgets may actually use the widget tree from a parent class. + // - @see UUserWidget::Initialize() + WidgetClass = WidgetClass->FindWidgetTreeOwningClass(); - // We need the same regeneration like for cooking. See UMovieSceneSequence::Serialize - FMovieSceneSequencePrecompiledTemplateStore Store; - FMovieSceneCompiler::Compile(*Anim, Store); + // Initialize the WidgetTree only if it's owned by the current widget class. + if (WidgetClass == Context.GetCurrentlyGeneratedClass()) + { + if (WidgetClass->WidgetTree) + { + ensure(WidgetClass->WidgetTree->GetOuter() == Context.GetCurrentlyGeneratedClass()); + FEmitDefaultValueHelper::HandleClassSubobject(Context, WidgetClass->WidgetTree, FEmitterLocalContext::EClassSubobjectList::MiscConvertedSubobjects, bCreate, bInitialize); + } - FEmitDefaultValueHelper::HandleClassSubobject(Context, Anim, FEmitterLocalContext::EClassSubobjectList::MiscConvertedSubobjects, bCreate, bInitialize); + for (UWidgetAnimation* Anim : WidgetClass->Animations) + { + ensure(Anim->GetOuter() == Context.GetCurrentlyGeneratedClass()); + + // We need the same regeneration like for cooking. See UMovieSceneSequence::Serialize + FMovieSceneSequencePrecompiledTemplateStore Store; + FMovieSceneCompiler::Compile(*Anim, Store); + + FEmitDefaultValueHelper::HandleClassSubobject(Context, Anim, FEmitterLocalContext::EClassSubobjectList::MiscConvertedSubobjects, bCreate, bInitialize); + } } } } void FBackendHelperUMG::EmitWidgetInitializationFunctions(FEmitterLocalContext& Context) { - if (auto WidgetClass = Cast(Context.GetCurrentlyGeneratedClass())) + if (UWidgetBlueprintGeneratedClass* WidgetClass = Cast(Context.GetCurrentlyGeneratedClass())) { Context.ResetPropertiesForInaccessibleStructs(); @@ -92,18 +104,56 @@ void FBackendHelperUMG::EmitWidgetInitializationFunctions(FEmitterLocalContext& Context.AddLine(TEXT("{")); Context.IncreaseIndent(); - const FString WidgetTreeStr = Context.FindGloballyMappedObject(WidgetClass->WidgetTree, UWidgetTree::StaticClass(), true); - ensure(!WidgetTreeStr.IsEmpty()); - const FString AnimationsArrayNativeName = GenerateLocalProperty(Context, FindFieldChecked(UWidgetBlueprintGeneratedClass::StaticClass(), TEXT("Animations")), reinterpret_cast(&WidgetClass->Animations)); - const FString BindingsArrayNativeName = GenerateLocalProperty(Context, FindFieldChecked(UWidgetBlueprintGeneratedClass::StaticClass(), TEXT("Bindings")), reinterpret_cast(&WidgetClass->Bindings)); - - Context.AddLine(FString::Printf(TEXT("UWidgetBlueprintGeneratedClass::%s(this, GetClass(), %s, %s, %s, %s, %s);") - , GET_FUNCTION_NAME_STRING_CHECKED(UWidgetBlueprintGeneratedClass, InitializeWidgetStatic) - , WidgetClass->HasTemplate() ? TEXT("true") : TEXT("false") - , WidgetClass->bAllowDynamicCreation ? TEXT("true") : TEXT("false") - , *WidgetTreeStr - , *AnimationsArrayNativeName - , *BindingsArrayNativeName)); + // Child widgets may actually use the widget tree from a parent class. + // - @see UUserWidget::Initialize() + UWidgetBlueprintGeneratedClass* WidgetTreeOwningClass = WidgetClass->FindWidgetTreeOwningClass(); + + // If we have a valid WidgetTree instance, emit code to initialize the widget using the owning class. + if (WidgetTreeOwningClass->WidgetTree != nullptr) + { + FString WidgetClassStr; + FString WidgetTreeStr; + + if (WidgetClass == WidgetTreeOwningClass) + { + // Simple case - WidgetTree instance is owned by the current class. + WidgetClassStr = TEXT("GetClass()"); + + // This object was already created as a class-owned subobject and mapped to the 'WidgetTree' value. + // - @see CreateClassSubobjects() + WidgetTreeStr = Context.FindGloballyMappedObject(WidgetClass->WidgetTree, UWidgetTree::StaticClass()); + } + else + { + // Emit code to assign the owning class to a local variable. + WidgetClassStr = Context.GenerateUniqueLocalName(); + Context.AddLine(FString::Printf(TEXT("UClass* %s = %s;"), + *WidgetClassStr, + *Context.FindGloballyMappedObject(WidgetTreeOwningClass, UClass::StaticClass(), true))); + + // Emit code to locate and assign the owning class's WidgetTree instance to a local variable. This will have been created as part of the owning class's ctor, but note + // that we have to look it up by name/outer because the converted class is a UDynamicClass and not a UWidgetBlueprintGeneratedClass, so there is no 'WidgetTree' member. + WidgetTreeStr = Context.GenerateUniqueLocalName(); + Context.AddLine(FString::Printf(TEXT("UWidgetTree* %s = CastChecked(StaticFindObjectFast(UWidgetTree::StaticClass(), %s, TEXT(\"WidgetTree\")));"), + *WidgetTreeStr, + *WidgetClassStr)); + } + + ensure(!WidgetTreeStr.IsEmpty()); + ensure(!WidgetClassStr.IsEmpty()); + + const FString AnimationsArrayNativeName = GenerateLocalProperty(Context, FindFieldChecked(UWidgetBlueprintGeneratedClass::StaticClass(), TEXT("Animations")), reinterpret_cast(&WidgetTreeOwningClass->Animations)); + const FString BindingsArrayNativeName = GenerateLocalProperty(Context, FindFieldChecked(UWidgetBlueprintGeneratedClass::StaticClass(), TEXT("Bindings")), reinterpret_cast(&WidgetTreeOwningClass->Bindings)); + + Context.AddLine(FString::Printf(TEXT("UWidgetBlueprintGeneratedClass::%s(this, %s, %s, %s, %s, %s, %s);") + , GET_FUNCTION_NAME_STRING_CHECKED(UWidgetBlueprintGeneratedClass, InitializeWidgetStatic) + , *WidgetClassStr + , WidgetTreeOwningClass->HasTemplate() ? TEXT("true") : TEXT("false") + , WidgetTreeOwningClass->bAllowDynamicCreation ? TEXT("true") : TEXT("false") + , *WidgetTreeStr + , *AnimationsArrayNativeName + , *BindingsArrayNativeName)); + } Context.DecreaseIndent(); Context.AddLine(TEXT("}")); diff --git a/Engine/Source/Developer/DesktopPlatform/Private/DesktopPlatformBase.cpp b/Engine/Source/Developer/DesktopPlatform/Private/DesktopPlatformBase.cpp index 140cff3765f9..1428bdc1237d 100644 --- a/Engine/Source/Developer/DesktopPlatform/Private/DesktopPlatformBase.cpp +++ b/Engine/Source/Developer/DesktopPlatform/Private/DesktopPlatformBase.cpp @@ -637,40 +637,6 @@ bool FDesktopPlatformBase::GenerateProjectFiles(const FString& RootDir, const FS return bRes; } -bool FDesktopPlatformBase::InvalidateMakefiles(const FString& RootDir, const FString& ProjectFileName, FFeedbackContext* Warn) -{ - // Composes the platform, and config (eg, "Win64 Development") - FString Arguments = FString::Printf(TEXT("%s %s"), FPlatformMisc::GetUBTPlatform(), FModuleManager::GetUBTConfiguration()); - - // -TargetType=Editor tells UBT to work out the editor target name from the project we provided - Arguments += TEXT(" -TargetType=Editor"); - - // Add the project path - if ( !ProjectFileName.IsEmpty() ) - { - Arguments += FString::Printf(TEXT(" -Project=\"%s\""), *IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*ProjectFileName)); - } - - // -invalidatemakefilesonly tells UBT to invalidate its UBT makefiles without building - Arguments += TEXT(" -invalidatemakefilesonly"); - - // Compile UnrealBuildTool if it doesn't exist. This can happen if we're just copying source from somewhere. - bool bRes = true; - Warn->BeginSlowTask(LOCTEXT("InvalidateMakefiles", "Invalidating makefiles..."), true, true); - if(!FPaths::FileExists(GetUnrealBuildToolExecutableFilename(RootDir))) - { - Warn->StatusUpdate(0, 1, LOCTEXT("BuildingUBT", "Building UnrealBuildTool...")); - bRes = BuildUnrealBuildTool(RootDir, *Warn); - } - if(bRes) - { - Warn->StatusUpdate(0, 1, LOCTEXT("InvalidateMakefiles", "Invalidating makefiles...")); - bRes = RunUnrealBuildTool(LOCTEXT("InvalidateMakefiles", "Invalidating makefiles..."), RootDir, Arguments, Warn); - } - Warn->EndSlowTask(); - return bRes; -} - bool FDesktopPlatformBase::IsUnrealBuildToolAvailable() { // If using installed build and the unreal build tool executable exists, then UBT is available. Otherwise check it can be built. diff --git a/Engine/Source/Developer/DesktopPlatform/Private/DesktopPlatformBase.h b/Engine/Source/Developer/DesktopPlatform/Private/DesktopPlatformBase.h index 5e8a9cf27250..0d5e2151eb2f 100644 --- a/Engine/Source/Developer/DesktopPlatform/Private/DesktopPlatformBase.h +++ b/Engine/Source/Developer/DesktopPlatform/Private/DesktopPlatformBase.h @@ -45,7 +45,6 @@ public: virtual bool CleanGameProject(const FString& ProjectDir, FString& OutFailPath, FFeedbackContext* Warn) override; virtual bool CompileGameProject(const FString& RootDir, const FString& ProjectFileName, FFeedbackContext* Warn) override; virtual bool GenerateProjectFiles(const FString& RootDir, const FString& ProjectFileName, FFeedbackContext* Warn, FString LogFilePath = FString()) override; - virtual bool InvalidateMakefiles(const FString& RootDir, const FString& ProjectFileName, FFeedbackContext* Warn) override; virtual bool IsUnrealBuildToolAvailable() override; virtual bool InvokeUnrealBuildToolSync(const FString& InCmdLineParams, FOutputDevice &Ar, bool bSkipBuildUBT, int32& OutReturnCode, FString& OutProcOutput) override; virtual FProcHandle InvokeUnrealBuildToolAsync(const FString& InCmdLineParams, FOutputDevice &Ar, void*& OutReadPipe, void*& OutWritePipe, bool bSkipBuildUBT = false) override; diff --git a/Engine/Source/Developer/DesktopPlatform/Public/IDesktopPlatform.h b/Engine/Source/Developer/DesktopPlatform/Public/IDesktopPlatform.h index 5bbed40ea493..32afd069feb7 100644 --- a/Engine/Source/Developer/DesktopPlatform/Public/IDesktopPlatform.h +++ b/Engine/Source/Developer/DesktopPlatform/Public/IDesktopPlatform.h @@ -348,16 +348,6 @@ public: */ virtual bool GenerateProjectFiles(const FString& RootDir, const FString& ProjectFileName, FFeedbackContext* Warn, FString LogFilePath = FString()) = 0; - /** - * Invalidate makefiles for project (to UBT regenerate them at startup). - * - * @param RootDir Engine root directory for the project to use. - * @param ProjectFileName Filename of the project to update - * @param Warn Feedback context to use for progress updates - * @return true if project files were generated successfully. - */ - virtual bool InvalidateMakefiles(const FString& RootDir, const FString& ProjectFileName, FFeedbackContext* Warn) = 0; - /** * Determines whether UnrealBuildTool is available * diff --git a/Engine/Source/Developer/FunctionalTesting/Private/AutomationBlueprintFunctionLibrary.cpp b/Engine/Source/Developer/FunctionalTesting/Private/AutomationBlueprintFunctionLibrary.cpp index 1ed27d88d112..3a03e4fd1559 100644 --- a/Engine/Source/Developer/FunctionalTesting/Private/AutomationBlueprintFunctionLibrary.cpp +++ b/Engine/Source/Developer/FunctionalTesting/Private/AutomationBlueprintFunctionLibrary.cpp @@ -40,6 +40,8 @@ #include "Scalability.h" #include "SceneViewExtension.h" #include "SceneView.h" +#include "Engine/GameEngine.h" +#include "Engine/LevelStreaming.h" #define LOCTEXT_NAMESPACE "Automation" @@ -492,12 +494,21 @@ void UAutomationBlueprintFunctionLibrary::FinishLoadingBeforeScreenshot() FModuleManager::GetModuleChecked("AutomationController").GetAutomationController()->ResetAutomationTestTimeout(TEXT("shader compilation")); } + FlushAsyncLoading(); + + // Make sure we finish all level streaming + if (UGameEngine* GameEngine = Cast(GEngine)) + { + if (UWorld* GameWorld = GameEngine->GetGameWorld()) + { + GameWorld->FlushLevelStreaming(EFlushLevelStreamingType::Full); + } + } + // Force all mip maps to load before taking the screenshot. UTexture::ForceUpdateTextureStreaming(); IStreamingManager::Get().StreamAllResources(0.0f); - - //IStreamingManager::Get(). } FIntPoint UAutomationBlueprintFunctionLibrary::GetAutomationScreenshotSize(const FAutomationScreenshotOptions& Options) @@ -797,6 +808,103 @@ bool UAutomationBlueprintFunctionLibrary::AreAutomatedTestsRunning() return GIsAutomationTesting; } +class FWaitForLoadingToFinish : public FPendingLatentAction +{ +public: + FWaitForLoadingToFinish(const FLatentActionInfo& LatentInfo) + : ExecutionFunction(LatentInfo.ExecutionFunction) + , OutputLink(LatentInfo.Linkage) + , CallbackTarget(LatentInfo.CallbackTarget) + { + WaitingFrames = 0; + UAutomationBlueprintFunctionLibrary::FinishLoadingBeforeScreenshot(); + } + + virtual ~FWaitForLoadingToFinish() + { + } + + bool AnyLevelStreaming() + { + // Make sure we finish all level streaming + if (UGameEngine* GameEngine = Cast(GEngine)) + { + if (UWorld* GameWorld = GameEngine->GetGameWorld()) + { + for (ULevelStreaming* LevelStreaming : GameWorld->GetStreamingLevels()) + { + // See whether there's a level with a pending request. + if (LevelStreaming) + { + if (LevelStreaming->HasLoadRequestPending()) + { + return true; + } + } + } + } + } + + return false; + } + + virtual void UpdateOperation(FLatentResponse& Response) override + { + bool bResetWaiting = false; + + if (IsAsyncLoading()) + { + bResetWaiting = true; + } + else if (AnyLevelStreaming()) + { + bResetWaiting = true; + } + + if (bResetWaiting) + { + WaitingFrames = 0; + } + else + { + WaitingFrames++; + } + + if (WaitingFrames > 60) + { + Response.FinishAndTriggerIf(true, ExecutionFunction, OutputLink, CallbackTarget); + } + } + +#if WITH_EDITOR + // Returns a human readable description of the latent operation's current state + virtual FString GetDescription() const override + { + return TEXT("Waiting For Loading"); + } +#endif + +private: + FName ExecutionFunction; + int32 OutputLink; + FWeakObjectPtr CallbackTarget; + + int32 WaitingFrames; +}; + + +void UAutomationBlueprintFunctionLibrary::AutomationWaitForLoading(UObject* WorldContextObject, FLatentActionInfo LatentInfo) +{ + if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)) + { + FLatentActionManager& LatentActionManager = World->GetLatentActionManager(); + if (LatentActionManager.FindExistingAction(LatentInfo.CallbackTarget, LatentInfo.UUID) == nullptr) + { + LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FWaitForLoadingToFinish(LatentInfo)); + } + } +} + bool UAutomationBlueprintFunctionLibrary::TakeHighResScreenshot(int32 ResX, int32 ResY, FString Filename, ACameraActor* Camera, bool bMaskEnabled, bool bCaptureHDR) { #if WITH_EDITOR diff --git a/Engine/Source/Developer/FunctionalTesting/Public/AutomationBlueprintFunctionLibrary.h b/Engine/Source/Developer/FunctionalTesting/Public/AutomationBlueprintFunctionLibrary.h index 6258d7e7ec28..f8d60e8b9f3f 100644 --- a/Engine/Source/Developer/FunctionalTesting/Public/AutomationBlueprintFunctionLibrary.h +++ b/Engine/Source/Developer/FunctionalTesting/Public/AutomationBlueprintFunctionLibrary.h @@ -74,10 +74,13 @@ public: UFUNCTION(BlueprintPure, Category="Automation") static bool AreAutomatedTestsRunning(); + UFUNCTION(BlueprintCallable, Category = "Automation", meta = (Latent, HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject", LatentInfo = "LatentInfo")) + static void AutomationWaitForLoading(UObject* WorldContextObject, FLatentActionInfo LatentInfo); + /** * take high res screenshot in editor. */ - UFUNCTION(BlueprintPure, Category = "Automation") + UFUNCTION(BlueprintCallable, Category = "Automation") static bool TakeHighResScreenshot(int32 ResX, int32 ResY, FString Filename, ACameraActor* Camera = nullptr, bool bMaskEnabled = false, bool bCaptureHDR = false); /** diff --git a/Engine/Source/Developer/HTML5/HTML5TargetPlatform/Private/HTML5TargetPlatform.h b/Engine/Source/Developer/HTML5/HTML5TargetPlatform/Private/HTML5TargetPlatform.h index 2072e62b686d..1643531f0b09 100644 --- a/Engine/Source/Developer/HTML5/HTML5TargetPlatform/Private/HTML5TargetPlatform.h +++ b/Engine/Source/Developer/HTML5/HTML5TargetPlatform/Private/HTML5TargetPlatform.h @@ -40,7 +40,7 @@ public: virtual void GetAllDevices( TArray& OutDevices ) const override; - virtual bool GenerateStreamingInstallManifest(const TMultiMap& ChunkMap, const TSet& ChunkIDsInUse) const override + virtual bool GenerateStreamingInstallManifest(const TMultiMap& PakchunkMap, const TSet& PakchunkIndicesInUse) const override { return true; } diff --git a/Engine/Source/Developer/HotReload/HotReload.Build.cs b/Engine/Source/Developer/HotReload/HotReload.Build.cs index 863072658d37..ed22e0d80c1f 100644 --- a/Engine/Source/Developer/HotReload/HotReload.Build.cs +++ b/Engine/Source/Developer/HotReload/HotReload.Build.cs @@ -5,33 +5,38 @@ public class HotReload : ModuleRules { public HotReload(ReadOnlyTargetRules Target) : base(Target) { - PublicDependencyModuleNames.AddRange( - new string[] + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "CoreUObject", + } + ); + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "Analytics", + "DirectoryWatcher", + "DesktopPlatform", + "Projects" + } + ); + + if (Target.bCompileAgainstEngine) + { + PrivateDependencyModuleNames.AddRange( + new string[] { - "Core", - "CoreUObject", + "Engine", + "UnrealEd", } ); + } - PrivateDependencyModuleNames.AddRange( - new string[] - { - "Analytics", - "DirectoryWatcher", - "DesktopPlatform", - "Projects" - } - ); - - if (Target.bCompileAgainstEngine) - { - PrivateDependencyModuleNames.AddRange( - new string[] - { - "Engine", - "UnrealEd", - } - ); - } + if(Target.bWithLiveCoding) + { + PrivateIncludePathModuleNames.Add("LiveCoding"); + } } } diff --git a/Engine/Source/Developer/HotReload/Private/HotReload.cpp b/Engine/Source/Developer/HotReload/Private/HotReload.cpp index 66b1df10e7ee..2c70f5b676f3 100644 --- a/Engine/Source/Developer/HotReload/Private/HotReload.cpp +++ b/Engine/Source/Developer/HotReload/Private/HotReload.cpp @@ -42,6 +42,10 @@ #include "Misc/ScopeExit.h" #include "Algo/Transform.h" +#if WITH_LIVE_CODING +#include "ILiveCodingModule.h" +#endif + #if WITH_EDITOR #include "Editor.h" #endif @@ -200,7 +204,7 @@ private: void ReplaceReferencesToReconstructedCDOs(); #if WITH_ENGINE - void RegisterForReinstancing(UClass* OldClass, UClass* NewClass); + void RegisterForReinstancing(UClass* OldClass, UClass* NewClass, EHotReloadedClassFlags Flags); void ReinstanceClasses(); /** @@ -601,6 +605,16 @@ FString FHotReloadModule::GetModuleCompileMethod(FName InModuleName) bool FHotReloadModule::RecompileModule(const FName InModuleName, const bool bReloadAfterRecompile, FOutputDevice &Ar, bool bFailIfGeneratedCodeChanges, bool bForceCodeProject) { #if WITH_HOT_RELOAD + +#if WITH_LIVE_CODING + ILiveCodingModule* LiveCoding = FModuleManager::GetModulePtr(LIVE_CODING_MODULE_NAME); + if (LiveCoding != nullptr && LiveCoding->IsEnabledForSession()) + { + UE_LOG(LogHotReload, Error, TEXT("Unable to hot-reload modules while Live Coding is enabled.")); + return false; + } +#endif + UE_LOG(LogHotReload, Log, TEXT("Recompiling module %s..."), *InModuleName.ToString()); // This is an internal request for hot-reload (not from IDE) @@ -1205,15 +1219,23 @@ namespace { } } -void FHotReloadModule::RegisterForReinstancing(UClass* OldClass, UClass* NewClass) +void FHotReloadModule::RegisterForReinstancing(UClass* OldClass, UClass* NewClass, EHotReloadedClassFlags Flags) { TPair Pair; Pair.Key = OldClass; Pair.Value = NewClass; - TArray >& ClassesToReinstance = GetClassesToReinstance(); - ClassesToReinstance.Add(MoveTemp(Pair)); + // Don't allow reinstancing of UEngine classes + if (!OldClass->IsChildOf(UEngine::StaticClass()) || !(Flags & EHotReloadedClassFlags::Changed)) + { + TArray >& ClassesToReinstance = GetClassesToReinstance(); + ClassesToReinstance.Add(MoveTemp(Pair)); + } + else + { + UE_LOG(LogHotReload, Warning, TEXT("Engine class '%s' has changed but will be ignored for hot reload"), *NewClass->GetName()); + } } void FHotReloadModule::ReinstanceClasses() @@ -1230,13 +1252,6 @@ void FHotReloadModule::ReinstanceClasses() TMap OldToNewClassesMap; for (const TPair& Pair : ClassesToReinstance) { - // Don't allow reinstancing of UEngine classes - if (Pair.Key->IsChildOf(UEngine::StaticClass())) - { - UE_LOG(LogHotReload, Warning, TEXT("Engine class '%s' has changed but will be ignored for hot reload"), *Pair.Key->GetName()); - continue; - } - if (Pair.Value != nullptr) { OldToNewClassesMap.Add(Pair.Key, Pair.Value); @@ -1245,11 +1260,7 @@ void FHotReloadModule::ReinstanceClasses() for (const TPair& Pair : ClassesToReinstance) { - // Don't allow reinstancing of UEngine classes - if (!Pair.Key->IsChildOf(UEngine::StaticClass())) - { - ReinstanceClass(Pair.Key, Pair.Value, OldToNewClassesMap); - } + ReinstanceClass(Pair.Key, Pair.Value, OldToNewClassesMap); } ClassesToReinstance.Empty(); @@ -1423,6 +1434,15 @@ bool FHotReloadModule::Tick(float DeltaTime) return true; } + // Early out if live coding is enabled +#if WITH_LIVE_CODING + ILiveCodingModule* LiveCoding = FModuleManager::GetModulePtr(LIVE_CODING_MODULE_NAME); + if (LiveCoding != nullptr && LiveCoding->IsEnabledForSession()) + { + return false; + } +#endif + #if WITH_EDITOR if (GEditor) { diff --git a/Engine/Source/Developer/IOS/IOSPlatformEditor/Private/IOSTargetSettingsCustomization.cpp b/Engine/Source/Developer/IOS/IOSPlatformEditor/Private/IOSTargetSettingsCustomization.cpp index 94667bb7cb1a..a22e296ac721 100644 --- a/Engine/Source/Developer/IOS/IOSPlatformEditor/Private/IOSTargetSettingsCustomization.cpp +++ b/Engine/Source/Developer/IOS/IOSPlatformEditor/Private/IOSTargetSettingsCustomization.cpp @@ -1807,27 +1807,22 @@ void FIOSTargetSettingsCustomization::SetShaderStandard(int32 Value) Message = LOCTEXT("iOSMetalShaderVersion1_1","Enabling Metal Shader Standard v1.1 increases the minimum operating system requirement for Metal from iOS 8.0 or later to iOS 9.0 or later. This does not affect tvOS."); SetMinVersion((int32)EIOSVersion::IOS_9); } - else if (Value < 2 && bMRTEnabled) + else if (Value < 3 && bMRTEnabled) { - FPropertyAccess::Result ResMRT = ShaderVersionPropertyHandle->SetValue((uint8)2); + FPropertyAccess::Result ResMRT = ShaderVersionPropertyHandle->SetValue((uint8)3); check(ResMRT == FPropertyAccess::Success); - Message = LOCTEXT("MetalMRTStandardv1.2","Enabling the Desktop Forward Renderer Metal requires Shader Standard v1.2 which increases the minimum operating system requirement for Metal from iOS 8.0 or later to iOS 10.0 or later."); - SetMinVersion((int32)EIOSVersion::IOS_10); - } - else if (Value == 2 && (EIOSVersion)EnumValue < EIOSVersion::IOS_10) - { - Message = LOCTEXT("iOSMetalShaderVersion1_2","Enabling Metal Shader Standard v1.2 increases the minimum operating system requirement for Metal from iOS 8.0/tvOS 9.0 or later to iOS/tvOS 10.0 or later."); - SetMinVersion((int32)EIOSVersion::IOS_10); + Message = LOCTEXT("MetalMRTStandardv1.2","Enabling the Desktop Forward Renderer Metal requires Shader Standard v2.0 which increases the minimum operating system requirement for Metal from iOS 10.0 or later to iOS 11.0 or later."); + SetMinVersion((int32)EIOSVersion::IOS_11); } else if (Value == 3 && (EIOSVersion)EnumValue < EIOSVersion::IOS_11) { - Message = LOCTEXT("iOSMetalShaderVersion2_0","Enabling Metal Shader Standard v2.0 increases the minimum operating system requirement for Metal from iOS 8.0/tvOS 9.0 or later to iOS/tvOS 11.0 or later."); + Message = LOCTEXT("iOSMetalShaderVersion2_0","Enabling Metal Shader Standard v2.0 increases the minimum operating system requirement for Metal from iOS 10.0/tvOS 10.0 or later to iOS/tvOS 11.0 or later."); SetMinVersion((int32)EIOSVersion::IOS_11); } else if (Value == 4 && (EIOSVersion)EnumValue < EIOSVersion::IOS_12) { - Message = LOCTEXT("iOSMetalShaderVersion2_1","Enabling Metal Shader Standard v2.1 increases the minimum operating system requirement for Metal from iOS 8.0/tvOS 9.0 or later to iOS/tvOS 12.0 or later."); + Message = LOCTEXT("iOSMetalShaderVersion2_1","Enabling Metal Shader Standard v2.1 increases the minimum operating system requirement for Metal from iOS 10.0/tvOS 10.0 or later to iOS/tvOS 12.0 or later."); SetMinVersion((int32)EIOSVersion::IOS_12); } @@ -1856,6 +1851,30 @@ void FIOSTargetSettingsCustomization::UpdateShaderStandardWarning() void FIOSTargetSettingsCustomization::UpdateOSVersionWarning() { + if (MRTPropertyHandle.IsValid() && ShaderVersionPropertyHandle.IsValid() && MinOSPropertyHandle.IsValid()) + { + bool bMRTEnabled = false; + MRTPropertyHandle->GetValue(bMRTEnabled); + + if (bMRTEnabled) + { + uint8 EnumValue; + MinOSPropertyHandle->GetValue(EnumValue); + if (EnumValue < (uint8)EIOSVersion::IOS_11) + { + SetMinVersion((int32)EIOSVersion::IOS_11); + + FText Message; + Message = LOCTEXT("MetalMRTStandardv1.2","Enabling the Desktop Forward Renderer Metal requires Shader Standard v2.0 which increases the minimum operating system requirement for Metal from iOS 10.0 or later to iOS 11.0 or later."); + IOSVersionWarningTextBox->SetError(Message); + } + } + else + { + FText Message; + IOSVersionWarningTextBox->SetError(Message); + } + } } void FIOSTargetSettingsCustomization::UpdateMetalMRTWarning() @@ -1869,20 +1888,29 @@ void FIOSTargetSettingsCustomization::UpdateMetalMRTWarning() { uint8 EnumValue; MinOSPropertyHandle->GetValue(EnumValue); - if (EnumValue < (uint8)EIOSVersion::IOS_10) + if (EnumValue < (uint8)EIOSVersion::IOS_11) { - SetMinVersion((int32)EIOSVersion::IOS_10); + SetMinVersion((int32)EIOSVersion::IOS_11); + + FText Message; + Message = LOCTEXT("MetalMRTStandardv1.2","Enabling the Desktop Forward Renderer Metal requires Shader Standard v2.0 which increases the minimum operating system requirement for Metal from iOS 10.0 or later to iOS 11.0 or later."); + IOSVersionWarningTextBox->SetError(Message); } ShaderVersionPropertyHandle->GetValue(EnumValue); - if (EnumValue < (uint8)EIOSMetalShaderStandard::IOSMetalSLStandard_1_2) + if (EnumValue < (uint8)EIOSMetalShaderStandard::IOSMetalSLStandard_2_0) { - SetShaderStandard((int32)EIOSMetalShaderStandard::IOSMetalSLStandard_1_2); + SetShaderStandard((int32)EIOSMetalShaderStandard::IOSMetalSLStandard_2_0); + + FText Message; + Message = LOCTEXT("MetalMRTStandardv1.2","Enabling the Desktop Forward Renderer Metal requires Shader Standard v2.0 which increases the minimum operating system requirement for Metal from iOS 10.0 or later to iOS 11.0 or later."); + ShaderVersionWarningTextBox->SetError(Message); } } else { UpdateOSVersionWarning(); + UpdateShaderStandardWarning(); } } } diff --git a/Engine/Source/Developer/IOS/IOSTargetPlatform/Private/IOSTargetPlatform.h b/Engine/Source/Developer/IOS/IOSTargetPlatform/Private/IOSTargetPlatform.h index 9750fa324a30..7dbc370c1f6a 100644 --- a/Engine/Source/Developer/IOS/IOSTargetPlatform/Private/IOSTargetPlatform.h +++ b/Engine/Source/Developer/IOS/IOSTargetPlatform/Private/IOSTargetPlatform.h @@ -78,7 +78,7 @@ public: virtual void GetAllDevices( TArray& OutDevices ) const override; - virtual bool GenerateStreamingInstallManifest(const TMultiMap& ChunkMap, const TSet& ChunkIDsInUse) const override + virtual bool GenerateStreamingInstallManifest(const TMultiMap& PakchunkMap, const TSet& PakchunkIndicesInUse) const override { return true; } diff --git a/Engine/Source/Developer/Linux/LinuxTargetPlatform/Private/LinuxTargetPlatform.h b/Engine/Source/Developer/Linux/LinuxTargetPlatform/Private/LinuxTargetPlatform.h index ff1a96a595e9..5166fea7017e 100644 --- a/Engine/Source/Developer/Linux/LinuxTargetPlatform/Private/LinuxTargetPlatform.h +++ b/Engine/Source/Developer/Linux/LinuxTargetPlatform/Private/LinuxTargetPlatform.h @@ -118,7 +118,7 @@ public: } } - virtual bool GenerateStreamingInstallManifest(const TMultiMap& ChunkMap, const TSet& ChunkIDsInUse) const override + virtual bool GenerateStreamingInstallManifest(const TMultiMap& PakchunkMap, const TSet& PakchunkIndicesInUse) const override { return true; } diff --git a/Engine/Source/Developer/Mac/MacTargetPlatform/Private/GenericMacTargetPlatform.h b/Engine/Source/Developer/Mac/MacTargetPlatform/Private/GenericMacTargetPlatform.h index a828fa2d69a8..205d325e769d 100644 --- a/Engine/Source/Developer/Mac/MacTargetPlatform/Private/GenericMacTargetPlatform.h +++ b/Engine/Source/Developer/Mac/MacTargetPlatform/Private/GenericMacTargetPlatform.h @@ -66,7 +66,7 @@ public: } } - virtual bool GenerateStreamingInstallManifest(const TMultiMap& ChunkMap, const TSet& ChunkIDsInUse) const override + virtual bool GenerateStreamingInstallManifest(const TMultiMap& PakchunkMap, const TSet& PakchunkIndicesInUse) const override { return true; } diff --git a/Engine/Source/Developer/MaterialBaking/Private/ExportMaterialProxy.h b/Engine/Source/Developer/MaterialBaking/Private/ExportMaterialProxy.h index ea85d5dbd06d..3ea57a01fca9 100644 --- a/Engine/Source/Developer/MaterialBaking/Private/ExportMaterialProxy.h +++ b/Engine/Source/Developer/MaterialBaking/Private/ExportMaterialProxy.h @@ -106,6 +106,11 @@ struct FExportMaterialCompiler : public FProxyMaterialCompiler return Compiler->ObjectBounds(); } + virtual int32 PreSkinnedLocalBounds() override + { + return Compiler->PreSkinnedLocalBounds(); + } + virtual int32 CameraVector() override { return Compiler->Constant3(0.0f, 0.0f, 1.0f); diff --git a/Engine/Source/Developer/MaterialUtilities/Private/MaterialUtilities.cpp b/Engine/Source/Developer/MaterialUtilities/Private/MaterialUtilities.cpp index 2b0f37c10255..4e617b81bc47 100644 --- a/Engine/Source/Developer/MaterialUtilities/Private/MaterialUtilities.cpp +++ b/Engine/Source/Developer/MaterialUtilities/Private/MaterialUtilities.cpp @@ -282,6 +282,11 @@ struct FExportMaterialCompiler : public FProxyMaterialCompiler return Compiler->ObjectBounds(); } + virtual int32 PreSkinnedLocalBounds() override + { + return Compiler->PreSkinnedLocalBounds(); + } + virtual int32 CameraVector() override { return Compiler->Constant3(0.0f, 0.0f, 1.0f); diff --git a/Engine/Source/Developer/MeshDescriptionOperations/Private/MeshDescriptionOperations.cpp b/Engine/Source/Developer/MeshDescriptionOperations/Private/MeshDescriptionOperations.cpp index c7614534961a..5e7f8de40384 100644 --- a/Engine/Source/Developer/MeshDescriptionOperations/Private/MeshDescriptionOperations.cpp +++ b/Engine/Source/Developer/MeshDescriptionOperations/Private/MeshDescriptionOperations.cpp @@ -1447,6 +1447,7 @@ bool FMeshDescriptionOperations::GenerateUniqueUVsForStaticMesh(const FMeshDescr // Create a copy of original mesh (only copy necessary data) FMeshDescription DuplicateMeshDescription(MeshDescription); + //Make sure we have a destination UV TextureCoordinnate { TVertexInstanceAttributesRef DuplicateVertexInstanceUVs = DuplicateMeshDescription.VertexInstanceAttributes().GetAttributesRef(MeshAttribute::VertexInstance::TextureCoordinate); @@ -1552,9 +1553,7 @@ bool FMeshDescriptionOperations::GenerateUniqueUVsForStaticMesh(const FMeshDescr { DuplicateMeshDescription.DeleteVertex(VertexID); } - //Compact and Remap IDs so we have clean ID from 0 to n since we just erase some polygons - FElementIDRemappings RemappingInfos; - DuplicateMeshDescription.Compact(RemappingInfos); + //Avoid compacting the DuplicateMeshDescription, since the remap of the VertexInstaceID will not be good anymore } } // Find overlapping corners for UV generator. Allow some threshold - this should not produce any error in a case if resulting diff --git a/Engine/Source/Developer/MeshMergeUtilities/Private/MeshMergeHelpers.cpp b/Engine/Source/Developer/MeshMergeUtilities/Private/MeshMergeHelpers.cpp index 9a76ed41e03f..6ee2fb86f2c6 100644 --- a/Engine/Source/Developer/MeshMergeUtilities/Private/MeshMergeHelpers.cpp +++ b/Engine/Source/Developer/MeshMergeUtilities/Private/MeshMergeHelpers.cpp @@ -1303,6 +1303,7 @@ void FMeshMergeHelpers::MergeImpostersToRawMesh(TArrayDeviceWeakPtr = DiscoveredDevice; + if (CurrentDevicePtr == DeviceList[ExistingEntryIdx]) + { + if (!CurrentDeviceOutputPtr.IsValid()) + { + if (CurrentDevicePtr.IsValid()) + { + ITargetDevicePtr PinnedPtr = CurrentDevicePtr->DeviceWeakPtr.Pin(); + if (PinnedPtr.IsValid() && PinnedPtr->IsConnected()) + { + CurrentDeviceOutputPtr = PinnedPtr->CreateDeviceOutputRouter(this); + } + } + } + } } else { diff --git a/Engine/Source/Developer/PakFileUtilities/PakFileUtilities.Build.cs b/Engine/Source/Developer/PakFileUtilities/PakFileUtilities.Build.cs index 685453aafbb7..204afcb99b70 100644 --- a/Engine/Source/Developer/PakFileUtilities/PakFileUtilities.Build.cs +++ b/Engine/Source/Developer/PakFileUtilities/PakFileUtilities.Build.cs @@ -6,7 +6,7 @@ public class PakFileUtilities : ModuleRules { public PakFileUtilities(ReadOnlyTargetRules Target) : base(Target) { - PrivateDependencyModuleNames.AddRange(new string[] { "Core", "PakFile", "Json", "Projects" }); + PrivateDependencyModuleNames.AddRange(new string[] { "Core", "PakFile", "Json", "Projects", "RSA" }); PrivateIncludePathModuleNames.AddRange( new string[] { diff --git a/Engine/Source/Developer/PakFileUtilities/Private/PakFileUtilities.cpp b/Engine/Source/Developer/PakFileUtilities/Private/PakFileUtilities.cpp index ded9722a42e3..0140c60282d6 100644 --- a/Engine/Source/Developer/PakFileUtilities/Private/PakFileUtilities.cpp +++ b/Engine/Source/Developer/PakFileUtilities/Private/PakFileUtilities.cpp @@ -23,69 +23,6 @@ IMPLEMENT_MODULE(FDefaultModuleImpl, PakFileUtilities); -/** - * Encryption keys: public and private - */ -struct FKeyPair -{ - /** Public decryption key */ - FEncryptionKey PublicKey; - /** Private encryption key */ - FEncryptionKey PrivateKey; - - friend FArchive& operator<<(FArchive& Ar, FKeyPair& Pair) - { - Ar << Pair.PublicKey.Exponent; - Ar << Pair.PublicKey.Modulus; - Ar << Pair.PrivateKey.Exponent; - Ar << Pair.PrivateKey.Modulus; - return Ar; - } - - bool IsValid() const - { - bool bAllKeysValid = !PrivateKey.Exponent.IsZero() - && !PrivateKey.Modulus.IsZero() - && !PublicKey.Exponent.IsZero() - && !PublicKey.Modulus.IsZero(); - - bool bIsValid = true; - - if (bAllKeysValid) - { - // Just some random values - static TEncryptionInt TestData[] = - { - 11, - 253, - 128, - 234, - 56, - 89, - 34, - 179, - 29, - 1024, - (int64)(MAX_int32), - (int64)(MAX_uint32)-1 - }; - - for (int32 TestIndex = 0; TestIndex < ARRAY_COUNT(TestData); ++TestIndex) - { - TEncryptionInt EncryptedData = FEncryption::ModularPow(TestData[TestIndex], PrivateKey.Exponent, PrivateKey.Modulus); - TEncryptionInt DecryptedData = FEncryption::ModularPow(EncryptedData, PublicKey.Exponent, PublicKey.Modulus); - if (TestData[TestIndex] != DecryptedData) - { - bIsValid = false; - break; - } - } - } - - return bIsValid; - } -}; - struct FNamedAESKey { FString Name; @@ -100,7 +37,7 @@ struct FNamedAESKey struct FKeyChain { - FKeyPair SigningKey; + FRSA::TKeyPtr SigningKey; TMap EncryptionKeys; const FNamedAESKey* MasterEncryptionKey = nullptr; }; @@ -1163,6 +1100,33 @@ TEncryptionInt ParseEncryptionIntFromJson(TSharedPtr InObj, const T } } +FRSA::TKeyPtr ParseRSAKeyFromJson(TSharedPtr InObj) +{ + TSharedPtr PublicKey = InObj->GetObjectField(TEXT("PublicKey")); + TSharedPtr PrivateKey = InObj->GetObjectField(TEXT("PrivateKey")); + + FString PublicExponentBase64, PrivateExponentBase64, PublicModulusBase64, PrivateModulusBase64; + + if ( PublicKey->TryGetStringField("Exponent", PublicExponentBase64) + && PublicKey->TryGetStringField("Modulus", PublicModulusBase64) + && PrivateKey->TryGetStringField("Exponent", PrivateExponentBase64) + && PrivateKey->TryGetStringField("Modulus", PrivateModulusBase64)) + { + check(PublicModulusBase64 == PrivateModulusBase64); + + TArray PublicExponent, PrivateExponent, Modulus; + FBase64::Decode(PublicExponentBase64, PublicExponent); + FBase64::Decode(PrivateExponentBase64, PrivateExponent); + FBase64::Decode(PublicModulusBase64, Modulus); + + return FRSA::CreateKey(PublicExponent, PrivateExponent, Modulus); + } + else + { + return nullptr; + } +} + void LoadKeyChainFromFile(const FString& InFilename, FKeyChain& OutCryptoSettings) { FArchive* File = IFileManager::Get().CreateFileReader(*InFilename); @@ -1194,13 +1158,7 @@ void LoadKeyChainFromFile(const FString& InFilename, FKeyChain& OutCryptoSetting const TSharedPtr* SigningKey = nullptr; if (RootObject->TryGetObjectField(TEXT("SigningKey"), SigningKey)) { - TSharedPtr PublicKey = (*SigningKey)->GetObjectField(TEXT("PublicKey")); - TSharedPtr PrivateKey = (*SigningKey)->GetObjectField(TEXT("PrivateKey")); - OutCryptoSettings.SigningKey.PublicKey.Exponent = ParseEncryptionIntFromJson(PublicKey, TEXT("Exponent")); - OutCryptoSettings.SigningKey.PublicKey.Modulus = ParseEncryptionIntFromJson(PublicKey, TEXT("Modulus")); - OutCryptoSettings.SigningKey.PrivateKey.Exponent = ParseEncryptionIntFromJson(PrivateKey, TEXT("Exponent")); - OutCryptoSettings.SigningKey.PrivateKey.Modulus = ParseEncryptionIntFromJson(PrivateKey, TEXT("Modulus")); - check(OutCryptoSettings.SigningKey.PublicKey.Modulus == OutCryptoSettings.SigningKey.PrivateKey.Modulus); + OutCryptoSettings.SigningKey = ParseRSAKeyFromJson(*SigningKey); } const TArray>* SecondaryEncryptionKeyArray = nullptr; @@ -1229,10 +1187,7 @@ void LoadKeyChainFromFile(const FString& InFilename, FKeyChain& OutCryptoSetting void LoadKeyChain(const TCHAR* CmdLine, FKeyChain& OutCryptoSettings) { - OutCryptoSettings.SigningKey.PrivateKey.Exponent.Zero(); - OutCryptoSettings.SigningKey.PrivateKey.Modulus.Zero(); - OutCryptoSettings.SigningKey.PublicKey.Exponent.Zero(); - OutCryptoSettings.SigningKey.PublicKey.Modulus.Zero(); + OutCryptoSettings.SigningKey.Reset(); OutCryptoSettings.EncryptionKeys.Empty(); // First, try and parse the keys from a supplied crypto key cache file @@ -1250,6 +1205,8 @@ void LoadKeyChain(const TCHAR* CmdLine, FKeyChain& OutCryptoSettings) && FParse::Value(CmdLine, TEXT("enginedir="), EngineDir, false) && FParse::Value(CmdLine, TEXT("platform="), Platform, false)) { + UE_LOG(LogPakFile, Warning, TEXT("A legacy command line syntax is being used for crypto config. Please update to using the -cryptokey parameter as soon as possible as this mode is deprecated")); + FConfigFile EngineConfig; FConfigCacheIni::LoadExternalIniFile(EngineConfig, TEXT("Engine"), *FPaths::Combine(EngineDir, TEXT("Config\\")), *FPaths::Combine(ProjectDir, TEXT("Config/")), true, *Platform); @@ -1293,10 +1250,7 @@ void LoadKeyChain(const TCHAR* CmdLine, FKeyChain& OutCryptoSettings) FBase64::Decode(PrivateExpBase64, PrivateExp); FBase64::Decode(ModulusBase64, Modulus); - OutCryptoSettings.SigningKey.PrivateKey.Exponent = TEncryptionInt((uint32*)&PrivateExp[0]); - OutCryptoSettings.SigningKey.PrivateKey.Modulus = TEncryptionInt((uint32*)&Modulus[0]); - OutCryptoSettings.SigningKey.PublicKey.Exponent = TEncryptionInt((uint32*)&PublicExp[0]); - OutCryptoSettings.SigningKey.PublicKey.Modulus = OutCryptoSettings.SigningKey.PrivateKey.Modulus; + OutCryptoSettings.SigningKey = FRSA::CreateKey(PublicExp, PrivateExp, Modulus); UE_LOG(LogPakFile, Display, TEXT("Parsed signature keys from config files.")); } @@ -1337,10 +1291,11 @@ void LoadKeyChain(const TCHAR* CmdLine, FKeyChain& OutCryptoSettings) ConfigFile.GetString(SectionName, TEXT("rsa.privateexp"), RSAPrivateExp); ConfigFile.GetString(SectionName, TEXT("rsa.modulus"), RSAModulus); - OutCryptoSettings.SigningKey.PrivateKey.Exponent.Parse(RSAPrivateExp); - OutCryptoSettings.SigningKey.PrivateKey.Modulus.Parse(RSAModulus); - OutCryptoSettings.SigningKey.PublicKey.Exponent.Parse(RSAPublicExp); - OutCryptoSettings.SigningKey.PublicKey.Modulus = OutCryptoSettings.SigningKey.PrivateKey.Modulus; + //TODO: Fix me! + //OutSigningKey.PrivateKey.Exponent.Parse(RSAPrivateExp); + //OutSigningKey.PrivateKey.Modulus.Parse(RSAModulus); + //OutSigningKey.PublicKey.Exponent.Parse(RSAPublicExp); + //OutSigningKey.PublicKey.Modulus = OutSigningKey.PrivateKey.Modulus; UE_LOG(LogPakFile, Display, TEXT("Parsed signature keys from config files.")); } @@ -1374,6 +1329,8 @@ void LoadKeyChain(const TCHAR* CmdLine, FKeyChain& OutCryptoSettings) if (EncryptionKeyString.Len() > 0) { + UE_LOG(LogPakFile, Warning, TEXT("A legacy command line syntax is being used for crypto config. Please update to using the -cryptokey parameter as soon as possible as this mode is deprecated")); + FNamedAESKey NewKey; NewKey.Name = TEXT("Default"); NewKey.Guid = FGuid(); @@ -1404,11 +1361,6 @@ void LoadKeyChain(const TCHAR* CmdLine, FKeyChain& OutCryptoSettings) } } - if (!OutCryptoSettings.SigningKey.IsValid()) - { - UE_LOG(LogPakFile, Fatal, TEXT("Pak signing keys are invalid")); - } - FString EncryptionKeyOverrideGuidString; FGuid EncryptionKeyOverrideGuid; if (FParse::Value(CmdLine, TEXT("EncryptionKeyOverrideGuid="), EncryptionKeyOverrideGuidString)) @@ -1447,7 +1399,7 @@ FArchive* CreatePakWriter(const TCHAR* Filename, const FKeyChain& InKeyChain, bo if (bSign) { UE_LOG(LogPakFile, Display, TEXT("Creating signed pak %s."), Filename); - Writer = new FSignedArchiveWriter(*Writer, Filename, InKeyChain.SigningKey.PublicKey, InKeyChain.SigningKey.PrivateKey); + Writer = new FSignedArchiveWriter(*Writer, Filename, InKeyChain.SigningKey); } else { diff --git a/Engine/Source/Developer/PakFileUtilities/Private/SignedArchiveWriter.cpp b/Engine/Source/Developer/PakFileUtilities/Private/SignedArchiveWriter.cpp index 49140c16c007..3d18994c901e 100644 --- a/Engine/Source/Developer/PakFileUtilities/Private/SignedArchiveWriter.cpp +++ b/Engine/Source/Developer/PakFileUtilities/Private/SignedArchiveWriter.cpp @@ -5,14 +5,13 @@ #include "Misc/SecureHash.h" #include "HAL/FileManager.h" -FSignedArchiveWriter::FSignedArchiveWriter(FArchive& InPak, const FString& InPakFilename, const FEncryptionKey& InPublicKey, const FEncryptionKey& InPrivateKey) +FSignedArchiveWriter::FSignedArchiveWriter(FArchive& InPak, const FString& InPakFilename, FRSA::TKeyPtr InSigningKey) : BufferArchive(Buffer) , PakWriter(InPak) , PakSignaturesFilename(FPaths::ChangeExtension(InPakFilename, TEXT("sig"))) , SizeOnDisk(0) , PakSize(0) - , PublicKey(InPublicKey) - , PrivateKey(InPrivateKey) + , SigningKey(InSigningKey) { Buffer.Reserve(FPakInfo::MaxChunkDataSize); } @@ -44,14 +43,10 @@ bool FSignedArchiveWriter::Close() SerializeBufferAndSign(); } - FEncryptedSignature EncryptedMasterHash; - FDecryptedSignature DecryptedMasterHash; - DecryptedMasterHash.Data = ComputePakChunkHash((const uint8*)&ChunkHashes[0], ChunkHashes.Num() * sizeof(TPakChunkHash)); - FEncryption::EncryptSignature(DecryptedMasterHash, EncryptedMasterHash, PrivateKey); - FArchive* SignatureWriter = IFileManager::Get().CreateFileWriter(*PakSignaturesFilename); - *SignatureWriter << EncryptedMasterHash; - *SignatureWriter << ChunkHashes; + FPakSignatureFile SignatureFile; + SignatureFile.SetChunkHashesAndSign(ChunkHashes, SigningKey); + SignatureFile.Serialize(*SignatureWriter); delete SignatureWriter; return FArchive::Close(); diff --git a/Engine/Source/Developer/PakFileUtilities/Private/SignedArchiveWriter.h b/Engine/Source/Developer/PakFileUtilities/Private/SignedArchiveWriter.h index 83b85cc72834..278402225d45 100644 --- a/Engine/Source/Developer/PakFileUtilities/Private/SignedArchiveWriter.h +++ b/Engine/Source/Developer/PakFileUtilities/Private/SignedArchiveWriter.h @@ -24,10 +24,8 @@ class FSignedArchiveWriter : public FArchive int64 SizeOnDisk; /** Data size (excluding signatures) */ int64 PakSize; - /** Decryption key */ - FEncryptionKey PublicKey; - /** Encryption key */ - FEncryptionKey PrivateKey; + /** Signing key */ + FRSA::TKeyPtr SigningKey; /** Hashes */ TArray ChunkHashes; @@ -38,7 +36,7 @@ class FSignedArchiveWriter : public FArchive public: - FSignedArchiveWriter(FArchive& InPak, const FString& InPakFilename, const FEncryptionKey& InPublicKey, const FEncryptionKey& InPrivateKey); + FSignedArchiveWriter(FArchive& InPak, const FString& InPakFilename, FRSA::TKeyPtr InSigningKey); virtual ~FSignedArchiveWriter(); // FArchive interface diff --git a/Engine/Source/Developer/ShaderFormatOpenGL/Private/GlslBackend.cpp b/Engine/Source/Developer/ShaderFormatOpenGL/Private/GlslBackend.cpp index 8f84c3812359..28659e9c4b30 100644 --- a/Engine/Source/Developer/ShaderFormatOpenGL/Private/GlslBackend.cpp +++ b/Engine/Source/Developer/ShaderFormatOpenGL/Private/GlslBackend.cpp @@ -1,4 +1,5 @@ // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +// . // This code is largely based on that in ir_print_glsl_visitor.cpp from // glsl-optimizer. @@ -1658,15 +1659,15 @@ class ir_gen_glsl_visitor : public ir_visitor { if (bIsStructured) { - if (src) - { - src->accept(this); - ralloc_asprintf_append(buffer, " = "); - } deref->image->accept(this); ralloc_asprintf_append(buffer, "["); deref->image_index->accept(this); ralloc_asprintf_append(buffer, "]"); + if (src) + { + ralloc_asprintf_append(buffer, " = "); + src->accept(this); + } } else { diff --git a/Engine/Source/Developer/ShaderFormatOpenGL/Private/OpenGLShaderCompiler.cpp b/Engine/Source/Developer/ShaderFormatOpenGL/Private/OpenGLShaderCompiler.cpp index 16826a7ec2d9..8200581520cf 100644 --- a/Engine/Source/Developer/ShaderFormatOpenGL/Private/OpenGLShaderCompiler.cpp +++ b/Engine/Source/Developer/ShaderFormatOpenGL/Private/OpenGLShaderCompiler.cpp @@ -1372,8 +1372,8 @@ uint32 FOpenGLFrontend::CalculateCrossCompilerFlags(GLSLVersion Version, const T CompilerFlags.Contains(CFLAG_UseEmulatedUB)) { CCFlags |= HLSLCC_FlattenUniformBuffers | HLSLCC_FlattenUniformBufferStructures; - // Do not use this flag as it adds too many uniforms. - //CCFlags |= HLSLCC_GroupFlattenedUniformBuffers; + // Enabling HLSLCC_GroupFlattenedUniformBuffers, see FORT-159483. + CCFlags |= HLSLCC_GroupFlattenedUniformBuffers; CCFlags |= HLSLCC_ExpandUBMemberArrays; } diff --git a/Engine/Source/Developer/ShaderFormatOpenGL/Private/ShaderFormatOpenGL.cpp b/Engine/Source/Developer/ShaderFormatOpenGL/Private/ShaderFormatOpenGL.cpp index 64b1cbb9618b..2cd7cb8b7af3 100644 --- a/Engine/Source/Developer/ShaderFormatOpenGL/Private/ShaderFormatOpenGL.cpp +++ b/Engine/Source/Developer/ShaderFormatOpenGL/Private/ShaderFormatOpenGL.cpp @@ -24,8 +24,8 @@ class FShaderFormatGLSL : public IShaderFormat enum { /** Version for shader format, this becomes part of the DDC key. */ - UE_SHADER_GLSL_VER = 78, - UE_SHADER_GLSL_ANDROID_VER = 78, + UE_SHADER_GLSL_VER = 79, + UE_SHADER_GLSL_ANDROID_VER = 79, }; void CheckFormat(FName Format) const diff --git a/Engine/Source/Developer/ShaderFormatVectorVM/Private/VectorVMShaderCompiler.cpp b/Engine/Source/Developer/ShaderFormatVectorVM/Private/VectorVMShaderCompiler.cpp index 69559a8728aa..71967a0e7db1 100644 --- a/Engine/Source/Developer/ShaderFormatVectorVM/Private/VectorVMShaderCompiler.cpp +++ b/Engine/Source/Developer/ShaderFormatVectorVM/Private/VectorVMShaderCompiler.cpp @@ -1,4 +1,5 @@ // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +// . #include "ShaderFormatVectorVM.h" #include "VectorVMBackend.h" diff --git a/Engine/Source/Developer/TargetPlatform/Public/Interfaces/ITargetPlatform.h b/Engine/Source/Developer/TargetPlatform/Public/Interfaces/ITargetPlatform.h index 05c8835e90cb..893f3be3a679 100644 --- a/Engine/Source/Developer/TargetPlatform/Public/Interfaces/ITargetPlatform.h +++ b/Engine/Source/Developer/TargetPlatform/Public/Interfaces/ITargetPlatform.h @@ -84,6 +84,9 @@ enum class ETargetPlatformFeatures /* The platform supports memory mapped animation */ MemoryMappedAnimation, + + /* The platform supports sparse textures */ + SparseTextures, }; @@ -210,11 +213,11 @@ public: /** * Generates a platform specific asset manifest given an array of FAssetData. * - * @param ChunkMap A map of asset path to ChunkIDs for all of the assets. - * @param ChunkIDsInUse A set of all ChunkIDs used by this set of assets. + * @param PakchunkMap A map of asset path to Pakchunk file indices for all of the assets. + * @param PakchunkIndicesInUse A set of all Pakchunk file indices used by this set of assets. * @return true if the manifest was successfully generated, or if the platform doesn't need a manifest . */ - virtual bool GenerateStreamingInstallManifest( const TMultiMap& ChunkMap, const TSet& ChunkIDsInUse ) const = 0; + virtual bool GenerateStreamingInstallManifest( const TMultiMap& PakchunkMap, const TSet& PakchunkIndicesInUse) const = 0; /** * Gets the default device. diff --git a/Engine/Source/Developer/VulkanShaderFormat/Private/VulkanBackend.cpp b/Engine/Source/Developer/VulkanShaderFormat/Private/VulkanBackend.cpp index 12b3e219db06..3d1b1e695a45 100644 --- a/Engine/Source/Developer/VulkanShaderFormat/Private/VulkanBackend.cpp +++ b/Engine/Source/Developer/VulkanShaderFormat/Private/VulkanBackend.cpp @@ -2344,7 +2344,7 @@ class FGenerateVulkanVisitor : public ir_visitor ir_instruction *const inst = (ir_instruction *)iter.get(); ir_assignment *assignment = inst->as_assignment(); - if (assignment && (assignment->rhs->ir_type == ir_type_dereference_variable || assignment->rhs->ir_type == ir_type_constant)) + if (assignment && (assignment->rhs->ir_type == ir_type_dereference_variable || assignment->rhs->ir_type == ir_type_constant || assignment->rhs->ir_type == ir_type_swizzle)) { dest_deref = assignment->lhs->as_dereference_variable(); true_value = assignment->rhs; @@ -2367,7 +2367,7 @@ class FGenerateVulkanVisitor : public ir_visitor ir_instruction *const inst = (ir_instruction *)iter.get(); ir_assignment *assignment = inst->as_assignment(); - if (assignment && (assignment->rhs->ir_type == ir_type_dereference_variable || assignment->rhs->ir_type == ir_type_constant)) + if (assignment && (assignment->rhs->ir_type == ir_type_dereference_variable || assignment->rhs->ir_type == ir_type_constant || assignment->rhs->ir_type == ir_type_swizzle)) { ir_dereference_variable *tmp_deref = assignment->lhs->as_dereference_variable(); if (tmp_deref diff --git a/Engine/Source/Developer/VulkanShaderFormat/Private/VulkanShaderCompiler.cpp b/Engine/Source/Developer/VulkanShaderFormat/Private/VulkanShaderCompiler.cpp index f0a5df7bacb9..b514a77183c7 100644 --- a/Engine/Source/Developer/VulkanShaderFormat/Private/VulkanShaderCompiler.cpp +++ b/Engine/Source/Developer/VulkanShaderFormat/Private/VulkanShaderCompiler.cpp @@ -1,5 +1,5 @@ // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. -// .. +// . #include "VulkanShaderFormat.h" #include "VulkanCommon.h" diff --git a/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_ClientStartupThread.cpp b/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_ClientStartupThread.cpp index 610f6a2f3443..d4675d191d7f 100644 --- a/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_ClientStartupThread.cpp +++ b/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_ClientStartupThread.cpp @@ -255,6 +255,18 @@ void ClientStartupThread::SetBuildArguments(const wchar_t* arguments) } // END EPIC MOD +// BEGIN EPIC MOD - Support for lazy-loading modules +void ClientStartupThread::EnableLazyLoadedModule(const wchar_t* fileName, Windows::HMODULE moduleBase) +{ + // we cannot wait for commands in the user command thread as long as startup hasn't finished + Join(); + + if (m_userCommandThread) + { + m_userCommandThread->EnableLazyLoadedModule(fileName, moduleBase); + } +} +// END EPIC MOD void ClientStartupThread::ApplySettingBool(const char* const settingName, int value) { diff --git a/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_ClientStartupThread.h b/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_ClientStartupThread.h index b6d791f172f3..b0da2cbbed37 100644 --- a/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_ClientStartupThread.h +++ b/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_ClientStartupThread.h @@ -59,6 +59,10 @@ public: void SetBuildArguments(const wchar_t* arguments); // END EPIC MOD + // BEGIN EPIC MOD - Support for lazy-loading modules + void EnableLazyLoadedModule(const wchar_t* fileName, Windows::HMODULE moduleBase); + // END EPIC MOD + private: struct ThreadContext { diff --git a/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_ClientUserCommandThread.cpp b/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_ClientUserCommandThread.cpp index d5351fa8cacf..bfff094082b3 100644 --- a/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_ClientUserCommandThread.cpp +++ b/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_ClientUserCommandThread.cpp @@ -233,6 +233,30 @@ namespace userCommands }; // END EPIC MOD + // BEGIN EPIC MOD - Support for lazy-loading modules + struct EnableLazyLoadedModuleCommand : public BaseCommand + { + EnableLazyLoadedModuleCommand(void) + : BaseCommand(Scope::NONE) + { + } + + virtual ~EnableLazyLoadedModuleCommand(void) {} + + virtual void Execute(DuplexPipe* pipe) override + { + commands::EnableLazyLoadedModule serverCommand; + serverCommand.processId = process::GetId(); + wcscpy_s(serverCommand.fileName, fileName.c_str()); + serverCommand.moduleBase = moduleBase; + pipe->SendCommandAndWaitForAck(serverCommand); + } + + std::wstring fileName; + Windows::HMODULE moduleBase; + }; + // END EPIC MOD + struct BuildPatchCommand : public BaseCommand { @@ -604,6 +628,23 @@ void ClientUserCommandThread::SetBuildArguments(const wchar_t* arguments) } // END EPIC MOD +// BEGIN EPIC MOD - Adding support for lazy-loading modules +void ClientUserCommandThread::EnableLazyLoadedModule(const wchar_t* fileName, Windows::HMODULE moduleBase) +{ + userCommands::EnableLazyLoadedModuleCommand* command = new userCommands::EnableLazyLoadedModuleCommand; + command->fileName = fileName; + command->moduleBase = moduleBase; + { + CriticalSection::ScopedLock lock(&g_userCommandQueueCS); + g_userCommandQueue.push_front(command); + } + + // signal to the thread that a new item is in the queue + m_itemInQueueEvent->Signal(); +} +// END EPIC MOD + + void ClientUserCommandThread::ApplySettingBool(const char* const settingName, int value) { userCommands::ApplySettingBoolCommand* command = new userCommands::ApplySettingBoolCommand; diff --git a/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_ClientUserCommandThread.h b/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_ClientUserCommandThread.h index a560eb0a47ff..491194ee9db2 100644 --- a/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_ClientUserCommandThread.h +++ b/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_ClientUserCommandThread.h @@ -63,6 +63,10 @@ public: void SetBuildArguments(const wchar_t* arguments); // END EPIC MOD + // BEGIN EPIC MOD - Support for lazy-loading modules + void EnableLazyLoadedModule(const wchar_t* fileName, Windows::HMODULE moduleBase); + // END EPIC MOD + void ApplySettingBool(const char* const settingName, int value); void ApplySettingInt(const char* const settingName, int value); void ApplySettingString(const char* const settingName, const wchar_t* const value); diff --git a/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_Commands.h b/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_Commands.h index 8edb8303ff76..9a8d5c1a6491 100644 --- a/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_Commands.h +++ b/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_Commands.h @@ -316,11 +316,27 @@ namespace commands }; // END EPIC MOD + // BEGIN EPIC MOD - Support for lazy-loading modules + struct EnableLazyLoadedModule + { + static const uint32_t ID = SetBuildArguments::ID + 1u; + + unsigned int processId; + wchar_t fileName[260]; + Windows::HMODULE moduleBase; + }; + + struct FinishedLazyLoadingModules + { + static const uint32_t ID = EnableLazyLoadedModule::ID + 1u; + }; + // END EPIC MOD + // tell the EXE that a bool setting needs to be changed struct ApplySettingBool { - // BEGIN EPIC MOD - Adding SetBuildArguments command - static const uint32_t ID = SetBuildArguments::ID + 1u; + // BEGIN EPIC MOD - Support for lazy-loading modules + static const uint32_t ID = FinishedLazyLoadingModules::ID + 1u; // END EPIC MOD char settingName[256]; diff --git a/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_EntryPoint.cpp b/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_EntryPoint.cpp index 1bf56454b4a4..a0a0ddd85af8 100644 --- a/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_EntryPoint.cpp +++ b/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_EntryPoint.cpp @@ -175,6 +175,13 @@ LPP_DLL_API void __cdecl LppSetBuildArguments(const wchar_t* arguments) } // END EPIC MOD +// BEGIN EPIC MOD - Support for lazy-loading modules +LPP_DLL_API void __cdecl LppEnableLazyLoadedModule(const wchar_t* const nameOfExeOrDll) +{ + HMODULE baseAddress = GetModuleHandle(nameOfExeOrDll); + g_startupThread->EnableLazyLoadedModule(nameOfExeOrDll, baseAddress); +} +// END EPIC MOD LPP_DLL_API void __cdecl LppApplySettingBool(const char* const settingName, int value) { diff --git a/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_EntryPoint.h b/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_EntryPoint.h index b596dd24283b..f3ca6d627462 100644 --- a/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_EntryPoint.h +++ b/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_EntryPoint.h @@ -18,3 +18,7 @@ void __cdecl LppSetBuildArguments(const wchar_t* const arguments); void __cdecl LppApplySettingBool(const char* const settingName, int value); void __cdecl LppApplySettingInt(const char* const settingName, int value); void __cdecl LppApplySettingString(const char* const settingName, const wchar_t* const value); + +// BEGIN EPIC MOD - Support for lazy-loading modules +void __cdecl LppEnableLazyLoadedModule(const wchar_t* const nameOfExeOrDll); +// END EPIC MODS \ No newline at end of file diff --git a/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_Logging.cpp b/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_Logging.cpp index 1ca97e83615e..cf5d1f2c10ee 100644 --- a/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_Logging.cpp +++ b/Engine/Source/Developer/Windows/LiveCoding/Private/External/LC_Logging.cpp @@ -30,16 +30,17 @@ namespace // BEGIN EPIC MOD - Redirecting log output static void DefaultOutputHandler(logging::Channel::Enum channel, logging::Type::Enum type, const wchar_t* const Message) { + FString MessageWithoutNewline = FString(Message).TrimEnd(); switch (type) { case logging::Type::LOG_WARNING: - UE_LOG(LogLiveCoding, Warning, TEXT("%s"), Message); + UE_LOG(LogLiveCoding, Warning, TEXT("%s"), *MessageWithoutNewline); break; case logging::Type::LOG_ERROR: - UE_LOG(LogLiveCoding, Error, TEXT("%s"), Message); + UE_LOG(LogLiveCoding, Error, TEXT("%s"), *MessageWithoutNewline); break; default: - UE_LOG(LogLiveCoding, Display, TEXT("%s"), Message); + UE_LOG(LogLiveCoding, Display, TEXT("%s"), *MessageWithoutNewline); break; } } diff --git a/Engine/Source/Developer/Windows/LiveCoding/Private/LiveCodingModule.cpp b/Engine/Source/Developer/Windows/LiveCoding/Private/LiveCodingModule.cpp index 24bc6b9ed965..0e7fef32ff22 100644 --- a/Engine/Source/Developer/Windows/LiveCoding/Private/LiveCodingModule.cpp +++ b/Engine/Source/Developer/Windows/LiveCoding/Private/LiveCodingModule.cpp @@ -137,9 +137,9 @@ void FLiveCodingModule::EnableForSession(bool bEnable) UE_LOG(LogLiveCoding, Display, TEXT("Console will be hidden but remain running in the background. Restart to disable completely.")); LppSetActive(false); LppSetVisible(false); + bEnabledForSession = false; } } - bEnabledForSession = bEnable; } bool FLiveCodingModule::IsEnabledForSession() const @@ -147,6 +147,18 @@ bool FLiveCodingModule::IsEnabledForSession() const return bEnabledForSession; } +bool FLiveCodingModule::CanEnableForSession() const +{ +#if !IS_MONOLITHIC + FModuleManager& ModuleManager = FModuleManager::Get(); + if(ModuleManager.HasAnyOverridenModuleFilename()) + { + return false; + } +#endif + return true; +} + bool FLiveCodingModule::HasStarted() const { return bStarted; @@ -193,6 +205,13 @@ bool FLiveCodingModule::StartLiveCoding() { if(!bStarted) { + // Make sure there aren't any hot reload modules already active + if (!CanEnableForSession()) + { + UE_LOG(LogLiveCoding, Error, TEXT("Unable to start live coding session. Some modules have already been hot reloaded.")); + return false; + } + // Setup the console path GLiveCodingConsolePath = ConsolePathVariable->GetString(); if (!FPaths::FileExists(GLiveCodingConsolePath)) @@ -239,7 +258,7 @@ void FLiveCodingModule::UpdateModules() #if IS_MONOLITHIC wchar_t FullFilePath[WINDOWS_MAX_PATH]; verify(GetModuleFileName(hInstance, FullFilePath, ARRAY_COUNT(FullFilePath))); - EnableModule(FullFilePath); + LppEnableModule(FullFilePath); #else TArray ModuleStatuses; FModuleManager::Get().QueryModules(ModuleStatuses); @@ -261,24 +280,6 @@ void FLiveCodingModule::UpdateModules() #endif } -void FLiveCodingModule::EnableModule(const FString& FullFilePath) -{ - if (!EnabledModules.Contains(FullFilePath)) - { - LppEnableModule(*FullFilePath); - EnabledModules.Add(FullFilePath); - } -} - -void FLiveCodingModule::DisableModule(const FString& FullFilePath) -{ - if(EnabledModules.Contains(FullFilePath)) - { - LppDisableModule(*FullFilePath); - EnabledModules.Remove(FullFilePath); - } -} - void FLiveCodingModule::OnModulesChanged(FName ModuleName, EModuleChangeReason Reason) { #if !IS_MONOLITHIC @@ -297,42 +298,42 @@ void FLiveCodingModule::OnModulesChanged(FName ModuleName, EModuleChangeReason R void FLiveCodingModule::ConfigureModule(const FName& Name, const FString& FullFilePath) { #if !IS_MONOLITHIC - if (ShouldEnableModule(Name, FullFilePath)) + if (!ConfiguredModules.Contains(Name)) { - EnableModule(FullFilePath); - } - else - { - DisableModule(FullFilePath); + if (ShouldPreloadModule(Name, FullFilePath)) + { + LppEnableModule(*FullFilePath); + } + else + { + LppEnableLazyLoadedModule(*FullFilePath); + } + ConfiguredModules.Add(Name); } #endif } -bool FLiveCodingModule::ShouldEnableModule(const FName& Name, const FString& FullFilePath) const +bool FLiveCodingModule::ShouldPreloadModule(const FName& Name, const FString& FullFilePath) const { - if (Settings->ExcludeSpecificModules.Contains(Name)) - { - return false; - } - if (Settings->IncludeSpecificModules.Contains(Name)) + if (Settings->PreloadNamedModules.Contains(Name)) { return true; } if (FullFilePath.StartsWith(FullProjectDir)) { - if (Settings->bIncludeProjectModules == Settings->bIncludeProjectPluginModules) + if (Settings->bPreloadProjectModules == Settings->bPreloadProjectPluginModules) { - return Settings->bIncludeProjectModules; + return Settings->bPreloadProjectModules; } if(FullFilePath.StartsWith(FullProjectPluginsDir)) { - return Settings->bIncludeProjectPluginModules; + return Settings->bPreloadProjectPluginModules; } else { - return Settings->bIncludeProjectModules; + return Settings->bPreloadProjectModules; } } else @@ -342,18 +343,18 @@ bool FLiveCodingModule::ShouldEnableModule(const FName& Name, const FString& Ful return false; } - if (Settings->bIncludeEngineModules == Settings->bIncludeEnginePluginModules) + if (Settings->bPreloadEngineModules == Settings->bPreloadEnginePluginModules) { - return Settings->bIncludeEngineModules; + return Settings->bPreloadEngineModules; } if(FullFilePath.StartsWith(FullEnginePluginsDir)) { - return Settings->bIncludeEnginePluginModules; + return Settings->bPreloadEnginePluginModules; } else { - return Settings->bIncludeEngineModules; + return Settings->bPreloadEngineModules; } } } diff --git a/Engine/Source/Developer/Windows/LiveCoding/Private/LiveCodingModule.h b/Engine/Source/Developer/Windows/LiveCoding/Private/LiveCodingModule.h index 3f499d765ad0..3208b1f26574 100644 --- a/Engine/Source/Developer/Windows/LiveCoding/Private/LiveCodingModule.h +++ b/Engine/Source/Developer/Windows/LiveCoding/Private/LiveCodingModule.h @@ -26,6 +26,7 @@ public: virtual bool IsEnabledByDefault() const override; virtual void EnableForSession(bool bInEnabled) override; virtual bool IsEnabledForSession() const override; + virtual bool CanEnableForSession() const override; virtual bool HasStarted() const override; virtual void ShowConsole() override; virtual void Compile() override; @@ -38,7 +39,7 @@ private: bool bEnabledLastTick; bool bEnabledForSession; bool bStarted; - TSet EnabledModules; + TSet ConfiguredModules; const FString FullEnginePluginsDir; const FString FullProjectDir; @@ -54,10 +55,8 @@ private: void OnModulesChanged(FName ModuleName, EModuleChangeReason Reason); void UpdateModules(); - void EnableModule(const FString& FullFilePath); - void DisableModule(const FString& FullFilePath); void ConfigureModule(const FName& Name, const FString& FullFilePath); - bool ShouldEnableModule(const FName& Name, const FString& FullFilePath) const; + bool ShouldPreloadModule(const FName& Name, const FString& FullFilePath) const; }; diff --git a/Engine/Source/Developer/Windows/LiveCoding/Private/LiveCodingSettings.cpp b/Engine/Source/Developer/Windows/LiveCoding/Private/LiveCodingSettings.cpp index d90622ce0b22..f489aa0798b2 100644 --- a/Engine/Source/Developer/Windows/LiveCoding/Private/LiveCodingSettings.cpp +++ b/Engine/Source/Developer/Windows/LiveCoding/Private/LiveCodingSettings.cpp @@ -5,10 +5,10 @@ ULiveCodingSettings::ULiveCodingSettings(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { - UProperty* EngineModulesProperty = StaticClass()->FindPropertyByName("bIncludeEngineModules"); + UProperty* EngineModulesProperty = StaticClass()->FindPropertyByName("bPreloadEngineModules"); check(EngineModulesProperty != nullptr); - UProperty* EnginePluginModulesProperty = StaticClass()->FindPropertyByName("bIncludeEnginePluginModules"); + UProperty* EnginePluginModulesProperty = StaticClass()->FindPropertyByName("bPreloadEnginePluginModules"); check(EnginePluginModulesProperty != nullptr); if (FApp::IsEngineInstalled()) @@ -17,6 +17,6 @@ ULiveCodingSettings::ULiveCodingSettings(const FObjectInitializer& ObjectInitial EnginePluginModulesProperty->ClearPropertyFlags(CPF_Edit); } - bIncludeProjectModules = true; - bIncludeProjectPluginModules = true; + bPreloadProjectModules = true; + bPreloadProjectPluginModules = true; } diff --git a/Engine/Source/Developer/Windows/LiveCoding/Private/LiveCodingSettings.h b/Engine/Source/Developer/Windows/LiveCoding/Private/LiveCodingSettings.h index f829e8fb8153..56e8dce3c934 100644 --- a/Engine/Source/Developer/Windows/LiveCoding/Private/LiveCodingSettings.h +++ b/Engine/Source/Developer/Windows/LiveCoding/Private/LiveCodingSettings.h @@ -20,29 +20,26 @@ class ULiveCodingSettings : public UObject GENERATED_BODY() public: - UPROPERTY(config, EditAnywhere, Category=General, Meta=(DisplayName="Enable Live Coding")) + UPROPERTY(config, EditAnywhere, Category=General, Meta=(ConfigRestartRequired=true, DisplayName="Enable Live Coding")) bool bEnabled; UPROPERTY(config, EditAnywhere, Category=General, Meta=(ConfigRestartRequired=true, EditCondition="bEnabled")) ELiveCodingStartupMode Startup; UPROPERTY(config, EditAnywhere, Category=Modules, Meta=(ConfigRestartRequired=true, EditCondition="bEnabled")) - bool bIncludeEngineModules; + bool bPreloadEngineModules; UPROPERTY(config, EditAnywhere, Category=Modules, Meta=(ConfigRestartRequired=true, EditCondition="bEnabled")) - bool bIncludeEnginePluginModules; + bool bPreloadEnginePluginModules; UPROPERTY(config, EditAnywhere, Category=Modules, Meta=(ConfigRestartRequired=true, EditCondition="bEnabled")) - bool bIncludeProjectModules; + bool bPreloadProjectModules; UPROPERTY(config, EditAnywhere, Category=Modules, Meta=(ConfigRestartRequired=true, EditCondition="bEnabled")) - bool bIncludeProjectPluginModules; + bool bPreloadProjectPluginModules; UPROPERTY(config, EditAnywhere, Category=Modules, Meta=(ConfigRestartRequired=true, EditCondition="bEnabled")) - TArray IncludeSpecificModules; - - UPROPERTY(config, EditAnywhere, Category=Modules, Meta=(ConfigRestartRequired=true, EditCondition="bEnabled")) - TArray ExcludeSpecificModules; + TArray PreloadNamedModules; ULiveCodingSettings(const FObjectInitializer& Initializer); }; diff --git a/Engine/Source/Developer/Windows/LiveCoding/Public/ILiveCodingModule.h b/Engine/Source/Developer/Windows/LiveCoding/Public/ILiveCodingModule.h index d5d5115a977c..654291a377fb 100644 --- a/Engine/Source/Developer/Windows/LiveCoding/Public/ILiveCodingModule.h +++ b/Engine/Source/Developer/Windows/LiveCoding/Public/ILiveCodingModule.h @@ -15,6 +15,7 @@ public: virtual void EnableForSession(bool bEnabled) = 0; virtual bool IsEnabledForSession() const = 0; + virtual bool CanEnableForSession() const = 0; virtual bool HasStarted() const = 0; diff --git a/Engine/Source/Developer/Windows/LiveCodingServer/LiveCodingServer.Build.cs b/Engine/Source/Developer/Windows/LiveCodingServer/LiveCodingServer.Build.cs index 661efa25e938..8bec7f955e44 100644 --- a/Engine/Source/Developer/Windows/LiveCodingServer/LiveCodingServer.Build.cs +++ b/Engine/Source/Developer/Windows/LiveCodingServer/LiveCodingServer.Build.cs @@ -27,6 +27,10 @@ public class LiveCodingServer : ModuleRules RuntimeDependencies.Add("$(TargetOutputDir)/msdia140.dll", Path.Combine(DiaSdkDir, "bin", "amd64", "msdia140.dll")); } - PrecompileForTargets = PrecompileTargetsType.None; + // Allow precompiling when generating project files so we can get intellisense + if(!Target.bGenerateProjectFiles) + { + PrecompileForTargets = PrecompileTargetsType.None; + } } } diff --git a/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_LiveModule.cpp b/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_LiveModule.cpp index 285f532cccbe..5d76bc590cee 100644 --- a/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_LiveModule.cpp +++ b/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_LiveModule.cpp @@ -1334,7 +1334,7 @@ LiveModule::ErrorType::Enum LiveModule::Update(FileAttributeCache* fileCache, Di LC_LOG_USER("File %S was modified or is new", modifiedOrNewObjFiles[i].c_str()); } - LC_LOG_USER("Building patch from %zu file(s) for Live++ module %S", modifiedOrNewObjFiles.size(), m_moduleName.c_str()); + LC_LOG_USER("Building patch from %zu file(s) for Live Coding module %S", modifiedOrNewObjFiles.size(), m_moduleName.c_str()); } // let the user know that we're about to compile diff --git a/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_LiveProcess.cpp b/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_LiveProcess.cpp index c73c66b49be0..b937ea330fe1 100644 --- a/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_LiveProcess.cpp +++ b/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_LiveProcess.cpp @@ -70,3 +70,41 @@ bool LiveProcess::TriedToLoadImage(const executable::Header& imageHeader) const { return (m_imagesTriedToLoad.find(imageHeader) != m_imagesTriedToLoad.end()); } + +// BEGIN EPIC MOD - Allow lazy-loading modules +void LiveProcess::AddLazyLoadedModule(const std::wstring moduleName, Windows::HMODULE moduleBase) +{ + LazyLoadedModule module; + module.m_moduleBase = moduleBase; + module.m_loaded = false; + m_lazyLoadedModules.insert(std::make_pair(moduleName, module)); +} + +void LiveProcess::SetLazyLoadedModuleAsLoaded(const std::wstring moduleName) +{ + std::unordered_map::iterator it = m_lazyLoadedModules.find(moduleName); + if (it != m_lazyLoadedModules.end()) + { + it->second.m_loaded = true; + } +} + +bool LiveProcess::IsPendingLazyLoadedModule(const std::wstring& moduleName) const +{ + std::unordered_map::const_iterator iter = m_lazyLoadedModules.find(moduleName); + return iter != m_lazyLoadedModules.end() && !iter->second.m_loaded; +} + +Windows::HMODULE LiveProcess::GetLazyLoadedModuleBase(const std::wstring& moduleName) const +{ + std::unordered_map::const_iterator iter = m_lazyLoadedModules.find(moduleName); + if (iter == m_lazyLoadedModules.end()) + { + return nullptr; + } + else + { + return iter->second.m_moduleBase; + } +} +// END EPIC MOD diff --git a/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_LiveProcess.h b/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_LiveProcess.h index f0a93022ef1c..3e63be616509 100644 --- a/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_LiveProcess.h +++ b/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_LiveProcess.h @@ -64,6 +64,13 @@ public: } // END EPIC MOD + // BEGIN EPIC MOD - Allow lazy-loading modules + void AddLazyLoadedModule(const std::wstring moduleName, Windows::HMODULE moduleBase); + void SetLazyLoadedModuleAsLoaded(const std::wstring moduleName); + bool IsPendingLazyLoadedModule(const std::wstring& moduleName) const; + Windows::HMODULE GetLazyLoadedModuleBase(const std::wstring& moduleName) const; + // END EPIC MOD + private: process::Handle m_processHandle; unsigned int m_processId; @@ -74,6 +81,16 @@ private: std::wstring m_buildArguments; // END EPIC MOD + // BEGIN EPIC MOD - Allow lazy-loading modules + struct LazyLoadedModule + { + Windows::HMODULE m_moduleBase; + bool m_loaded; + }; + + types::unordered_map m_lazyLoadedModules; + // END EPIC MOD + // loaded modules are not identified by their full path, but by their executable image header. // we do this to ensure that the same executable loaded from a different path is not treated as // a different executable. diff --git a/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_ServerCommandThread.cpp b/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_ServerCommandThread.cpp index 2ecff0767ae8..39549a7ad899 100644 --- a/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_ServerCommandThread.cpp +++ b/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_ServerCommandThread.cpp @@ -22,6 +22,7 @@ #include "LC_PrimitiveNames.h" #include "LC_AppSettings.h" #include "LC_Allocators.h" +#include "LC_DuplexPipeClient.h" #include // unreachable code @@ -584,6 +585,96 @@ BOOL CALLBACK FocusApplicationWindows(HWND WindowHandle, LPARAM Lparam) } // END EPIC MOD +// BEGIN EPIC MOD - Support for lazy-loading modules +bool ServerCommandThread::FinishedLazyLoadingModulesAction::Execute(CommandType* command, const DuplexPipe* pipe, void* context) +{ + pipe->SendAck(); + return false; +} + +struct ClientProxyThread +{ + struct ProxyGetModuleAction + { + typedef commands::GetModule CommandType; + + static bool Execute(CommandType* command, const DuplexPipe* pipe, void* context) + { + pipe->SendAck(); + + LiveProcess* process = static_cast(context); + + commands::GetModuleInfo cmd; + cmd.moduleBase = process->GetLazyLoadedModuleBase(command->path); + cmd.processId = process->GetProcessId(); + cmd.loadImports = command->loadImports; + cmd.taskContext = command->taskContext; + wcscpy_s(cmd.path, command->path); + pipe->SendCommandAndWaitForAck(cmd); + + return true; + } + }; + + struct ProxyEnableModuleFinishedAction + { + typedef commands::EnableModuleFinished CommandType; + + static bool Execute(CommandType* command, const DuplexPipe* pipe, void* context) + { + pipe->SendAck(); + return false; + } + }; + + LiveProcess* m_process; + DuplexPipeClient* m_pipe; + std::vector m_enableModules; + thread::Handle m_threadHandle; + + ClientProxyThread(LiveProcess* process, DuplexPipeClient* pipe, const std::vector enableModules) + : m_process(process) + , m_pipe(pipe) + , m_enableModules(enableModules) + { + m_threadHandle = thread::Create(64u * 1024u, &StaticEntryPoint, this); + thread::SetName("Live coding client proxy"); + } + + ~ClientProxyThread() + { + thread::Join(m_threadHandle); + thread::Close(m_threadHandle); + } + + static unsigned int __stdcall StaticEntryPoint(void* context) + { + static_cast(context)->EntryPoint(); + return 0; + } + + void EntryPoint() + { + m_pipe->SendCommandAndWaitForAck(commands::EnableModuleBatchBegin()); + for(const std::wstring& enableModule : m_enableModules) + { + commands::EnableModule enableModuleCommand; + enableModuleCommand.processId = m_process->GetProcessId(); + wcscpy_s(enableModuleCommand.path, enableModule.c_str()); + enableModuleCommand.token = nullptr; + m_pipe->SendCommandAndWaitForAck(enableModuleCommand); + + CommandMap commandMap; + commandMap.RegisterAction(); + commandMap.RegisterAction(); + commandMap.HandleCommands(m_pipe, m_process); + } + m_pipe->SendCommandAndWaitForAck(commands::EnableModuleBatchEnd()); + m_pipe->SendCommandAndWaitForAck(commands::FinishedLazyLoadingModules()); + } +}; +// END EPIC MOD + void ServerCommandThread::CompileChanges(bool didAllProcessesMakeProgress) { // recompile files, if any @@ -602,6 +693,7 @@ void ServerCommandThread::CompileChanges(bool didAllProcessesMakeProgress) const ILiveCodingServer::FCompileDelegate& CompileDelegate = GLiveCodingServer->GetCompileDelegate(); if (CompileDelegate.IsBound()) { + // Get the list of arguments for building each target, and use the delegate to pass them to UBT TArray Targets; for (LiveProcess* liveProcess : m_liveProcesses) { @@ -617,6 +709,45 @@ void ServerCommandThread::CompileChanges(bool didAllProcessesMakeProgress) return; } + // Enable any lazy-loaded modules that we need + for (LiveProcess* liveProcess : m_liveProcesses) + { + types::vector LoadModuleFileNames; + for(const TPair>& Pair : ModuleToObjectFiles) + { + std::wstring ModuleFileName = file::NormalizePath(*Pair.Key); + if (liveProcess->IsPendingLazyLoadedModule(ModuleFileName)) + { + LoadModuleFileNames.push_back(ModuleFileName); + } + } + if (LoadModuleFileNames.size() > 0) + { + const std::wstring PipeName = primitiveNames::Pipe(m_processGroupName + L"_ClientProxy"); + + DuplexPipeServer ServerPipe; + ServerPipe.Create(PipeName.c_str()); + + DuplexPipeClient ClientPipe; + ClientPipe.Connect(PipeName.c_str()); + + ClientProxyThread ClientThread(liveProcess, &ClientPipe, LoadModuleFileNames); + + CommandMap commandMap; + commandMap.RegisterAction(); + commandMap.RegisterAction(); + commandMap.RegisterAction(); + commandMap.RegisterAction(); + commandMap.HandleCommands(&ServerPipe, this); + + for (const std::wstring& loadModuleFileName : LoadModuleFileNames) + { + liveProcess->SetLazyLoadedModuleAsLoaded(loadModuleFileName); + } + } + } + + // Build up a list of all the modified object files in each module types::unordered_set ValidModuleFileNames; for (const LiveModule* liveModule : m_liveModules) { @@ -1046,6 +1177,9 @@ unsigned int ServerCommandThread::CommandThread(DuplexPipeServer* pipe, Event* r // BEGIN EPIC MOD - Adding SetBuildArguments command commandMap.RegisterAction(); // END EPIC MOD + // BEGIN EPIC MOD - Support for lazy-loading modules + commandMap.RegisterAction(); + // END EPIC MOD commandMap.RegisterAction(); commandMap.RegisterAction(); commandMap.RegisterAction(); @@ -1356,6 +1490,30 @@ bool ServerCommandThread::SetBuildArgumentsAction::Execute(CommandType* command, } // END EPIC MOD +// BEGIN EPIC MOD - Support for lazy-loading modules +bool ServerCommandThread::EnableLazyLoadedModuleAction::Execute(CommandType* command, const DuplexPipe* pipe, void* context) +{ + ServerCommandThread* commandThread = static_cast(context); + + // protect against accepting this command while compilation is already in progress + CriticalSection::ScopedLock lock(&commandThread->m_actionCS); + + for (LiveProcess* process : commandThread->m_liveProcesses) + { + if (process->GetProcessId() == command->processId) + { + const std::wstring modulePath = file::NormalizePath(command->fileName); + process->AddLazyLoadedModule(modulePath, command->moduleBase); + LC_LOG_DEV("Registered module %S for lazy-loading", modulePath.c_str()); + } + } + + pipe->SendAck(); + + return true; +} +// END EPIC MOD + bool ServerCommandThread::RegisterProcessAction::Execute(CommandType* command, const DuplexPipe* pipe, void* context) { pipe->SendAck(); @@ -1453,6 +1611,9 @@ bool ServerCommandThread::EnableModuleBatchEndAction::Execute(CommandType*, cons // EPIC REMOVED: g_theApp.GetMainFrame()->ResetStatusBarText(); // tell user we are ready + // BEGIN EPIC MOD - Support for lazy-loading modules + if (thread::GetId() != thread::GetId(commandThread->m_compileThread)) + // END EPIC MOD { const int shortcut = appSettings::g_compileShortcut->GetValue(); const std::wstring& shortcutText = shortcut::ConvertShortcutToText(shortcut); diff --git a/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_ServerCommandThread.h b/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_ServerCommandThread.h index 8d3890bdd849..2a0486eca342 100644 --- a/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_ServerCommandThread.h +++ b/Engine/Source/Developer/Windows/LiveCodingServer/Private/External/LC_ServerCommandThread.h @@ -136,6 +136,20 @@ private: }; // END EPIC MOD + // BEGIN EPIC MOD - Adding support for lazy-loading modules + struct EnableLazyLoadedModuleAction + { + typedef commands::EnableLazyLoadedModule CommandType; + static bool Execute(CommandType* command, const DuplexPipe* pipe, void* context); + }; + + struct FinishedLazyLoadingModulesAction + { + typedef commands::FinishedLazyLoadingModules CommandType; + static bool Execute(CommandType* command, const DuplexPipe* pipe, void* context); + }; + // END EPIC MOD + struct RegisterProcessAction { typedef commands::RegisterProcess CommandType; diff --git a/Engine/Source/Developer/Windows/WindowsTargetPlatform/Private/GenericWindowsTargetPlatform.h b/Engine/Source/Developer/Windows/WindowsTargetPlatform/Private/GenericWindowsTargetPlatform.h index 0d6294303790..8a9eb2d57dac 100644 --- a/Engine/Source/Developer/Windows/WindowsTargetPlatform/Private/GenericWindowsTargetPlatform.h +++ b/Engine/Source/Developer/Windows/WindowsTargetPlatform/Private/GenericWindowsTargetPlatform.h @@ -110,7 +110,7 @@ public: } } - virtual bool GenerateStreamingInstallManifest(const TMultiMap& ChunkMap, const TSet& ChunkIDsInUse) const override + virtual bool GenerateStreamingInstallManifest(const TMultiMap& PakchunkMap, const TSet& PakchunkIndicesInUse) const override { return true; } diff --git a/Engine/Source/Editor/AnimationBlueprintEditor/Private/StateMachineConnectionDrawingPolicy.cpp b/Engine/Source/Editor/AnimationBlueprintEditor/Private/StateMachineConnectionDrawingPolicy.cpp index 8dde0f3209dc..532db240c1cb 100644 --- a/Engine/Source/Editor/AnimationBlueprintEditor/Private/StateMachineConnectionDrawingPolicy.cpp +++ b/Engine/Source/Editor/AnimationBlueprintEditor/Private/StateMachineConnectionDrawingPolicy.cpp @@ -75,9 +75,9 @@ void FStateMachineConnectionDrawingPolicy::DetermineLinkGeometry( { StartWidgetGeometry = PinGeometries->Find(OutputPinWidget); - if (TSharedRef* pTargetWidget = PinToPinWidgetMap.Find(InputPin)) + if (TSharedPtr* pTargetWidget = PinToPinWidgetMap.Find(InputPin)) { - TSharedRef InputWidget = *pTargetWidget; + TSharedRef InputWidget = (*pTargetWidget).ToSharedRef(); EndWidgetGeometry = PinGeometries->Find(InputWidget); } } diff --git a/Engine/Source/Editor/BlueprintGraph/Private/EdGraphSchema_K2.cpp b/Engine/Source/Editor/BlueprintGraph/Private/EdGraphSchema_K2.cpp index 07897e208b31..333520f888ed 100644 --- a/Engine/Source/Editor/BlueprintGraph/Private/EdGraphSchema_K2.cpp +++ b/Engine/Source/Editor/BlueprintGraph/Private/EdGraphSchema_K2.cpp @@ -6112,7 +6112,8 @@ bool UEdGraphSchema_K2::ReplaceOldNodeWithNew(UK2Node* OldNode, UK2Node* NewNode NewNode->NodeComment = OldNode->NodeComment; NewNode->bCommentBubblePinned = OldNode->bCommentBubblePinned; NewNode->bCommentBubbleVisible = OldNode->bCommentBubbleVisible; - + + FLinkerLoad::InvalidateExport(OldNode); OldNode->DestroyNode(); } return !bFailedToFindPin; diff --git a/Engine/Source/Editor/Blutility/Classes/EditorUtilityWidget.h b/Engine/Source/Editor/Blutility/Classes/EditorUtilityWidget.h index dc05646e9864..a35d81fdb29b 100644 --- a/Engine/Source/Editor/Blutility/Classes/EditorUtilityWidget.h +++ b/Engine/Source/Editor/Blutility/Classes/EditorUtilityWidget.h @@ -16,7 +16,7 @@ class AActor; class UEditorPerProjectUserSettings; -UCLASS(Abstract, config = Editor) +UCLASS(Abstract, meta = (ShowWorldContextPin), config = Editor) class BLUTILITY_API UEditorUtilityWidget : public UUserWidget { GENERATED_UCLASS_BODY() diff --git a/Engine/Source/Editor/Blutility/Private/EditorUtilityWidgetBlueprint.cpp b/Engine/Source/Editor/Blutility/Private/EditorUtilityWidgetBlueprint.cpp index f7e0b5013584..46f7efc83b66 100644 --- a/Engine/Source/Editor/Blutility/Private/EditorUtilityWidgetBlueprint.cpp +++ b/Engine/Source/Editor/Blutility/Private/EditorUtilityWidgetBlueprint.cpp @@ -26,11 +26,20 @@ void UEditorUtilityWidgetBlueprint::BeginDestroy() if (!GIsRequestingExit) { IBlutilityModule* BlutilityModule = FModuleManager::GetModulePtr("Blutility"); - BlutilityModule->RemoveLoadedScriptUI(this); + if (BlutilityModule) + { + BlutilityModule->RemoveLoadedScriptUI(this); + } - FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked(TEXT("LevelEditor")); - TSharedPtr LevelEditorTabManager = LevelEditorModule.GetLevelEditorTabManager(); - LevelEditorTabManager->UnregisterTabSpawner(RegistrationName); + FLevelEditorModule* LevelEditorModule = FModuleManager::GetModulePtr(TEXT("LevelEditor")); + if (LevelEditorModule) + { + TSharedPtr LevelEditorTabManager = LevelEditorModule->GetLevelEditorTabManager(); + if (LevelEditorTabManager.IsValid()) + { + LevelEditorTabManager->UnregisterTabSpawner(RegistrationName); + } + } } Super::BeginDestroy(); diff --git a/Engine/Source/Editor/GameProjectGeneration/GameProjectGeneration.Build.cs b/Engine/Source/Editor/GameProjectGeneration/GameProjectGeneration.Build.cs index ec1889c0704d..6d41430db01d 100644 --- a/Engine/Source/Editor/GameProjectGeneration/GameProjectGeneration.Build.cs +++ b/Engine/Source/Editor/GameProjectGeneration/GameProjectGeneration.Build.cs @@ -59,5 +59,10 @@ public class GameProjectGeneration : ModuleRules "MainFrame", } ); + + if(Target.bWithLiveCoding) + { + PrivateIncludePathModuleNames.Add("LiveCoding"); + } } } diff --git a/Engine/Source/Editor/GameProjectGeneration/Private/GameProjectUtils.cpp b/Engine/Source/Editor/GameProjectGeneration/Private/GameProjectUtils.cpp index 2d4e180e90e6..f618ec90150f 100644 --- a/Engine/Source/Editor/GameProjectGeneration/Private/GameProjectUtils.cpp +++ b/Engine/Source/Editor/GameProjectGeneration/Private/GameProjectUtils.cpp @@ -76,6 +76,10 @@ #include "ProjectBuildMutatorFeature.h" +#if WITH_LIVE_CODING +#include "ILiveCodingModule.h" +#endif + #define LOCTEXT_NAMESPACE "GameProjectUtils" #define MAX_PROJECT_PATH_BUFFER_SPACE 130 // Leave a reasonable buffer of additional characters to account for files created in the content directory during or after project generation @@ -3813,12 +3817,8 @@ GameProjectUtils::EAddCodeToProjectResult GameProjectUtils::AddCodeToProject_Int // First see if we can avoid a full generation by adding the new files to an already open project if ( bProjectHadCodeFiles && FSourceCodeNavigation::AddSourceFiles(CreatedFilesForExternalAppRead) ) { - // We successfully added the new files to the solution, but we still need to run UBT with -gather to update any UBT makefiles - if ( FDesktopPlatformModule::Get()->InvalidateMakefiles(FPaths::RootDir(), FPaths::GetProjectFilePath(), GWarn) ) - { - // We managed the gather, so we can skip running the full generate - bGenerateProjectFiles = false; - } + // We managed the gather, so we can skip running the full generate + bGenerateProjectFiles = false; } if ( bGenerateProjectFiles ) @@ -3845,6 +3845,15 @@ GameProjectUtils::EAddCodeToProjectResult GameProjectUtils::AddCodeToProject_Int OutHeaderFilePath = NewHeaderFilename; OutCppFilePath = NewCppFilename; +#if WITH_LIVE_CODING + ILiveCodingModule* LiveCoding = FModuleManager::GetModulePtr(LIVE_CODING_MODULE_NAME); + if (LiveCoding != nullptr && LiveCoding->IsEnabledForSession()) + { + OutFailReason = LOCTEXT("FailedToCompileLiveCodingEnabled", "Adding classes dynamically is not allowed with Live Coding enabled."); + return EAddCodeToProjectResult::FailedToHotReload; + } +#endif + if (!bProjectHadCodeFiles) { // This is the first time we add code to this project so compile its game DLL diff --git a/Engine/Source/Editor/GraphEditor/Private/BlueprintConnectionDrawingPolicy.cpp b/Engine/Source/Editor/GraphEditor/Private/BlueprintConnectionDrawingPolicy.cpp index e009bb5d999f..6090f326a36d 100644 --- a/Engine/Source/Editor/GraphEditor/Private/BlueprintConnectionDrawingPolicy.cpp +++ b/Engine/Source/Editor/GraphEditor/Private/BlueprintConnectionDrawingPolicy.cpp @@ -363,9 +363,9 @@ FKismetConnectionDrawingPolicy::FTimePair const* FKismetConnectionDrawingPolicy: bool FKismetConnectionDrawingPolicy::FindPinCenter(UEdGraphPin* Pin, FVector2D& OutCenter) const { - if (const TSharedRef* pPinWidget = PinToPinWidgetMap.Find(Pin)) + if (const TSharedPtr* pPinWidget = PinToPinWidgetMap.Find(Pin)) { - if (FArrangedWidget* pPinEntry = PinGeometries->Find(*pPinWidget)) + if (FArrangedWidget* pPinEntry = PinGeometries->Find((*pPinWidget).ToSharedRef())) { OutCenter = FGeometryHelper::CenterOf(pPinEntry->Geometry); return true; diff --git a/Engine/Source/Editor/GraphEditor/Private/ConnectionDrawingPolicy.cpp b/Engine/Source/Editor/GraphEditor/Private/ConnectionDrawingPolicy.cpp index 2f574c938fce..9f5705cb90f5 100644 --- a/Engine/Source/Editor/GraphEditor/Private/ConnectionDrawingPolicy.cpp +++ b/Engine/Source/Editor/GraphEditor/Private/ConnectionDrawingPolicy.cpp @@ -424,9 +424,9 @@ void FConnectionDrawingPolicy::DetermineLinkGeometry( { StartWidgetGeometry = PinGeometries->Find(OutputPinWidget); - if (TSharedRef* pTargetWidget = PinToPinWidgetMap.Find(InputPin)) + if (TSharedPtr* pTargetWidget = PinToPinWidgetMap.Find(InputPin)) { - TSharedRef InputWidget = *pTargetWidget; + TSharedRef InputWidget = (*pTargetWidget).ToSharedRef(); EndWidgetGeometry = PinGeometries->Find(InputWidget); } } diff --git a/Engine/Source/Editor/GraphEditor/Private/KismetPins/SGraphPinClass.cpp b/Engine/Source/Editor/GraphEditor/Private/KismetPins/SGraphPinClass.cpp index 1b4cc6daf332..3b81336921c3 100644 --- a/Engine/Source/Editor/GraphEditor/Private/KismetPins/SGraphPinClass.cpp +++ b/Engine/Source/Editor/GraphEditor/Private/KismetPins/SGraphPinClass.cpp @@ -19,6 +19,7 @@ void SGraphPinClass::Construct(const FArguments& InArgs, UEdGraphPin* InGraphPinObj) { SGraphPin::Construct(SGraphPin::FArguments(), InGraphPinObj); + bAllowAbstractClasses = true; } FReply SGraphPinClass::OnClickUse() @@ -52,6 +53,8 @@ public: /** All children of these classes will be included unless filtered out by another setting. */ TSet< const UClass* > AllowedChildrenOfClasses; + bool bAllowAbstractClasses = true; + virtual bool IsClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const UClass* InClass, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs ) override { // If it appears on the allowed child-of classes list (or there is nothing on that list) @@ -64,6 +67,7 @@ public: // Don't allow classes from a loaded map (e.g. LSBPs) unless we're already working inside that package context. Otherwise, choosing the class would lead to a GLEO at save time. Result &= !ClassPackage->ContainsMap() || ClassPackage == GraphPinOutermostPackage; + Result &= bAllowAbstractClasses || !InClass->HasAnyClassFlags(CLASS_Abstract); } return Result; @@ -71,7 +75,7 @@ public: virtual bool IsUnloadedClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const TSharedRef< const IUnloadedBlueprintData > InUnloadedClassData, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs) override { - return (InFilterFuncs->IfInChildOfClassesSet( AllowedChildrenOfClasses, InUnloadedClassData) != EFilterReturn::Failed); + return (InFilterFuncs->IfInChildOfClassesSet( AllowedChildrenOfClasses, InUnloadedClassData) != EFilterReturn::Failed) && (bAllowAbstractClasses || !InUnloadedClassData->HasAnyClassFlags(CLASS_Abstract)); } }; @@ -93,6 +97,7 @@ TSharedRef SGraphPinClass::GenerateAssetPicker() } TSharedPtr Filter = MakeShareable(new FGraphPinFilter); + Filter->bAllowAbstractClasses = bAllowAbstractClasses; Options.ClassFilter = Filter; Filter->AllowedChildrenOfClasses.Add(PinRequiredParentClass); diff --git a/Engine/Source/Editor/GraphEditor/Private/MaterialGraphConnectionDrawingPolicy.cpp b/Engine/Source/Editor/GraphEditor/Private/MaterialGraphConnectionDrawingPolicy.cpp index 280a534667bd..2fb78403b691 100644 --- a/Engine/Source/Editor/GraphEditor/Private/MaterialGraphConnectionDrawingPolicy.cpp +++ b/Engine/Source/Editor/GraphEditor/Private/MaterialGraphConnectionDrawingPolicy.cpp @@ -26,9 +26,9 @@ FMaterialGraphConnectionDrawingPolicy::FMaterialGraphConnectionDrawingPolicy(int bool FMaterialGraphConnectionDrawingPolicy::FindPinCenter(UEdGraphPin* Pin, FVector2D& OutCenter) const { - if (const TSharedRef* pPinWidget = PinToPinWidgetMap.Find(Pin)) + if (const TSharedPtr* pPinWidget = PinToPinWidgetMap.Find(Pin)) { - if (FArrangedWidget* pPinEntry = PinGeometries->Find(*pPinWidget)) + if (FArrangedWidget* pPinEntry = PinGeometries->Find((*pPinWidget).ToSharedRef())) { OutCenter = FGeometryHelper::CenterOf(pPinEntry->Geometry); return true; diff --git a/Engine/Source/Editor/GraphEditor/Public/ConnectionDrawingPolicy.h b/Engine/Source/Editor/GraphEditor/Public/ConnectionDrawingPolicy.h index 71ef0ea22ca3..e44fdba0cf18 100644 --- a/Engine/Source/Editor/GraphEditor/Public/ConnectionDrawingPolicy.h +++ b/Engine/Source/Editor/GraphEditor/Public/ConnectionDrawingPolicy.h @@ -91,7 +91,7 @@ protected: float HoverDeemphasisDarkFraction; const FSlateRect& ClippingRect; FSlateWindowElementList& DrawElementsList; - TMap< UEdGraphPin*, TSharedRef > PinToPinWidgetMap; + TMap< UEdGraphPin*, TSharedPtr > PinToPinWidgetMap; TSet< FEdGraphPinReference > HoveredPins; TMap, FArrangedWidget>* PinGeometries; double LastHoverTimeEvent; diff --git a/Engine/Source/Editor/GraphEditor/Private/KismetPins/SGraphPinClass.h b/Engine/Source/Editor/GraphEditor/Public/KismetPins/SGraphPinClass.h similarity index 81% rename from Engine/Source/Editor/GraphEditor/Private/KismetPins/SGraphPinClass.h rename to Engine/Source/Editor/GraphEditor/Public/KismetPins/SGraphPinClass.h index 50af24269aaa..f12d6c096abc 100644 --- a/Engine/Source/Editor/GraphEditor/Private/KismetPins/SGraphPinClass.h +++ b/Engine/Source/Editor/GraphEditor/Public/KismetPins/SGraphPinClass.h @@ -11,13 +11,14 @@ ///////////////////////////////////////////////////// // SGraphPinClass -class SGraphPinClass : public SGraphPinObject +class GRAPHEDITOR_API SGraphPinClass : public SGraphPinObject { public: SLATE_BEGIN_ARGS(SGraphPinClass) {} SLATE_END_ARGS() void Construct(const FArguments& InArgs, UEdGraphPin* InGraphPinObj); + void SetAllowAbstractClasses(bool bAllow) { bAllowAbstractClasses = bAllow; } protected: // Called when a new class was picked via the asset picker @@ -34,4 +35,7 @@ protected: /** Cached AssetData without the _C */ mutable FAssetData CachedEditorAssetData; + + /** Whether abstract classes should be filtered out in the class viewer */ + bool bAllowAbstractClasses; }; diff --git a/Engine/Source/Editor/InputBindingEditor/Private/InputBindingEditorModule.cpp b/Engine/Source/Editor/InputBindingEditor/Private/InputBindingEditorModule.cpp index f2b1e4942e62..e7147d62632d 100644 --- a/Engine/Source/Editor/InputBindingEditor/Private/InputBindingEditorModule.cpp +++ b/Engine/Source/Editor/InputBindingEditor/Private/InputBindingEditorModule.cpp @@ -30,6 +30,15 @@ public: return MakeShareable( new FEditorKeyboardShortcutSettings ); } + virtual void PendingDelete() + { + if (EditorPanel.IsValid()) + { + ensure(EditorPanel.IsUnique()); + EditorPanel.Reset(); + } + } + virtual void CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) { EditorPanel = MakeShareable( new FInputBindingEditorPanel ); diff --git a/Engine/Source/Editor/Kismet/Private/BlueprintEditor.cpp b/Engine/Source/Editor/Kismet/Private/BlueprintEditor.cpp index a7ffc668d8a1..4485f3814229 100644 --- a/Engine/Source/Editor/Kismet/Private/BlueprintEditor.cpp +++ b/Engine/Source/Editor/Kismet/Private/BlueprintEditor.cpp @@ -7997,7 +7997,7 @@ TSharedPtr FBlueprintEditor::CustomizeSCSEditor(USceneC FText FBlueprintEditor::GetPIEStatus() const { UBlueprint* CurrentBlueprint = GetBlueprintObj(); - UWorld *DebugWorld = NULL; + UWorld *DebugWorld = nullptr; ENetMode NetMode = NM_Standalone; if (CurrentBlueprint) { @@ -8009,18 +8009,24 @@ FText FBlueprintEditor::GetPIEStatus() const else { UObject* ObjOuter = CurrentBlueprint->GetObjectBeingDebugged(); - while(DebugWorld == NULL && ObjOuter != NULL) + while(DebugWorld == nullptr && ObjOuter != nullptr) { ObjOuter = ObjOuter->GetOuter(); DebugWorld = Cast(ObjOuter); } + + if (DebugWorld) + { + // Redirect through streaming levels to find the owning world; this ensures that we always use the appropriate NetMode for the context string below. + if (DebugWorld->PersistentLevel != nullptr && DebugWorld->PersistentLevel->OwningWorld != nullptr) + { + DebugWorld = DebugWorld->PersistentLevel->OwningWorld; + } + + NetMode = DebugWorld->GetNetMode(); + } } } - - if (DebugWorld) - { - NetMode = DebugWorld->GetNetMode(); - } if (NetMode == NM_ListenServer || NetMode == NM_DedicatedServer) { @@ -8028,6 +8034,12 @@ FText FBlueprintEditor::GetPIEStatus() const } else if (NetMode == NM_Client) { + FWorldContext* PIEContext = GEngine->GetWorldContextFromWorld(DebugWorld); + if (PIEContext && PIEContext->PIEInstance > 1) + { + return FText::Format(LOCTEXT("PIEStatusClientSimulatingFormat", "CLIENT {0} - SIMULATING"), FText::AsNumber(PIEContext->PIEInstance - 1)); + } + return LOCTEXT("PIEStatusClientSimulating", "CLIENT - SIMULATING"); } diff --git a/Engine/Source/Editor/Kismet/Private/SBlueprintDiff.cpp b/Engine/Source/Editor/Kismet/Private/SBlueprintDiff.cpp index eabaf3964323..cb8bf5eb3d54 100644 --- a/Engine/Source/Editor/Kismet/Private/SBlueprintDiff.cpp +++ b/Engine/Source/Editor/Kismet/Private/SBlueprintDiff.cpp @@ -834,6 +834,11 @@ void SBlueprintDiff::OnCloseAssetEditor(UObject* Asset, EAssetEditorCloseReason // Tell our window to close and set our selves to collapsed to try and stop it from ticking SetVisibility(EVisibility::Collapsed); + if (AssetEditorCloseDelegate.IsValid()) + { + FAssetEditorManager::Get().OnAssetEditorRequestClose().Remove(AssetEditorCloseDelegate); + } + if (WeakParentWindow.IsValid()) { WeakParentWindow.Pin()->RequestDestroyWindow(); diff --git a/Engine/Source/Editor/Kismet/Private/SBlueprintEditorSelectedDebugObjectWidget.cpp b/Engine/Source/Editor/Kismet/Private/SBlueprintEditorSelectedDebugObjectWidget.cpp index c22855f648b3..4a71f42ddcd1 100644 --- a/Engine/Source/Editor/Kismet/Private/SBlueprintEditorSelectedDebugObjectWidget.cpp +++ b/Engine/Source/Editor/Kismet/Private/SBlueprintEditorSelectedDebugObjectWidget.cpp @@ -243,7 +243,14 @@ void SBlueprintEditorSelectedDebugObjectWidget::GenerateDebugWorldNames(bool bRe for (TObjectIterator It; It; ++It) { UWorld *TestWorld = *It; - if (!TestWorld || TestWorld->WorldType != EWorldType::PIE) + + // Include only PIE and worlds that own the persistent level (i.e. non-streaming levels). + const bool bIsValidDebugWorld = (TestWorld != nullptr) + && TestWorld->WorldType == EWorldType::PIE + && TestWorld->PersistentLevel != nullptr + && TestWorld->PersistentLevel->OwningWorld == TestWorld; + + if (!bIsValidDebugWorld) { continue; } diff --git a/Engine/Source/Editor/Kismet/Private/SSCSEditor.cpp b/Engine/Source/Editor/Kismet/Private/SSCSEditor.cpp index c7d73a426f78..2ea2aa1c0269 100644 --- a/Engine/Source/Editor/Kismet/Private/SSCSEditor.cpp +++ b/Engine/Source/Editor/Kismet/Private/SSCSEditor.cpp @@ -3721,7 +3721,6 @@ void SSCSEditor::Construct( const FArguments& InArgs ) ]; } - this->ChildSlot [ Contents.ToSharedRef() @@ -5118,7 +5117,7 @@ UActorComponent* SSCSEditor::AddNewComponent( UClass* NewComponentClass, UObject } // Begin a transaction. The transaction will end when the component name will be provided/confirmed by the user. - check(!OngoingCreateTransaction.IsValid()) + check(!DeferredOngoingCreateTransaction.IsValid()) TUniquePtr AddTransaction = MakeUnique( LOCTEXT("AddComponent", "Add Component") ); UActorComponent* NewComponent = nullptr; @@ -6175,7 +6174,7 @@ void SSCSEditor::OnItemScrolledIntoView( FSCSEditorTreeNodePtrType InItem, const if(DeferredRenameRequest == ItemName) { DeferredRenameRequest = NAME_None; - InItem->OnRequestRename(MoveTemp(OngoingCreateTransaction)); // Transfer responsibility to end the 'create + give initial name' transaction to the tree item if such transaction is ongoing. + InItem->OnRequestRename(MoveTemp(DeferredOngoingCreateTransaction)); // Transfer responsibility to end the 'create + give initial name' transaction to the tree item if such transaction is ongoing. } } } @@ -6200,10 +6199,28 @@ void SSCSEditor::OnRenameComponent(TUniquePtr InComponentCre DeferredRenameRequest = SelectedItems[0]->GetNodeID(); - check(!OngoingCreateTransaction.IsValid()); // If this fails, something in the chain of responsibility failed to end the previous transaction. - OngoingCreateTransaction = MoveTemp(InComponentCreateTransaction); // If a 'create + give initial name' transaction is ongoing, take responsibility of ending it until it's transfered to the selected item. + check(!DeferredOngoingCreateTransaction.IsValid()); // If this fails, something in the chain of responsibility failed to end the previous transaction. + DeferredOngoingCreateTransaction = MoveTemp(InComponentCreateTransaction); // If a 'create + give initial name' transaction is ongoing, take responsibility of ending it until the selected item is scrolled into view. SCSTreeWidget->RequestScrollIntoView(SelectedItems[0]); + + if (DeferredOngoingCreateTransaction.IsValid()) + { + // Ensure the item will be scrolled into view during the frame (See explanation in OnPostTick()). + PostTickHandle = FSlateApplication::Get().OnPostTick().AddSP(this, &SSCSEditor::OnPostTick); + } +} + +void SSCSEditor::OnPostTick(float) +{ + // If a 'create + give initial name' is ongoing and the transaction ownership was not transferred during the frame it was requested, it is most likely because the newly + // created item could not be scrolled into view (should say 'teleported', the scrolling is not animated). The tree view will not put the item in view if the there is + // no space left to display the item. (ex a splitter where all the display space is used by the other component). End the transaction before starting a new frame. The user + // will not be able to rename on creation, the widget is likely not in view and cannot be edited anyway. + DeferredOngoingCreateTransaction.Reset(); + + // The post tick event handler is not required anymore. + FSlateApplication::Get().OnPostTick().Remove(PostTickHandle); } bool SSCSEditor::CanRenameComponent() const diff --git a/Engine/Source/Editor/Kismet/Public/SSCSEditor.h b/Engine/Source/Editor/Kismet/Public/SSCSEditor.h index 44875b0d5376..d693f9ed0677 100644 --- a/Engine/Source/Editor/Kismet/Public/SSCSEditor.h +++ b/Engine/Source/Editor/Kismet/Public/SSCSEditor.h @@ -976,6 +976,9 @@ public: /** Callback for the action trees to get the filter text */ FText GetFilterText() const; + /** Called at the end of each frame. */ + void OnPostTick(float); + protected: FString GetSelectedClassText() const; @@ -1143,7 +1146,10 @@ public: FName DeferredRenameRequest; /** Scope the creation of a component which ends when the initial component 'name' is given/accepted by the user, which can be several frames after the component was actually created. */ - TUniquePtr OngoingCreateTransaction; + TUniquePtr DeferredOngoingCreateTransaction; + + /** Used to unregister from the post tick event. */ + FDelegateHandle PostTickHandle; /** Attribute that provides access to the Actor context for which we are viewing/editing the SCS. */ TAttribute ActorContext; diff --git a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdMode.cpp b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdMode.cpp index a307e35035e2..4d9c0b0419be 100644 --- a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdMode.cpp +++ b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdMode.cpp @@ -561,9 +561,8 @@ void FEdModeLandscape::Enter() if (GetMutableDefault()->bProceduralLandscape) { - ALandscape* Landscape = CurrentToolTarget.LandscapeInfo->LandscapeActor.Get(); - - if (Landscape != nullptr) + ALandscape* Landscape = GetLandscape(); + if (Landscape) { Landscape->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All_Render); } @@ -1055,6 +1054,63 @@ bool FEdModeLandscape::GetCursor(EMouseCursor::Type& OutCursor) const return Result; } +bool FEdModeLandscape::GetOverrideCursorVisibility(bool& bWantsOverride, bool& bHardwareCursorVisible, bool bSoftwareCursorVisible) const +{ + if (!IsEditingEnabled()) + { + return false; + } + + bool Result = false; + if (NewLandscapePreviewMode == ENewLandscapePreviewMode::None) + { + if (CurrentTool) + { + Result = CurrentTool->GetOverrideCursorVisibility(bWantsOverride, bHardwareCursorVisible, bSoftwareCursorVisible); + } + } + + return Result; +} + +bool FEdModeLandscape::PreConvertMouseMovement(FEditorViewportClient* InViewportClient) +{ + if (!IsEditingEnabled()) + { + return false; + } + + bool Result = false; + if (NewLandscapePreviewMode == ENewLandscapePreviewMode::None) + { + if (CurrentTool) + { + Result = CurrentTool->PreConvertMouseMovement(InViewportClient); + } + } + + return Result; +} + +bool FEdModeLandscape::PostConvertMouseMovement(FEditorViewportClient* InViewportClient) +{ + if (!IsEditingEnabled()) + { + return false; + } + + bool Result = false; + if (NewLandscapePreviewMode == ENewLandscapePreviewMode::None) + { + if (CurrentTool) + { + Result = CurrentTool->PostConvertMouseMovement(InViewportClient); + } + } + + return Result; +} + bool FEdModeLandscape::DisallowMouseDeltaTracking() const { // We never want to use the mouse delta tracker while painting @@ -1840,7 +1896,19 @@ bool FEdModeLandscape::InputKey(FEditorViewportClient* ViewportClient, FViewport FVector HitLocation; if (LandscapeMouseTrace(ViewportClient, HitLocation)) { - if (CurrentToolTarget.TargetType == ELandscapeToolTargetType::Weightmap && CurrentToolTarget.LayerInfo == NULL) + const FName CurrentToolName = CurrentTool->GetToolName(); + ALandscape* Landscape = GetLandscape(); + bool bProceduralLandscape = GetMutableDefault()->bProceduralLandscape; + + if (bProceduralLandscape && GetCurrentProceduralLayer() && !GetCurrentProceduralLayer()->bVisible) + { + FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "LandscapeProceduralLayerHidden", "Painting in a hidden layer is not allowed.")); + } + else if (bProceduralLandscape && GetCurrentProceduralLayer() && GetCurrentProceduralLayer()->bLocked) + { + FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "LandscapeProceduralLayerLocked", "This layer is locked. You must unlock it before you can work on this layer.")); + } + else if (CurrentToolTarget.TargetType == ELandscapeToolTargetType::Weightmap && CurrentToolTarget.LayerInfo == NULL && CurrentToolName != TEXT("BPCustom")) { FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "LandscapeNeedToCreateLayerInfo", "This layer has no layer info assigned yet. You must create or assign a layer info before you can paint this layer.")); @@ -3282,6 +3350,7 @@ void FEdModeLandscape::ReimportData(const FLandscapeTargetListInfo& TargetInfo) const FString& SourceFilePath = TargetInfo.ReimportFilePath(); if (SourceFilePath.Len()) { + FScopedSetLandscapeCurrentEditingProceduralLayer Scope(GetLandscape(), GetCurrentProceduralLayerGuid(), [&] { RequestProceduralContentUpdate(); }); ImportData(TargetInfo, SourceFilePath); } else @@ -3386,42 +3455,34 @@ void FEdModeLandscape::ImportData(const FLandscapeTargetListInfo& TargetInfo, co return; } - if (GetMutableDefault()->bProceduralLandscape) { - ChangeHeightmapsToCurrentProceduralLayerHeightmaps(); - } + ALandscape* Landscape = GetLandscape(); + FScopedSetLandscapeCurrentEditingProceduralLayer Scope(Landscape, GetCurrentProceduralLayerGuid(), [&] { check(Landscape); Landscape->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::Heightmap_All); }); - TArray Data; - if (ImportResolution != LandscapeResolution) - { - // Cloned from FLandscapeEditorDetailCustomization_NewLandscape.OnCreateButtonClicked - // so that reimports behave the same as the initial import :) + TArray Data; + if (ImportResolution != LandscapeResolution) + { + // Cloned from FLandscapeEditorDetailCustomization_NewLandscape.OnCreateButtonClicked + // so that reimports behave the same as the initial import :) - const int32 OffsetX = (int32)(LandscapeResolution.Width - ImportResolution.Width) / 2; - const int32 OffsetY = (int32)(LandscapeResolution.Height - ImportResolution.Height) / 2; + const int32 OffsetX = (int32)(LandscapeResolution.Width - ImportResolution.Width) / 2; + const int32 OffsetY = (int32)(LandscapeResolution.Height - ImportResolution.Height) / 2; - Data.SetNumUninitialized(LandscapeResolution.Width * LandscapeResolution.Height * sizeof(uint16)); + Data.SetNumUninitialized(LandscapeResolution.Width * LandscapeResolution.Height * sizeof(uint16)); - LandscapeEditorUtils::ExpandData(Data.GetData(), ImportData.Data.GetData(), - 0, 0, ImportResolution.Width - 1, ImportResolution.Height - 1, - -OffsetX, -OffsetY, LandscapeResolution.Width - OffsetX - 1, LandscapeResolution.Height - OffsetY - 1); - } - else - { - Data = MoveTemp(ImportData.Data); - } + LandscapeEditorUtils::ExpandData(Data.GetData(), ImportData.Data.GetData(), + 0, 0, ImportResolution.Width - 1, ImportResolution.Height - 1, + -OffsetX, -OffsetY, LandscapeResolution.Width - OffsetX - 1, LandscapeResolution.Height - OffsetY - 1); + } + else + { + Data = MoveTemp(ImportData.Data); + } - FScopedTransaction Transaction(LOCTEXT("Undo_ImportHeightmap", "Importing Landscape Heightmap")); + FScopedTransaction Transaction(LOCTEXT("Undo_ImportHeightmap", "Importing Landscape Heightmap")); - FHeightmapAccessor HeightmapAccessor(LandscapeInfo); - HeightmapAccessor.SetData(MinX, MinY, MaxX, MaxY, Data.GetData()); - - if (GetMutableDefault()->bProceduralLandscape) - { - ChangeHeightmapsToCurrentProceduralLayerHeightmaps(true); - - check(CurrentToolTarget.LandscapeInfo->LandscapeActor.IsValid()); - CurrentToolTarget.LandscapeInfo->LandscapeActor.Get()->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::Heightmap_All); + FHeightmapAccessor HeightmapAccessor(LandscapeInfo); + HeightmapAccessor.SetData(MinX, MinY, MaxX, MaxY, Data.GetData()); } } else @@ -3510,30 +3571,35 @@ void FEdModeLandscape::ImportData(const FLandscapeTargetListInfo& TargetInfo, co return; } - TArray Data; - if (ImportResolution != LandscapeResolution) { - // Cloned from FLandscapeEditorDetailCustomization_NewLandscape.OnCreateButtonClicked - // so that reimports behave the same as the initial import :) + ALandscape* Landscape = GetLandscape(); + FScopedSetLandscapeCurrentEditingProceduralLayer Scope(Landscape, GetCurrentProceduralLayerGuid(), [&] { check(Landscape); Landscape->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::Weightmap_All); }); - const int32 OffsetX = (int32)(LandscapeResolution.Width - ImportResolution.Width) / 2; - const int32 OffsetY = (int32)(LandscapeResolution.Height - ImportResolution.Height) / 2; + TArray Data; + if (ImportResolution != LandscapeResolution) + { + // Cloned from FLandscapeEditorDetailCustomization_NewLandscape.OnCreateButtonClicked + // so that reimports behave the same as the initial import :) - Data.SetNumUninitialized(LandscapeResolution.Width * LandscapeResolution.Height * sizeof(uint8)); + const int32 OffsetX = (int32)(LandscapeResolution.Width - ImportResolution.Width) / 2; + const int32 OffsetY = (int32)(LandscapeResolution.Height - ImportResolution.Height) / 2; - LandscapeEditorUtils::ExpandData(Data.GetData(), ImportData.Data.GetData(), - 0, 0, ImportResolution.Width - 1, ImportResolution.Height - 1, - -OffsetX, -OffsetY, LandscapeResolution.Width - OffsetX - 1, LandscapeResolution.Height - OffsetY - 1); + Data.SetNumUninitialized(LandscapeResolution.Width * LandscapeResolution.Height * sizeof(uint8)); + + LandscapeEditorUtils::ExpandData(Data.GetData(), ImportData.Data.GetData(), + 0, 0, ImportResolution.Width - 1, ImportResolution.Height - 1, + -OffsetX, -OffsetY, LandscapeResolution.Width - OffsetX - 1, LandscapeResolution.Height - OffsetY - 1); + } + else + { + Data = MoveTemp(ImportData.Data); + } + + FScopedTransaction Transaction(LOCTEXT("Undo_ImportWeightmap", "Importing Landscape Layer")); + + FAlphamapAccessor AlphamapAccessor(LandscapeInfo, TargetInfo.LayerInfoObj.Get()); + AlphamapAccessor.SetData(MinX, MinY, MaxX, MaxY, Data.GetData(), ELandscapeLayerPaintingRestriction::None); } - else - { - Data = MoveTemp(ImportData.Data); - } - - FScopedTransaction Transaction(LOCTEXT("Undo_ImportWeightmap", "Importing Landscape Layer")); - - FAlphamapAccessor AlphamapAccessor(LandscapeInfo, TargetInfo.LayerInfoObj.Get()); - AlphamapAccessor.SetData(MinX, MinY, MaxX, MaxY, Data.GetData(), ELandscapeLayerPaintingRestriction::None); } } } @@ -3663,12 +3729,14 @@ void FEdModeLandscape::DeleteLandscapeComponents(ULandscapeInfo* LandscapeInfo, HeightmapTexture->ClearFlags(RF_Standalone); // Remove when there is no reference for this Heightmap... } - for (int32 i = 0; i < Component->WeightmapTextures.Num(); ++i) + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(); + + for (UTexture2D* WeightmapTexture : ComponentWeightmapTextures) { - Component->WeightmapTextures[i]->SetFlags(RF_Transactional); - Component->WeightmapTextures[i]->Modify(); - Component->WeightmapTextures[i]->MarkPackageDirty(); - Component->WeightmapTextures[i]->ClearFlags(RF_Standalone); + WeightmapTexture->SetFlags(RF_Transactional); + WeightmapTexture->Modify(); + WeightmapTexture->MarkPackageDirty(); + WeightmapTexture->ClearFlags(RF_Standalone); } if (Component->XYOffsetmapTexture) @@ -3871,6 +3939,22 @@ ALandscape* FEdModeLandscape::ChangeComponentSetting(int32 NumComponentsX, int32 Landscape->bUsedForNavigation = OldLandscapeProxy->bUsedForNavigation; Landscape->MaxPaintedLayersPerComponent = OldLandscapeProxy->MaxPaintedLayersPerComponent; + if (GetMutableDefault()->bProceduralLandscape) + { + ALandscape* OldLandscapeActor = Cast(OldLandscapeProxy); + ALandscape* NewLandscapeActor = Cast(Landscape); + + NewLandscapeActor->PreviousExperimentalLandscapeProcedural = OldLandscapeActor->PreviousExperimentalLandscapeProcedural; + + // Moves specific config including brushes to new landscape actor + for (const FProceduralLayer& Layer : OldLandscapeActor->ProceduralLayers) + { + FProceduralLayer* NewLayer = NewLandscapeActor->ProceduralLayers.FindByPredicate( [&](const FProceduralLayer& NewLayer) { return NewLayer.Name == Layer.Name; } ); + check(NewLayer != nullptr); + *NewLayer = Layer; + } + } + Landscape->CreateLandscapeInfo(); // Clone landscape splines @@ -4040,19 +4124,8 @@ ELandscapeEditingState FEdModeLandscape::GetEditingState() const int32 FEdModeLandscape::GetProceduralLayerCount() const { - if (CurrentToolTarget.LandscapeInfo == nullptr) - { - return 0; - } - - ALandscape* Landscape = CurrentToolTarget.LandscapeInfo->LandscapeActor.Get(); - - if (Landscape == nullptr) - { - return 0; - } - - return Landscape->ProceduralLayers.Num(); + ALandscape* Landscape = GetLandscape(); + return Landscape ? Landscape->ProceduralLayers.Num() : 0; } void FEdModeLandscape::SetCurrentProceduralLayer(int32 InLayerIndex) @@ -4067,129 +4140,103 @@ int32 FEdModeLandscape::GetCurrentProceduralLayerIndex() const return CurrentToolTarget.CurrentProceduralLayerIndex; } -FName FEdModeLandscape::GetProceduralLayerName(int32 InLayerIndex) const +ALandscape* FEdModeLandscape::GetLandscape() const { - if (CurrentToolTarget.LandscapeInfo == nullptr) - { - return NAME_None; - } - - ALandscape* Landscape = CurrentToolTarget.LandscapeInfo->LandscapeActor.Get(); - - if (Landscape == nullptr || !Landscape->ProceduralLayers.IsValidIndex(InLayerIndex)) - { - return NAME_None; - } - - return Landscape->ProceduralLayers[InLayerIndex].Name; + return CurrentToolTarget.LandscapeInfo.IsValid() ? CurrentToolTarget.LandscapeInfo->LandscapeActor.Get() : nullptr; } -FName FEdModeLandscape::GetCurrentProceduralLayerName() const +FProceduralLayer* FEdModeLandscape::GetProceduralLayer(int32 InLayerIndex) const { - return GetProceduralLayerName(CurrentToolTarget.CurrentProceduralLayerIndex); + ALandscape* Landscape = GetLandscape(); + return Landscape ? Landscape->GetProceduralLayer(InLayerIndex) : nullptr; +} + +FName FEdModeLandscape::GetProceduralLayerName(int32 InLayerIndex) const +{ + FProceduralLayer* Layer = GetProceduralLayer(InLayerIndex); + return Layer ? Layer->Name : NAME_None; +} + +bool FEdModeLandscape::CanRenameProceduralLayerTo(int32 InLayerIndex, const FName& InNewName) +{ + ALandscape* Landscape = GetLandscape(); + if (Landscape) + { + int32 LayerCount = GetProceduralLayerCount(); + for (int32 LayerIdx = 0; LayerIdx < LayerCount; ++LayerIdx) + { + if (LayerIdx != InLayerIndex && GetProceduralLayerName(LayerIdx) == InNewName) + { + return false; + } + } + } + return true; } void FEdModeLandscape::SetProceduralLayerName(int32 InLayerIndex, const FName& InName) { - if (CurrentToolTarget.LandscapeInfo == nullptr) + ALandscape* Landscape = GetLandscape(); + if (Landscape) { - return; + Landscape->SetProceduralLayerName(InLayerIndex, InName); } - - ALandscape* Landscape = CurrentToolTarget.LandscapeInfo->LandscapeActor.Get(); - - if (Landscape == nullptr || !Landscape->ProceduralLayers.IsValidIndex(InLayerIndex)) - { - return; - } - - Landscape->ProceduralLayers[InLayerIndex].Name = InName; } -float FEdModeLandscape::GetProceduralLayerWeight(int32 InLayerIndex) const +float FEdModeLandscape::GetProceduralLayerAlpha(int32 InLayerIndex) const { - if (CurrentToolTarget.LandscapeInfo == nullptr) + FProceduralLayer* Layer = GetProceduralLayer(InLayerIndex); + if (Layer) { - return 1.0f; + return (CurrentToolTarget.TargetType == ELandscapeToolTargetType::Heightmap) ? Layer->HeightmapAlpha : Layer->WeightmapAlpha; } - - ALandscape* Landscape = CurrentToolTarget.LandscapeInfo->LandscapeActor.Get(); - - if (Landscape == nullptr || !Landscape->ProceduralLayers.IsValidIndex(InLayerIndex)) - { - return 1.0f; - } - - return Landscape->ProceduralLayers[InLayerIndex].Weight; + return 1.0f; } -void FEdModeLandscape::SetProceduralLayerWeight(float InWeight, int32 InLayerIndex) +void FEdModeLandscape::SetProceduralLayerAlpha(int32 InLayerIndex, float InAlpha) { - if (CurrentToolTarget.LandscapeInfo == nullptr) + ALandscape* Landscape = GetLandscape(); + if (Landscape) { - return; + Landscape->SetProceduralLayerAlpha(InLayerIndex, InAlpha, CurrentToolTarget.TargetType == ELandscapeToolTargetType::Heightmap); } - - ALandscape* Landscape = CurrentToolTarget.LandscapeInfo->LandscapeActor.Get(); - - if (Landscape == nullptr || !Landscape->ProceduralLayers.IsValidIndex(InLayerIndex)) - { - return; - } - - Landscape->ProceduralLayers[InLayerIndex].Weight = InWeight; - - Landscape->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::Heightmap_All); -} - -void FEdModeLandscape::SetProceduralLayerVisibility(bool InVisible, int32 InLayerIndex) -{ - if (CurrentToolTarget.LandscapeInfo == nullptr) - { - return; - } - - ALandscape* Landscape = CurrentToolTarget.LandscapeInfo->LandscapeActor.Get(); - - if (Landscape == nullptr || !Landscape->ProceduralLayers.IsValidIndex(InLayerIndex)) - { - return; - } - - Landscape->ProceduralLayers[InLayerIndex].Visible = InVisible; - - Landscape->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::Heightmap_All); } bool FEdModeLandscape::IsProceduralLayerVisible(int32 InLayerIndex) const { - if (CurrentToolTarget.LandscapeInfo == nullptr) + FProceduralLayer* Layer = GetProceduralLayer(InLayerIndex); + return Layer ? Layer->bVisible : false; +} + +void FEdModeLandscape::SetProceduralLayerVisibility(bool bInVisible, int32 InLayerIndex) +{ + ALandscape* Landscape = GetLandscape(); + if (Landscape) { - return true; + Landscape->SetProceduralLayerVisibility(InLayerIndex, bInVisible); } +} - ALandscape* Landscape = CurrentToolTarget.LandscapeInfo->LandscapeActor.Get(); +bool FEdModeLandscape::IsProceduralLayerLocked(int32 InLayerIndex) const +{ + FProceduralLayer* Layer = GetProceduralLayer(InLayerIndex); + return Layer ? Layer->bLocked : false; +} - if (Landscape == nullptr || !Landscape->ProceduralLayers.IsValidIndex(InLayerIndex)) +void FEdModeLandscape::SetProceduralLayerLocked(int32 InLayerIndex, bool bInLocked) +{ + FProceduralLayer* Layer = GetProceduralLayer(InLayerIndex); + if (Layer) { - return true; + Layer->bLocked = bInLocked; } - - return Landscape->ProceduralLayers[InLayerIndex].Visible; } void FEdModeLandscape::AddBrushToCurrentProceduralLayer(int32 InTargetType, ALandscapeBlueprintCustomBrush* InBrush) { - if (CurrentToolTarget.LandscapeInfo == nullptr || !CurrentToolTarget.LandscapeInfo->LandscapeActor.IsValid()) - { - return; - } - - ALandscape* Landscape = CurrentToolTarget.LandscapeInfo->LandscapeActor.Get(); - + ALandscape* Landscape = GetLandscape(); FProceduralLayer* Layer = GetCurrentProceduralLayer(); - - if (Layer == nullptr) + if (!Landscape || !Layer) { return; } @@ -4207,96 +4254,83 @@ void FEdModeLandscape::AddBrushToCurrentProceduralLayer(int32 InTargetType, ALan InBrush->SetOwningLandscape(Landscape); - Landscape->RequestProceduralContentUpdate(InTargetType == ELandscapeToolTargetType::Type::Heightmap ? EProceduralContentUpdateFlag::Heightmap_All : EProceduralContentUpdateFlag::Weightmap_All); + Landscape->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All_Render); } -void FEdModeLandscape::RequestProceduralContentUpdate() +void FEdModeLandscape::RequestProceduralContentUpdate(bool InUpdateAllMaterials) { - if (CurrentToolTarget.LandscapeInfo == nullptr || !CurrentToolTarget.LandscapeInfo->LandscapeActor.IsValid()) + ALandscape* Landscape = GetLandscape(); + if (Landscape) { - return; + Landscape->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All_Render, InUpdateAllMaterials); } - - ALandscape* Landscape = CurrentToolTarget.LandscapeInfo->LandscapeActor.Get(); - - Landscape->RequestProceduralContentUpdate(CurrentToolTarget.TargetType == ELandscapeToolTargetType::Type::Heightmap ? EProceduralContentUpdateFlag::Heightmap_All : EProceduralContentUpdateFlag::Weightmap_All); } void FEdModeLandscape::RemoveBrushFromCurrentProceduralLayer(int32 InTargetType, ALandscapeBlueprintCustomBrush* InBrush) { - if (CurrentToolTarget.LandscapeInfo == nullptr || !CurrentToolTarget.LandscapeInfo->LandscapeActor.IsValid()) + ALandscape* Landscape = GetLandscape(); + if (!Landscape) { return; } - - ALandscape* Landscape = CurrentToolTarget.LandscapeInfo->LandscapeActor.Get(); - - FProceduralLayer* Layer = GetCurrentProceduralLayer(); - - if (Layer == nullptr) + for (FProceduralLayer& Layer : Landscape->ProceduralLayers) { - return; - } - - int32 IndexToRemove = INDEX_NONE; - for (int32 i = 0; i < Layer->Brushes.Num(); ++i) - { - if (Layer->Brushes[i].BPCustomBrush == InBrush) + int32 IndexToRemove = INDEX_NONE; + for (int32 i = 0; i < Layer.Brushes.Num(); ++i) { - IndexToRemove = i; + if (Layer.Brushes[i].BPCustomBrush == InBrush) + { + IndexToRemove = i; + break; + } + } + + if (IndexToRemove != INDEX_NONE) + { + Layer.Brushes.RemoveAt(IndexToRemove); + + for (int32 i = 0; i < Layer.HeightmapBrushOrderIndices.Num(); ++i) + { + if (Layer.HeightmapBrushOrderIndices[i] == IndexToRemove) + { + // Update the value of the index of all the one after the one we removed, so index still correctly match actual brushes list + for (int32 j = 0; j < Layer.HeightmapBrushOrderIndices.Num(); ++j) + { + if (Layer.HeightmapBrushOrderIndices[j] > IndexToRemove) + { + --Layer.HeightmapBrushOrderIndices[j]; + } + } + + Layer.HeightmapBrushOrderIndices.RemoveAt(i); + break; + } + } + + for (int32 i = 0; i < Layer.WeightmapBrushOrderIndices.Num(); ++i) + { + if (Layer.WeightmapBrushOrderIndices[i] == IndexToRemove) + { + // Update the value of the index of all the one after the one we removed, so index still correctly match actual brushes list + for (int32 j = 0; j < Layer.WeightmapBrushOrderIndices.Num(); ++j) + { + if (Layer.WeightmapBrushOrderIndices[j] > IndexToRemove) + { + --Layer.WeightmapBrushOrderIndices[j]; + } + } + + Layer.WeightmapBrushOrderIndices.RemoveAt(i); + break; + } + } + + InBrush->SetOwningLandscape(nullptr); break; } } - if (IndexToRemove != INDEX_NONE) - { - Layer->Brushes.RemoveAt(IndexToRemove); - - if (InTargetType == ELandscapeToolTargetType::Type::Heightmap) - { - for (int32 i = 0; i < Layer->HeightmapBrushOrderIndices.Num(); ++i) - { - if (Layer->HeightmapBrushOrderIndices[i] == IndexToRemove) - { - // Update the value of the index of all the one after the one we removed, so index still correctly match actual brushes list - for (int32 j = 0; j < Layer->HeightmapBrushOrderIndices.Num(); ++j) - { - if (Layer->HeightmapBrushOrderIndices[j] > IndexToRemove) - { - --Layer->HeightmapBrushOrderIndices[j]; - } - } - - Layer->HeightmapBrushOrderIndices.RemoveAt(i); - break; - } - } - } - else - { - for (int32 i = 0; i < Layer->WeightmapBrushOrderIndices.Num(); ++i) - { - if (Layer->WeightmapBrushOrderIndices[i] == IndexToRemove) - { - // Update the value of the index of all the one after the one we removed, so index still correctly match actual brushes list - for (int32 j = 0; j < Layer->WeightmapBrushOrderIndices.Num(); ++j) - { - if (Layer->WeightmapBrushOrderIndices[j] > IndexToRemove) - { - --Layer->HeightmapBrushOrderIndices[j]; - } - } - - Layer->WeightmapBrushOrderIndices.RemoveAt(i); - break; - } - } - } - - InBrush->SetOwningLandscape(nullptr); - } - - Landscape->RequestProceduralContentUpdate(InTargetType == ELandscapeToolTargetType::Type::Heightmap ? EProceduralContentUpdateFlag::Heightmap_All : EProceduralContentUpdateFlag::Weightmap_All); + Landscape->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All_Render); } bool FEdModeLandscape::AreAllBrushesCommitedToCurrentProceduralLayer(int32 InTargetType) @@ -4310,7 +4344,7 @@ bool FEdModeLandscape::AreAllBrushesCommitedToCurrentProceduralLayer(int32 InTar for (FLandscapeProceduralLayerBrush& Brush : Layer->Brushes) { - if (!Brush.BPCustomBrush->IsCommited() + if (Brush.BPCustomBrush != nullptr && !Brush.BPCustomBrush->IsCommited() && ((InTargetType == ELandscapeToolTargetType::Type::Heightmap && Brush.BPCustomBrush->IsAffectingHeightmap()) || (InTargetType == ELandscapeToolTargetType::Type::Weightmap && Brush.BPCustomBrush->IsAffectingWeightmap()))) { return false; @@ -4331,7 +4365,10 @@ void FEdModeLandscape::SetCurrentProceduralLayerBrushesCommitState(int32 InTarge for (FLandscapeProceduralLayerBrush& Brush : Layer->Brushes) { - Brush.BPCustomBrush->SetCommitState(InCommited); + if (Brush.BPCustomBrush != nullptr) + { + Brush.BPCustomBrush->SetCommitState(InCommited); + } } GEngine->BroadcastLevelActorListChanged(); @@ -4402,8 +4439,8 @@ TArray FEdModeLandscape::GetBrushesForCurrentPr for (const FLandscapeProceduralLayerBrush& Brush : Layer->Brushes) { - if ((Brush.BPCustomBrush->IsAffectingHeightmap() && InTargetType == ELandscapeToolTargetType::Type::Heightmap) - || (Brush.BPCustomBrush->IsAffectingWeightmap() && InTargetType == ELandscapeToolTargetType::Type::Weightmap)) + if (Brush.BPCustomBrush != nullptr && ((Brush.BPCustomBrush->IsAffectingHeightmap() && InTargetType == ELandscapeToolTargetType::Type::Heightmap) + || (Brush.BPCustomBrush->IsAffectingWeightmap() && InTargetType == ELandscapeToolTargetType::Type::Weightmap))) { Brushes.Add(Brush.BPCustomBrush); } @@ -4412,43 +4449,64 @@ TArray FEdModeLandscape::GetBrushesForCurrentPr return Brushes; } -FProceduralLayer* FEdModeLandscape::GetCurrentProceduralLayer() const +bool FEdModeLandscape::IsCurrentProceduralLayerBlendSubstractive(const TWeakObjectPtr& InLayerInfoObj) const { - if (!CurrentToolTarget.LandscapeInfo.IsValid()) + FProceduralLayer* Layer = GetCurrentProceduralLayer(); + + if (Layer == nullptr) { - return nullptr; + return false; } - ALandscape* Landscape = CurrentToolTarget.LandscapeInfo->LandscapeActor.Get(); + bool* AllocationBlend = Layer->WeightmapLayerAllocationBlend.Find(InLayerInfoObj.Get()); - if (Landscape == nullptr) + if (AllocationBlend != nullptr) { - return nullptr; + return (*AllocationBlend); } - FName CurrentLayerName = GetCurrentProceduralLayerName(); - - if (CurrentLayerName == NAME_None) - { - return nullptr; - } - - for (FProceduralLayer& Layer : Landscape->ProceduralLayers) - { - if (Layer.Name == CurrentLayerName) - { - return &Layer; - } - } - - return nullptr; + return false; } -void FEdModeLandscape::ChangeHeightmapsToCurrentProceduralLayerHeightmaps(bool InResetCurrentEditingHeightmap) +void FEdModeLandscape::SetCurrentProceduralLayerSubstractiveBlendStatus(bool InStatus, const TWeakObjectPtr& InLayerInfoObj) +{ + FProceduralLayer* Layer = GetCurrentProceduralLayer(); + + if (Layer == nullptr) + { + return; + } + + bool* AllocationBlend = Layer->WeightmapLayerAllocationBlend.Find(InLayerInfoObj.Get()); + + if (AllocationBlend == nullptr) + { + Layer->WeightmapLayerAllocationBlend.Add(InLayerInfoObj.Get(), InStatus); + } + else + { + *AllocationBlend = InStatus; + } + + RequestProceduralContentUpdate(true); +} + +FProceduralLayer* FEdModeLandscape::GetCurrentProceduralLayer() const +{ + return GetProceduralLayer(GetCurrentProceduralLayerIndex()); +} + +FGuid FEdModeLandscape::GetCurrentProceduralLayerGuid() const +{ + FProceduralLayer* CurrentProceduralLayer = GetCurrentProceduralLayer(); + return CurrentProceduralLayer ? CurrentProceduralLayer->Guid : FGuid(); +} + +bool FEdModeLandscape::NeedToFillEmptyLayersForProcedural() const { if (!CurrentToolTarget.LandscapeInfo.IsValid() || !CurrentToolTarget.LandscapeInfo->LandscapeActor.IsValid()) { - return; + return false; } TArray AllLandscapes; @@ -4459,41 +4517,28 @@ void FEdModeLandscape::ChangeHeightmapsToCurrentProceduralLayerHeightmaps(bool I AllLandscapes.Add(It); } - FName CurrentLayerName = GetCurrentProceduralLayerName(); - - if (CurrentLayerName == NAME_None) + for (const ALandscapeProxy* LandscapeProxy : AllLandscapes) { - return; - } - - for (ALandscapeProxy* LandscapeProxy : AllLandscapes) - { - FProceduralLayerData* CurrentLayerData = LandscapeProxy->ProceduralLayersData.Find(CurrentLayerName); - - if (CurrentLayerData == nullptr) + for (const auto& ItLayerPair : LandscapeProxy->ProceduralLayersData) { - continue; - } + const FProceduralLayerData& ProceduralLayerData = ItLayerPair.Value; - for (ULandscapeComponent* Component : LandscapeProxy->LandscapeComponents) - { - if (InResetCurrentEditingHeightmap) + for (const auto& ItWeightmapPair : ProceduralLayerData.WeightmapData) { - Component->SetCurrentEditingHeightmap(nullptr); - } - else - { - UTexture2D** LayerHeightmap = CurrentLayerData->Heightmaps.Find(Component->GetHeightmap()); + const FWeightmapLayerData& WeightmapData = ItWeightmapPair.Value; - if (LayerHeightmap != nullptr) + for (const FWeightmapLayerAllocationInfo& Alloc : WeightmapData.WeightmapLayerAllocations) { - Component->SetCurrentEditingHeightmap(*LayerHeightmap); + if (Alloc.LayerInfo != nullptr) + { + return false; + } } } - - Component->MarkRenderStateDirty(); } } + + return true; } void FEdModeLandscape::OnLevelActorAdded(AActor* InActor) diff --git a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdMode.h b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdMode.h index c0fc96bf8666..33f5920e12ca 100644 --- a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdMode.h +++ b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdMode.h @@ -98,11 +98,12 @@ struct FLandscapeTargetListInfo { } - FLandscapeInfoLayerSettings* GetLandscapeInfoLayerSettings() const + int32 GetLandscapeInfoLayerIndex() const { + int32 Index = INDEX_NONE; + if (TargetType == ELandscapeToolTargetType::Weightmap) { - int32 Index = INDEX_NONE; if (LayerInfoObj.IsValid()) { Index = LandscapeInfo->GetLayerInfoIndex(LayerInfoObj.Get(), Owner.Get()); @@ -111,11 +112,20 @@ struct FLandscapeTargetListInfo { Index = LandscapeInfo->GetLayerInfoIndex(LayerName, Owner.Get()); } - if (ensure(Index != INDEX_NONE)) - { - return &LandscapeInfo->Layers[Index]; - } } + + return Index; + } + + FLandscapeInfoLayerSettings* GetLandscapeInfoLayerSettings() const + { + int32 Index = GetLandscapeInfoLayerIndex(); + + if (Index != INDEX_NONE) + { + return &LandscapeInfo->Layers[Index]; + } + return NULL; } @@ -418,6 +428,15 @@ public: virtual bool GetCursor(EMouseCursor::Type& OutCursor) const override; + /** Get override cursor visibility settings */ + virtual bool GetOverrideCursorVisibility(bool& bWantsOverride, bool& bHardwareCursorVisible, bool bSoftwareCursorVisible) const override; + + /** Called before mouse movement is converted to drag/rot */ + virtual bool PreConvertMouseMovement(FEditorViewportClient* InViewportClient) override; + + /** Called after mouse movement is converted to drag/rot */ + virtual bool PostConvertMouseMovement(FEditorViewportClient* InViewportClient) override; + /** Forces real-time perspective viewports */ void ForceRealTimeViewports(const bool bEnable, const bool bStoreCurrentState); @@ -474,13 +493,22 @@ public: int32 GetProceduralLayerCount() const; void SetCurrentProceduralLayer(int32 InLayerIndex); int32 GetCurrentProceduralLayerIndex() const; - FName GetCurrentProceduralLayerName() const; + ALandscape* GetLandscape() const; + struct FProceduralLayer* GetProceduralLayer(int32 InLayerIndex) const; FName GetProceduralLayerName(int32 InLayerIndex) const; void SetProceduralLayerName(int32 InLayerIndex, const FName& InName); - float GetProceduralLayerWeight(int32 InLayerIndex) const; - void SetProceduralLayerWeight(float InWeight, int32 InLayerIndex); + bool CanRenameProceduralLayerTo(int32 InLayerIndex, const FName& InNewName); + float GetProceduralLayerAlpha(int32 InLayerIndex) const; + void SetProceduralLayerAlpha(int32 InLayerIndex, float InAlpha); void SetProceduralLayerVisibility(bool InVisible, int32 InLayerIndex); bool IsProceduralLayerVisible(int32 InLayerIndex) const; + bool IsProceduralLayerLocked(int32 InLayerIndex) const; + void SetProceduralLayerLocked(int32 InLayerIndex, bool bInLocked); + struct FProceduralLayer* GetCurrentProceduralLayer() const; + FGuid GetCurrentProceduralLayerGuid() const; + bool IsCurrentProceduralLayerBlendSubstractive(const TWeakObjectPtr& InLayerInfoObj) const; + void SetCurrentProceduralLayerSubstractiveBlendStatus(bool InStatus, const TWeakObjectPtr& InLayerInfoObj); + void AddBrushToCurrentProceduralLayer(int32 InTargetType, class ALandscapeBlueprintCustomBrush* InBrush); void RemoveBrushFromCurrentProceduralLayer(int32 InTargetType, class ALandscapeBlueprintCustomBrush* InBrush); bool AreAllBrushesCommitedToCurrentProceduralLayer(int32 InTargetType); @@ -488,9 +516,9 @@ public: TArray& GetBrushesOrderForCurrentProceduralLayer(int32 InTargetType) const; class ALandscapeBlueprintCustomBrush* GetBrushForCurrentProceduralLayer(int32 InTargetType, int8 BrushIndex) const; TArray GetBrushesForCurrentProceduralLayer(int32 InTargetType); - struct FProceduralLayer* GetCurrentProceduralLayer() const; - void ChangeHeightmapsToCurrentProceduralLayerHeightmaps(bool InResetCurrentEditingHeightmap = false); - void RequestProceduralContentUpdate(); + + bool NeedToFillEmptyLayersForProcedural() const; + void RequestProceduralContentUpdate(bool InUpdateAllMaterials = false); void OnLevelActorAdded(AActor* InActor); void OnLevelActorRemoved(AActor* InActor); diff --git a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModeBrushes.cpp b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModeBrushes.cpp index 21bcef80044a..10a5b39f3893 100644 --- a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModeBrushes.cpp +++ b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModeBrushes.cpp @@ -212,12 +212,14 @@ public: } else { - bool bExisting = Component->WeightmapLayerAllocations.ContainsByPredicate([LayerInfo](const FWeightmapLayerAllocationInfo& Allocation) { return Allocation.LayerInfo == LayerInfo; }); + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(true); + + bool bExisting = ComponentWeightmapLayerAllocations.ContainsByPredicate([LayerInfo](const FWeightmapLayerAllocationInfo& Allocation) { return Allocation.LayerInfo == LayerInfo; }); if (!bExisting) { if (EdMode->UISettings->PaintingRestriction == ELandscapeLayerPaintingRestriction::ExistingOnly || (EdMode->UISettings->PaintingRestriction == ELandscapeLayerPaintingRestriction::UseMaxLayers && - LandscapeProxy->MaxPaintedLayersPerComponent > 0 && Component->WeightmapLayerAllocations.Num() >= LandscapeProxy->MaxPaintedLayersPerComponent)) + LandscapeProxy->MaxPaintedLayersPerComponent > 0 && ComponentWeightmapLayerAllocations.Num() >= LandscapeProxy->MaxPaintedLayersPerComponent)) { bCanPaint = false; } diff --git a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModeComponentTools.cpp b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModeComponentTools.cpp index 871e8af14e6b..d1b5a4dfbedb 100644 --- a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModeComponentTools.cpp +++ b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModeComponentTools.cpp @@ -342,9 +342,48 @@ public: return false; } + if (GetMutableDefault()->bProceduralLandscape) + { + ALandscape* Landscape = this->EdMode->GetLandscape(); + if (Landscape) + { + Landscape->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::Weightmap_Render); + Landscape->SetCurrentEditingProceduralLayer(this->EdMode->GetCurrentProceduralLayerGuid()); + } + } + return FLandscapeToolBase::BeginTool(ViewportClient, InTarget, InHitLocation); } + virtual void Tick(FEditorViewportClient* ViewportClient, float DeltaTime) override + { + FLandscapeToolBase::Tick(ViewportClient, DeltaTime); + + if (GetMutableDefault()->bProceduralLandscape && this->IsToolActive()) + { + ALandscape* Landscape = this->EdMode->GetLandscape(); + if (Landscape) + { + Landscape->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::Weightmap_Render); + } + } + } + + virtual void EndTool(FEditorViewportClient* ViewportClient) override + { + if (GetMutableDefault()->bProceduralLandscape) + { + ALandscape* Landscape = this->EdMode->GetLandscape(); + if (Landscape) + { + Landscape->SetCurrentEditingProceduralLayer(); + Landscape->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::Weightmap_All); + } + } + + FLandscapeToolBase::EndTool(ViewportClient); + } + virtual const TCHAR* GetToolName() override { return TEXT("Visibility"); } virtual FText GetDisplayName() override { return NSLOCTEXT("UnrealEd", "LandscapeMode_Visibility", "Visibility"); }; @@ -596,7 +635,9 @@ public: FLandscapeEditDataInterface LandscapeEdit(LandscapeInfo); for (ULandscapeComponent* Component : TargetSelectedComponents) { - int32 TotalNeededChannels = Component->WeightmapLayerAllocations.Num(); + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(); + + int32 TotalNeededChannels = ComponentWeightmapLayerAllocations.Num(); int32 CurrentLayer = 0; TArray NewWeightmapTextures; @@ -607,7 +648,7 @@ public: // UE_LOG(LogLandscape, Log, TEXT("Still need %d channels"), TotalNeededChannels); UTexture2D* CurrentWeightmapTexture = nullptr; - FLandscapeWeightmapUsage* CurrentWeightmapUsage = nullptr; + ULandscapeWeightmapUsage* CurrentWeightmapUsage = nullptr; if (TotalNeededChannels < 4) { @@ -617,8 +658,8 @@ public: int32 BestDistanceSquared = MAX_int32; for (auto& WeightmapUsagePair : LandscapeProxy->WeightmapUsageMap) { - FLandscapeWeightmapUsage* TryWeightmapUsage = &WeightmapUsagePair.Value; - if (TryWeightmapUsage->FreeChannelCount() >= TotalNeededChannels) + ULandscapeWeightmapUsage* TryWeightmapUsage = WeightmapUsagePair.Value; + if (TryWeightmapUsage->FreeChannelCount() >= TotalNeededChannels) // TODO: handle procedural layer { // See if this candidate is closer than any others we've found for (int32 ChanIdx = 0; ChanIdx < 4; ChanIdx++) @@ -654,7 +695,7 @@ public: CurrentWeightmapTexture->PostEditChange(); // Store it in the usage map - CurrentWeightmapUsage = &LandscapeProxy->WeightmapUsageMap.Add(CurrentWeightmapTexture, FLandscapeWeightmapUsage()); + CurrentWeightmapUsage = LandscapeProxy->WeightmapUsageMap.Add(CurrentWeightmapTexture, NewObject(LandscapeProxy)); // UE_LOG(LogLandscape, Log, TEXT("Making a new texture %s"), *CurrentWeightmapTexture->GetName()); } @@ -668,7 +709,7 @@ public: if (CurrentWeightmapUsage->ChannelUsage[ChanIdx] == nullptr) { // Use this allocation - FWeightmapLayerAllocationInfo& AllocInfo = Component->WeightmapLayerAllocations[CurrentLayer]; + FWeightmapLayerAllocationInfo& AllocInfo = ComponentWeightmapLayerAllocations[CurrentLayer]; if (AllocInfo.WeightmapTextureIndex == 255) { @@ -677,15 +718,20 @@ public: } else { - UTexture2D* OldWeightmapTexture = Component->WeightmapTextures[AllocInfo.WeightmapTextureIndex]; + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(); + UTexture2D* OldWeightmapTexture = ComponentWeightmapTextures[AllocInfo.WeightmapTextureIndex]; // Copy the data LandscapeEdit.CopyTextureChannel(CurrentWeightmapTexture, ChanIdx, OldWeightmapTexture, AllocInfo.WeightmapTextureChannel); LandscapeEdit.ZeroTextureChannel(OldWeightmapTexture, AllocInfo.WeightmapTextureChannel); // Remove the old allocation - FLandscapeWeightmapUsage* OldWeightmapUsage = Component->GetLandscapeProxy()->WeightmapUsageMap.Find(OldWeightmapTexture); - OldWeightmapUsage->ChannelUsage[AllocInfo.WeightmapTextureChannel] = nullptr; + ULandscapeWeightmapUsage** OldWeightmapUsage = Component->GetLandscapeProxy()->WeightmapUsageMap.Find(OldWeightmapTexture); + + if (OldWeightmapUsage != nullptr) + { + (*OldWeightmapUsage)->ChannelUsage[AllocInfo.WeightmapTextureChannel] = nullptr; + } } // Assign the new allocation @@ -699,10 +745,10 @@ public: } // Replace the weightmap textures - Component->WeightmapTextures = NewWeightmapTextures; + Component->SetWeightmapTextures(NewWeightmapTextures); // Update the mipmaps for the textures we edited - for (UTexture2D* WeightmapTexture : Component->WeightmapTextures) + for (UTexture2D* WeightmapTexture : NewWeightmapTextures) { FLandscapeTextureDataInfo* WeightmapDataInfo = LandscapeEdit.GetTextureDataInfo(WeightmapTexture); diff --git a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModePaintTools.cpp b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModePaintTools.cpp index 4717eb491748..3fb9bdfc0cb9 100644 --- a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModePaintTools.cpp +++ b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModePaintTools.cpp @@ -77,13 +77,13 @@ public: { if (GetMutableDefault()->bProceduralLandscape) { - ALandscape* Landscape = this->EdMode->CurrentToolTarget.LandscapeInfo->LandscapeActor.Get(); - if (Landscape != nullptr) + ALandscape* Landscape = this->EdMode->GetLandscape(); + if (Landscape) { - Landscape->RequestProceduralContentUpdate(this->EdMode->CurrentToolTarget.TargetType == ELandscapeToolTargetType::Type::Heightmap ? EProceduralContentUpdateFlag::Heightmap_Render : EProceduralContentUpdateFlag::Weightmap_Render); + bool bUpdateHeightmap = this->EdMode->CurrentToolTarget.TargetType == ELandscapeToolTargetType::Type::Heightmap; + Landscape->RequestProceduralContentUpdate(bUpdateHeightmap ? EProceduralContentUpdateFlag::Heightmap_Render : EProceduralContentUpdateFlag::Weightmap_Render); + Landscape->SetCurrentEditingProceduralLayer(this->EdMode->GetCurrentProceduralLayerGuid()); } - - this->EdMode->ChangeHeightmapsToCurrentProceduralLayerHeightmaps(false); } return FLandscapeToolBase::BeginTool(ViewportClient, InTarget, InHitLocation); @@ -93,22 +93,12 @@ public: { if (GetMutableDefault()->bProceduralLandscape) { - if (this->EdMode->CurrentToolTarget.TargetType == ELandscapeToolTargetType::Type::Heightmap) + ALandscape* Landscape = this->EdMode->GetLandscape(); + if (Landscape) { - this->EdMode->ChangeHeightmapsToCurrentProceduralLayerHeightmaps(true); - - if (this->EdMode->CurrentToolTarget.LandscapeInfo->LandscapeActor.IsValid()) - { - this->EdMode->CurrentToolTarget.LandscapeInfo->LandscapeActor->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::Heightmap_All); - } - } - else - { - // TODO: Activate/Deactivate weightmap layers - if (this->EdMode->CurrentToolTarget.LandscapeInfo->LandscapeActor.IsValid()) - { - this->EdMode->CurrentToolTarget.LandscapeInfo->LandscapeActor->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::Weightmap_All); - } + bool bUpdateHeightmap = this->EdMode->CurrentToolTarget.TargetType == ELandscapeToolTargetType::Type::Heightmap; + Landscape->SetCurrentEditingProceduralLayer(); + Landscape->RequestProceduralContentUpdate(bUpdateHeightmap ? EProceduralContentUpdateFlag::Heightmap_All : EProceduralContentUpdateFlag::Weightmap_All); } } @@ -822,15 +812,15 @@ public: float ScaleZ = LocalToWorld.GetScale3D().Z; float TranslateZ = LocalToWorld.GetTranslation().Z; float TerraceInterval = UISettings->TerraceInterval; - float Smoothness = UISettings->TerraceSmooth; + float Smoothness = UISettings->TerraceSmooth; float WorldHeight = LandscapeDataAccess::GetLocalHeight(DataScanline[X]); - + //move into world space WorldHeight = (WorldHeight * ScaleZ) + TranslateZ; float CurrentHeight = WorldHeight; //smoothing part - float CurrentLevel = WorldHeight / TerraceInterval; + float CurrentLevel = WorldHeight / TerraceInterval; Smoothness = 1.0f / FMath::Max(Smoothness, 0.0001f); float CurrentPhase = FMath::Frac(CurrentLevel); float Halfmask = FMath::Clamp(FMath::CeilToFloat(CurrentPhase - 0.5f), 0.0f, 1.0f); @@ -838,12 +828,34 @@ public: float SCurve = FMath::Lerp(CurrentPhase, (1.0f - CurrentPhase), Halfmask) * 2.0f; SCurve = FMath::Pow(SCurve, Smoothness) * 0.5f; SCurve = FMath::Lerp(SCurve, 1.0f - SCurve, Halfmask) * TerraceInterval; - WorldHeight = (CurrentLevel * TerraceInterval) + SCurve; + WorldHeight = (CurrentLevel * TerraceInterval) + SCurve; //end of smoothing part - float FinalHeight = FMath::Lerp(CurrentHeight, WorldHeight , Strength); + float FinalHeight = FMath::Lerp(CurrentHeight, WorldHeight, Strength); FinalHeight = (FinalHeight - TranslateZ) / ScaleZ; - DataScanline[X] = LandscapeDataAccess::GetTexHeight(FinalHeight); + DataScanline[X] = LandscapeDataAccess::GetTexHeight(FinalHeight); + } + break; + case ELandscapeToolFlattenMode::Interval: + { + const FTransform& LocalToWorld = this->Target.LandscapeInfo->GetLandscapeProxy()->ActorToWorld(); + float ScaleZ = LocalToWorld.GetScale3D().Z; + float TranslateZ = LocalToWorld.GetTranslation().Z; + float TerraceInterval = UISettings->TerraceInterval; + float TargetHeight = LandscapeDataAccess::GetLocalHeight(FlattenHeight); + float CurrentHeight = LandscapeDataAccess::GetLocalHeight(DataScanline[X]); + + //move into world space + TargetHeight = (TargetHeight * ScaleZ) + TranslateZ; + CurrentHeight = (CurrentHeight * ScaleZ) + TranslateZ; + + TargetHeight = (FMath::RoundToFloat(TargetHeight / TerraceInterval)) * TerraceInterval; + TargetHeight = FMath::Lerp(CurrentHeight, TargetHeight, BrushValue); + + //back to local space of landscape object + TargetHeight = (TargetHeight - TranslateZ) / ScaleZ; + DataScanline[X] = LandscapeDataAccess::GetTexHeight(TargetHeight); + } break; case ELandscapeToolFlattenMode::Raise: diff --git a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModeSplineTools.cpp b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModeSplineTools.cpp index dc404144bf00..4e4b584c64ec 100644 --- a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModeSplineTools.cpp +++ b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEdModeSplineTools.cpp @@ -49,6 +49,8 @@ public: , SelectedSplineControlPoints() , SelectedSplineSegments() , DraggingTangent_Segment(NULL) + , DraggingTangent_Length(0.0f) + , DraggingTangent_CacheCoordSpace(ECoordSystem::COORD_None) , DraggingTangent_End(false) , bMovingControlPoint(false) , bAutoRotateOnJoin(true) @@ -1442,6 +1444,12 @@ public: HLandscapeSplineProxy_Tangent* SplineProxy = (HLandscapeSplineProxy_Tangent*)HitProxy; DraggingTangent_Segment = SplineProxy->SplineSegment; DraggingTangent_End = SplineProxy->End; + DraggingTangent_Length = DraggingTangent_Segment->Connections[DraggingTangent_End].TangentLen; + + // Coord system MUST be set here, even if widget coord system space claims to already be in local space. + DraggingTangent_CacheCoordSpace = InViewportClient->GetWidgetCoordSystemSpace(); + InViewportClient->SetWidgetCoordSystemSpace(ECoordSystem::COORD_Local); + InViewportClient->SetRequiredCursorOverride(true, EMouseCursor::GrabHandClosed); GEditor->BeginTransaction(LOCTEXT("LandscapeSpline_ModifyTangent", "Modify Landscape Spline Tangent")); ULandscapeSplinesComponent* SplinesComponent = DraggingTangent_Segment->GetOuterULandscapeSplinesComponent(); @@ -1480,6 +1488,9 @@ public: DraggingTangent_Segment = NULL; + InViewportClient->SetWidgetCoordSystemSpace(DraggingTangent_CacheCoordSpace); + InViewportClient->SetRequiredCursorOverride(false); + GEditor->EndTransaction(); return false; // false to let FEditorViewportClient.InputKey end mouse tracking @@ -1593,23 +1604,69 @@ public: return UpdateSplitSegment(SelectedControlPoint, Location); } + virtual bool GetOverrideCursorVisibility(bool& bWantsOverride, bool& bHardwareCursorVisible, bool bSoftwareCursorVisible) const override + { + if (DraggingTangent_Segment) + { + bWantsOverride = true; + bHardwareCursorVisible = true; + bSoftwareCursorVisible = false; + return true; + } + + bWantsOverride = false; + + return false; + } + + virtual bool PreConvertMouseMovement(FEditorViewportClient* InViewportClient) override + { + if (DraggingTangent_Segment) + { + InViewportClient->SetWidgetModeOverride(FWidget::WM_Translate); + InViewportClient->SetCurrentWidgetAxis(EAxisList::X); + return true; + } + + return false; + } + + virtual bool PostConvertMouseMovement(FEditorViewportClient* InViewportClient) override + { + if (DraggingTangent_Segment) + { + InViewportClient->SetWidgetModeOverride(FWidget::WM_Scale); + InViewportClient->SetCurrentWidgetAxis(EAxisList::None); + return true; + } + + return false; + } + virtual bool InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale) override { FVector Drag = InDrag; if (DraggingTangent_Segment) { + InViewportClient->SetRequiredCursorOverride(true, EMouseCursor::GrabHandClosed); + const ULandscapeSplinesComponent* SplinesComponent = DraggingTangent_Segment->GetOuterULandscapeSplinesComponent(); FLandscapeSplineSegmentConnection& Connection = DraggingTangent_Segment->Connections[DraggingTangent_End]; FVector StartLocation; FRotator StartRotation; Connection.ControlPoint->GetConnectionLocationAndRotation(Connection.SocketName, StartLocation, StartRotation); + FVector ForwardVector = FQuatRotationMatrix(StartRotation.Quaternion()).TransformVector(FVector(1.0f, 0.0f, 0.0f)); + FVector DragLocal = SplinesComponent->GetComponentTransform().InverseTransformVector(Drag); + float Angle = FMath::Acos(FVector::DotProduct(DragLocal, ForwardVector) / DragLocal.Size()); float OldTangentLen = Connection.TangentLen; - Connection.TangentLen += SplinesComponent->GetComponentTransform().InverseTransformVector(Drag) | StartRotation.Vector(); + Connection.TangentLen = DraggingTangent_Length + (Angle < HALF_PI ? 2.0 : -2.0) * DragLocal.Size(); - // Disallow a tangent of exactly 0 - if (Connection.TangentLen == 0) + // Disallow a tangent of exactly 0 and don't allow tangents to flip + if ((Connection.TangentLen > 0 && OldTangentLen < 0) || + (Connection.TangentLen < 0 && OldTangentLen > 0) || + Connection.TangentLen == 0) { if (OldTangentLen > 0) { @@ -1790,7 +1847,7 @@ public: FVector HandlePos1 = SplinesComponent->GetComponentTransform().TransformPosition(ControlPoint->Location + ControlPoint->Rotation.Vector() * 20); DrawDashedLine(PDI, HandlePos0, HandlePos1, FColor::White, 20, SDPG_Foreground); - if (GLevelEditorModeTools().GetWidgetMode() == FWidget::WM_Scale) + if (GLevelEditorModeTools().GetWidgetMode() == FWidget::WM_Scale && !Viewport->GetClient()->IsOrtho()) { for (const FLandscapeSplineConnection& Connection : ControlPoint->ConnectedSegments) { @@ -1799,16 +1856,17 @@ public: FVector StartPos = SplinesComponent->GetComponentTransform().TransformPosition(StartLocation); FVector HandlePos = SplinesComponent->GetComponentTransform().TransformPosition(StartLocation + StartRotation.Vector() * Connection.GetNearConnection().TangentLen / 2); - PDI->DrawLine(StartPos, HandlePos, FColor::White, SDPG_Foreground); + FColor TangentColor = (Connection.Segment == DraggingTangent_Segment && Connection.End == DraggingTangent_End) ? FColor::Yellow : FColor::White; + PDI->DrawLine(StartPos, HandlePos, TangentColor, SDPG_Foreground); if (PDI->IsHitTesting()) PDI->SetHitProxy(new HLandscapeSplineProxy_Tangent(Connection.Segment, Connection.End)); - PDI->DrawPoint(HandlePos, FColor::White, 10.0f, SDPG_Foreground); + PDI->DrawPoint(HandlePos, TangentColor, 10.0f, SDPG_Foreground); if (PDI->IsHitTesting()) PDI->SetHitProxy(NULL); } } } - if (GLevelEditorModeTools().GetWidgetMode() == FWidget::WM_Scale) + if (GLevelEditorModeTools().GetWidgetMode() == FWidget::WM_Scale && !Viewport->GetClient()->IsOrtho()) { for (ULandscapeSplineSegment* Segment : SelectedSplineSegments) { @@ -1823,9 +1881,10 @@ public: FVector EndPos = SplinesComponent->GetComponentTransform().TransformPosition(StartLocation); FVector EndHandlePos = SplinesComponent->GetComponentTransform().TransformPosition(StartLocation + StartRotation.Vector() * Connection.TangentLen / 2); - PDI->DrawLine(EndPos, EndHandlePos, FColor::White, SDPG_Foreground); + FColor TangentColor = (Segment == DraggingTangent_Segment && End == DraggingTangent_End) ? FColor::Yellow : FColor::White; + PDI->DrawLine(EndPos, EndHandlePos, TangentColor, SDPG_Foreground); if (PDI->IsHitTesting()) PDI->SetHitProxy(new HLandscapeSplineProxy_Tangent(Segment, !!End)); - PDI->DrawPoint(EndHandlePos, FColor::White, 10.0f, SDPG_Foreground); + PDI->DrawPoint(EndHandlePos, TangentColor, 10.0f, SDPG_Foreground); if (PDI->IsHitTesting()) PDI->SetHitProxy(NULL); } } @@ -1851,7 +1910,7 @@ public: virtual bool UsesTransformWidget() const override { - if (SelectedSplineControlPoints.Num() > 0) + if (SelectedSplineControlPoints.Num() > 0 || DraggingTangent_Segment) { // The editor can try to render the transform widget before the landscape editor ticks and realizes that the landscape has been hidden/deleted const ALandscapeProxy* LandscapeProxy = EdMode->CurrentToolTarget.LandscapeInfo->GetLandscapeProxy(); @@ -1889,10 +1948,22 @@ public: virtual FVector GetWidgetLocation() const override { - if (SelectedSplineControlPoints.Num() > 0) + const ALandscapeProxy* LandscapeProxy = EdMode->CurrentToolTarget.LandscapeInfo->GetLandscapeProxy(); + if (LandscapeProxy) { - const ALandscapeProxy* LandscapeProxy = EdMode->CurrentToolTarget.LandscapeInfo->GetLandscapeProxy(); - if (LandscapeProxy) + if (DraggingTangent_Segment) + { + const FLandscapeSplineSegmentConnection& Connection = DraggingTangent_Segment->Connections[DraggingTangent_End]; + ULandscapeSplineControlPoint* ControlPoint = Connection.ControlPoint; + ULandscapeSplinesComponent* SplinesComponent = ControlPoint->GetOuterULandscapeSplinesComponent(); + FVector StartLocation; FRotator StartRotation; + ControlPoint->GetConnectionLocationAndRotation(Connection.SocketName, StartLocation, StartRotation); + + // Return tangent handle location. + return SplinesComponent->GetComponentTransform().TransformPosition(StartLocation + StartRotation.Vector() * DraggingTangent_Length / 2); + + } + else if (SelectedSplineControlPoints.Num() > 0) { ULandscapeSplineControlPoint* FirstPoint = *SelectedSplineControlPoints.CreateConstIterator(); ULandscapeSplinesComponent* SplinesComponent = FirstPoint->GetOuterULandscapeSplinesComponent(); @@ -1905,10 +1976,18 @@ public: virtual FMatrix GetWidgetRotation() const override { - if (SelectedSplineControlPoints.Num() > 0) + const ALandscapeProxy* LandscapeProxy = EdMode->CurrentToolTarget.LandscapeInfo->GetLandscapeProxy(); + if (LandscapeProxy) { - const ALandscapeProxy* LandscapeProxy = EdMode->CurrentToolTarget.LandscapeInfo->GetLandscapeProxy(); - if (LandscapeProxy) + if (DraggingTangent_Segment) + { + const FLandscapeSplineSegmentConnection& Connection = DraggingTangent_Segment->Connections[DraggingTangent_End]; + ULandscapeSplinesComponent* SplinesComponent = Connection.ControlPoint->GetOuterULandscapeSplinesComponent(); + FVector StartLocation; FRotator StartRotation; + Connection.ControlPoint->GetConnectionLocationAndRotation(Connection.SocketName, StartLocation, StartRotation); + return FQuatRotationTranslationMatrix(StartRotation.Quaternion() * SplinesComponent->GetComponentTransform().GetRotation(), FVector::ZeroVector); + } + else if (SelectedSplineControlPoints.Num() > 0) { ULandscapeSplineControlPoint* FirstPoint = *SelectedSplineControlPoints.CreateConstIterator(); ULandscapeSplinesComponent* SplinesComponent = FirstPoint->GetOuterULandscapeSplinesComponent(); @@ -2150,6 +2229,8 @@ protected: TSet SelectedSplineSegments; ULandscapeSplineSegment* DraggingTangent_Segment; + float DraggingTangent_Length; + ECoordSystem DraggingTangent_CacheCoordSpace; uint32 DraggingTangent_End : 1; uint32 bMovingControlPoint : 1; diff --git a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetailCustomization_ProceduralLayers.cpp b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetailCustomization_ProceduralLayers.cpp index 4077ba4fa281..07f973796f07 100644 --- a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetailCustomization_ProceduralLayers.cpp +++ b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetailCustomization_ProceduralLayers.cpp @@ -22,6 +22,7 @@ #include "EditorModes.h" #include "LandscapeEditorModule.h" #include "LandscapeEditorObject.h" +#include "Landscape.h" #include "DetailLayoutBuilder.h" #include "IDetailPropertyRow.h" @@ -44,6 +45,8 @@ #include "LandscapeEditorDetailCustomization_TargetLayers.h" #include "Widgets/Input/SEditableText.h" #include "Widgets/Input/SNumericEntryBox.h" +#include "Widgets/Text/SInlineEditableTextBlock.h" +#include "LandscapeEditorCommands.h" #define LOCTEXT_NAMESPACE "LandscapeEditor.Layers" @@ -60,12 +63,7 @@ void FLandscapeEditorDetailCustomization_ProceduralLayers::CustomizeDetails(IDet FEdModeLandscape* LandscapeEdMode = GetEditorMode(); if (LandscapeEdMode && LandscapeEdMode->CurrentToolMode != nullptr) { - const FName CurrentToolName = LandscapeEdMode->CurrentTool->GetToolName(); - - if (LandscapeEdMode->CurrentToolMode->SupportedTargetTypes != 0) - { - LayerCategory.AddCustomBuilder(MakeShareable(new FLandscapeEditorCustomNodeBuilder_ProceduralLayers(DetailBuilder.GetThumbnailPool().ToSharedRef()))); - } + LayerCategory.AddCustomBuilder(MakeShareable(new FLandscapeEditorCustomNodeBuilder_ProceduralLayers(DetailBuilder.GetThumbnailPool().ToSharedRef()))); } } END_SLATE_FUNCTION_BUILD_OPTIMIZATION @@ -103,7 +101,7 @@ void FLandscapeEditorCustomNodeBuilder_ProceduralLayers::GenerateHeaderRowConten [ SNew(STextBlock) .Font(IDetailLayoutBuilder::GetDetailFont()) - .Text(FText::FromString(TEXT(""))) + .Text(FText::FromString(TEXT("Layers"))) ]; } @@ -127,6 +125,8 @@ void FLandscapeEditorCustomNodeBuilder_ProceduralLayers::GenerateChildContent(ID LayerList.ToSharedRef() ]; + InlineTextBlocks.Empty(); + InlineTextBlocks.AddDefaulted(LandscapeEdMode->GetProceduralLayerCount()); for (int32 i = 0; i < LandscapeEdMode->GetProceduralLayerCount(); ++i) { TSharedPtr GeneratedRowWidget = GenerateRow(i); @@ -151,105 +151,100 @@ TSharedPtr FLandscapeEditorCustomNodeBuilder_ProceduralLayers::Generate TSharedPtr RowWidget = SNew(SLandscapeEditorSelectableBorder) .Padding(0) .VAlign(VAlign_Center) - //.OnContextMenuOpening_Static(&FLandscapeEditorCustomNodeBuilder_Layers::OnTargetLayerContextMenuOpening, Target) + .OnContextMenuOpening(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::OnLayerContextMenuOpening, InLayerIndex) .OnSelected(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::OnLayerSelectionChanged, InLayerIndex) .IsSelected(TAttribute::Create(TAttribute::FGetter::CreateSP(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::IsLayerSelected, InLayerIndex))) .Visibility(EVisibility::Visible) [ SNew(SHorizontalBox) - /*+ SHorizontalBox::Slot() + + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) - .Padding(FMargin(2)) [ - SNew(SImage) - .Image(FEditorStyle::GetBrush(TEXT("LandscapeEditor.Target_Heightmap"))) - ] - */ - - + SHorizontalBox::Slot() - .VAlign(VAlign_Center) - .FillWidth(1.0f) - .Padding(4, 0) - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - .VAlign(VAlign_Center) - .Padding(0, 2) - .HAlign(HAlign_Left) + SNew(SButton) + .ButtonStyle(FEditorStyle::Get(), "NoBorder") + .OnClicked(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::OnToggleLock, InLayerIndex) + .ToolTipText(LOCTEXT("FLandscapeEditorCustomNodeBuilder_ProceduralLayerLock", "Locks the current layer")) [ - SNew(SEditableText) - .SelectAllTextWhenFocused(true) - .IsReadOnly(true) - .Text(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::GetLayerText, InLayerIndex) - .ToolTipText(LOCTEXT("FLandscapeEditorCustomNodeBuilder_ProceduralLayers_tooltip", "Name of the Layer")) - .OnTextCommitted(FOnTextCommitted::CreateSP(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::OnLayerTextCommitted, InLayerIndex)) + SNew(SImage) + .Image(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::GetLockBrushForLayer, InLayerIndex) ] ] - + SHorizontalBox::Slot() - .FillWidth(1.0f) + + +SHorizontalBox::Slot() + .AutoWidth() .VAlign(VAlign_Center) - .Padding(0, 2) - .HAlign(HAlign_Center) [ - SNew(SCheckBox) - .OnCheckStateChanged(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::OnLayerVisibilityChanged, InLayerIndex) - .IsChecked(TAttribute::Create(TAttribute::FGetter::CreateSP(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::IsLayerVisible, InLayerIndex))) - .ToolTipText(LOCTEXT("FLandscapeEditorCustomNodeBuilder_ProceduralLayerVisibility_Tooltips", "Is layer visible")) + SNew(SButton) + .ContentPadding(0) + .ButtonStyle(FEditorStyle::Get(), "NoBorder") + .OnClicked(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::OnToggleVisibility, InLayerIndex) + .ToolTipText(LOCTEXT("FLandscapeEditorCustomNodeBuilder_ProceduralLayerVisibility", "Toggle Layer Visibility")) + .HAlign(HAlign_Center) + .VAlign(VAlign_Center) .Content() [ - SNew(STextBlock) - .Text(LOCTEXT("FLandscapeEditorCustomNodeBuilder_ProceduralLayerVisibility", "Visibility")) + SNew(SImage) + .Image(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::GetVisibilityBrushForLayer, InLayerIndex) ] ] + + SHorizontalBox::Slot() - .Padding(0) - .FillWidth(1.0f) + .FillWidth(1.0) .VAlign(VAlign_Center) - .HAlign(HAlign_Left) + .Padding(4, 0) [ - SNew(STextBlock) - .Text(LOCTEXT("FLandscapeEditorCustomNodeBuilder_ProceduralLayerWeight", "Weight")) + SAssignNew(InlineTextBlocks[InLayerIndex], SInlineEditableTextBlock) + .Text(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::GetLayerText, InLayerIndex) + .ToolTipText(LOCTEXT("FLandscapeEditorCustomNodeBuilder_ProceduralLayers_tooltip", "Name of the Layer")) + .OnVerifyTextChanged(FOnVerifyTextChanged::CreateSP(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::CanRenameProceduralLayerTo, InLayerIndex)) + .OnTextCommitted(FOnTextCommitted::CreateSP(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::SetProceduralLayerName, InLayerIndex)) ] + + SHorizontalBox::Slot() + .FillWidth(1.0f) .VAlign(VAlign_Center) .Padding(0, 2) - .HAlign(HAlign_Left) - .FillWidth(1.0f) + .HAlign(HAlign_Right) [ - SNew(SNumericEntryBox) - .AllowSpin(true) - .MinValue(0.0f) - .MaxValue(65536.0f) - .MaxSliderValue(65536.0f) - .MinDesiredValueWidth(25.0f) - .Value(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::GetLayerWeight, InLayerIndex) - .OnValueChanged(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::SetLayerWeight, InLayerIndex) - .IsEnabled(true) - ] + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .Padding(0) + .FillWidth(1.0f) + .VAlign(VAlign_Center) + .HAlign(HAlign_Left) + [ + SNew(STextBlock) + .Text(LOCTEXT("FLandscapeEditorCustomNodeBuilder_ProceduralLayerAlpha", "Alpha")) + ] + + SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .Padding(0, 2) + .HAlign(HAlign_Left) + .FillWidth(1.0f) + [ + SNew(SNumericEntryBox) + .AllowSpin(true) + .MinValue(0.0f) + .MaxValue(100.0f) + .MaxSliderValue(100.0f) + .MinDesiredValueWidth(60.0f) + .Value(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::GetLayerAlpha, InLayerIndex) + .OnValueChanged(this, &FLandscapeEditorCustomNodeBuilder_ProceduralLayers::SetLayerAlpha, InLayerIndex) + .IsEnabled(true) + ] + ] ]; return RowWidget; } END_SLATE_FUNCTION_BUILD_OPTIMIZATION -void FLandscapeEditorCustomNodeBuilder_ProceduralLayers::OnLayerTextCommitted(const FText& InText, ETextCommit::Type InCommitType, int32 InLayerIndex) -{ - FEdModeLandscape* LandscapeEdMode = GetEditorMode(); - - if (LandscapeEdMode != nullptr) - { - LandscapeEdMode->SetProceduralLayerName(InLayerIndex, *InText.ToString()); - } -} - FText FLandscapeEditorCustomNodeBuilder_ProceduralLayers::GetLayerText(int32 InLayerIndex) const { FEdModeLandscape* LandscapeEdMode = GetEditorMode(); - - if (LandscapeEdMode != nullptr) + if (LandscapeEdMode) { return FText::FromName(LandscapeEdMode->GetProceduralLayerName(InLayerIndex)); } @@ -268,6 +263,186 @@ bool FLandscapeEditorCustomNodeBuilder_ProceduralLayers::IsLayerSelected(int32 I return false; } +bool FLandscapeEditorCustomNodeBuilder_ProceduralLayers::CanRenameProceduralLayerTo(const FText& InNewText, FText& OutErrorMessage, int32 InLayerIndex) +{ + FEdModeLandscape* LandscapeEdMode = GetEditorMode(); + if (LandscapeEdMode) + { + if (!LandscapeEdMode->CanRenameProceduralLayerTo(InLayerIndex, *InNewText.ToString())) + { + OutErrorMessage = LOCTEXT("RenameFailed_AlreadyExists", "This layer already exists"); + return false; + } + } + return true; +} + +void FLandscapeEditorCustomNodeBuilder_ProceduralLayers::SetProceduralLayerName(const FText& InText, ETextCommit::Type InCommitType, int32 InLayerIndex) +{ + FEdModeLandscape* LandscapeEdMode = GetEditorMode(); + if (LandscapeEdMode) + { + const FScopedTransaction Transaction(LOCTEXT("Landscape_ProceduralLayers_Rename", "Rename Procedural Layer")); + LandscapeEdMode->SetProceduralLayerName(InLayerIndex, *InText.ToString()); + } +} + +TSharedPtr FLandscapeEditorCustomNodeBuilder_ProceduralLayers::OnLayerContextMenuOpening(int32 InLayerIndex) +{ + FEdModeLandscape* LandscapeEdMode = GetEditorMode(); + ALandscape* Landscape = LandscapeEdMode ? LandscapeEdMode->GetLandscape() : nullptr; + if (LandscapeEdMode && Landscape) + { + FProceduralLayer* Layer = LandscapeEdMode->GetProceduralLayer(InLayerIndex); + TSharedRef SharedThis = AsShared(); + FMenuBuilder MenuBuilder(true, NULL); + MenuBuilder.BeginSection("LandscapeEditorProceduralLayerActions", LOCTEXT("LandscapeEditorProceduralLayerActions.Heading", "Layers")); + { + // Create Layer + FUIAction CreateLayerAction = FUIAction(FExecuteAction::CreateLambda([SharedThis] { SharedThis->CreateLayer(); })); + MenuBuilder.AddMenuEntry(LOCTEXT("CreateLayer", "Create"), LOCTEXT("CreateLayerTooltip", "Create Layer"), FSlateIcon(), CreateLayerAction); + + if (Layer) + { + // Rename Layer + FUIAction RenameLayerAction = FUIAction(FExecuteAction::CreateLambda([SharedThis, InLayerIndex] { SharedThis->RenameLayer(InLayerIndex); })); + MenuBuilder.AddMenuEntry(LOCTEXT("RenameLayer", "Rename..."), LOCTEXT("RenameLayerTooltip", "Rename Layer"), FSlateIcon(), RenameLayerAction); + + if (!Layer->bLocked) + { + // Clear Layer + FUIAction ClearLayerAction = FUIAction(FExecuteAction::CreateLambda([SharedThis, InLayerIndex] { SharedThis->ClearLayer(InLayerIndex); })); + MenuBuilder.AddMenuEntry(LOCTEXT("ClearLayer", "Clear..."), LOCTEXT("ClearLayerTooltip", "Clear Layer"), FSlateIcon(), ClearLayerAction); + + if (Landscape->ProceduralLayers.Num() > 1) + { + // Delete Layer + FUIAction DeleteLayerAction = FUIAction(FExecuteAction::CreateLambda([SharedThis, InLayerIndex] { SharedThis->DeleteLayer(InLayerIndex); } )); + MenuBuilder.AddMenuEntry(LOCTEXT("DeleteLayer", "Delete..."), LOCTEXT("DeleteLayerTooltip", "Delete Layer"), FSlateIcon(), DeleteLayerAction); + } + } + } + } + MenuBuilder.EndSection(); + + MenuBuilder.BeginSection("LandscapeEditorProceduralLayerVisibility", LOCTEXT("LandscapeEditorProceduralLayerVisibility.Heading", "Visibility")); + { + if (Layer) + { + if (Layer->bVisible) + { + // Hide Selected Layer + FUIAction HideSelectedLayerAction = FUIAction(FExecuteAction::CreateLambda([SharedThis, InLayerIndex] { SharedThis->OnToggleVisibility(InLayerIndex); })); + MenuBuilder.AddMenuEntry(LOCTEXT("HideSelectedLayer", "Hide Selected"), LOCTEXT("HideSelectedLayerTooltip", "Hide Selected Layer"), FSlateIcon(), HideSelectedLayerAction); + } + else + { + // Show Selected Layer + FUIAction ShowSelectedLayerAction = FUIAction(FExecuteAction::CreateLambda([SharedThis, InLayerIndex] { SharedThis->OnToggleVisibility(InLayerIndex); })); + MenuBuilder.AddMenuEntry(LOCTEXT("ShowSelectedLayer", "Show Selected"), LOCTEXT("ShowSelectedLayerTooltip", "Show Selected Layer"), FSlateIcon(), ShowSelectedLayerAction); + } + + // Show Only Selected Layer + FUIAction ShowOnlySelectedLayerAction = FUIAction(FExecuteAction::CreateLambda([SharedThis, InLayerIndex] { SharedThis->ShowOnlySelectedLayer(InLayerIndex); })); + MenuBuilder.AddMenuEntry(LOCTEXT("ShowOnlySelectedLayer", "Show Only Selected"), LOCTEXT("ShowOnlySelectedLayerTooltip", "Show Only Selected Layer"), FSlateIcon(), ShowOnlySelectedLayerAction); + } + + // Show All Layers + FUIAction ShowAllLayersAction = FUIAction(FExecuteAction::CreateLambda([SharedThis] { SharedThis->ShowAllLayers(); })); + MenuBuilder.AddMenuEntry(LOCTEXT("ShowAllLayers", "Show All Layers"), LOCTEXT("ShowAllLayersTooltip", "Show All Layers"), FSlateIcon(), ShowAllLayersAction); + } + MenuBuilder.EndSection(); + + return MenuBuilder.MakeWidget(); + } + return NULL; +} + +void FLandscapeEditorCustomNodeBuilder_ProceduralLayers::RenameLayer(int32 InLayerIndex) +{ + if (InlineTextBlocks.IsValidIndex(InLayerIndex) && InlineTextBlocks[InLayerIndex].IsValid()) + { + InlineTextBlocks[InLayerIndex]->EnterEditingMode(); + } +} + +void FLandscapeEditorCustomNodeBuilder_ProceduralLayers::ClearLayer(int32 InLayerIndex) +{ + FEdModeLandscape* LandscapeEdMode = GetEditorMode(); + ALandscape* Landscape = LandscapeEdMode ? LandscapeEdMode->GetLandscape() : nullptr; + if (Landscape) + { + FProceduralLayer* Layer = LandscapeEdMode->GetProceduralLayer(InLayerIndex); + if (Layer) + { + EAppReturnType::Type Result = FMessageDialog::Open(EAppMsgType::YesNo, FText::Format(LOCTEXT("LandscapeMode_Message", "The layer {0} content will be completely cleared. Continue?"), FText::FromName(Layer->Name))); + if (Result == EAppReturnType::Yes) + { + const FScopedTransaction Transaction(LOCTEXT("Landscape_ProceduralLayers_Clean", "Clean Procedural Layer")); + Landscape->ClearProceduralLayer(InLayerIndex); + } + } + } +} + +void FLandscapeEditorCustomNodeBuilder_ProceduralLayers::DeleteLayer(int32 InLayerIndex) +{ + FEdModeLandscape* LandscapeEdMode = GetEditorMode(); + ALandscape* Landscape = LandscapeEdMode ? LandscapeEdMode->GetLandscape() : nullptr; + if (Landscape && Landscape->ProceduralLayers.Num() > 1) + { + FProceduralLayer* Layer = LandscapeEdMode->GetProceduralLayer(InLayerIndex); + if (Layer) + { + EAppReturnType::Type Result = FMessageDialog::Open(EAppMsgType::YesNo, FText::Format(LOCTEXT("LandscapeMode_Message", "The layer {0} will be deleted. Continue?"), FText::FromName(Layer->Name))); + if (Result == EAppReturnType::Yes) + { + const FScopedTransaction Transaction(LOCTEXT("Landscape_ProceduralLayers_Delete", "Delete Procedural Layer")); + Landscape->DeleteProceduralLayer(InLayerIndex); + int32 NewLayerSelectionIndex = Landscape->GetProceduralLayer(InLayerIndex) ? InLayerIndex : 0; + OnLayerSelectionChanged(NewLayerSelectionIndex); + LandscapeEdMode->RefreshDetailPanel(); + } + } + } +} + +void FLandscapeEditorCustomNodeBuilder_ProceduralLayers::ShowOnlySelectedLayer(int32 InLayerIndex) +{ + FEdModeLandscape* LandscapeEdMode = GetEditorMode(); + ALandscape* Landscape = LandscapeEdMode ? LandscapeEdMode->GetLandscape() : nullptr; + if (Landscape) + { + const FScopedTransaction Transaction(LOCTEXT("ShowOnlySelectedLayer", "Show Only Selected Layer")); + Landscape->ShowOnlySelectedProceduralLayer(InLayerIndex); + } +} + +void FLandscapeEditorCustomNodeBuilder_ProceduralLayers::ShowAllLayers() +{ + FEdModeLandscape* LandscapeEdMode = GetEditorMode(); + ALandscape* Landscape = LandscapeEdMode ? LandscapeEdMode->GetLandscape() : nullptr; + if (Landscape) + { + const FScopedTransaction Transaction(LOCTEXT("ShowAllLayers", "Show All Layers")); + Landscape->ShowAllProceduralLayers(); + } +} + +void FLandscapeEditorCustomNodeBuilder_ProceduralLayers::CreateLayer() +{ + FEdModeLandscape* LandscapeEdMode = GetEditorMode(); + ALandscape* Landscape = LandscapeEdMode ? LandscapeEdMode->GetLandscape() : nullptr; + if (Landscape) + { + { + const FScopedTransaction Transaction(LOCTEXT("Landscape_ProceduralLayers_Create", "Create Procedural Layer")); + Landscape->CreateProceduralLayer(); + } + LandscapeEdMode->RefreshDetailPanel(); + } +} + void FLandscapeEditorCustomNodeBuilder_ProceduralLayers::OnLayerSelectionChanged(int32 InLayerIndex) { FEdModeLandscape* LandscapeEdMode = GetEditorMode(); @@ -278,48 +453,61 @@ void FLandscapeEditorCustomNodeBuilder_ProceduralLayers::OnLayerSelectionChanged } } -TOptional FLandscapeEditorCustomNodeBuilder_ProceduralLayers::GetLayerWeight(int32 InLayerIndex) const +TOptional FLandscapeEditorCustomNodeBuilder_ProceduralLayers::GetLayerAlpha(int32 InLayerIndex) const { FEdModeLandscape* LandscapeEdMode = GetEditorMode(); if (LandscapeEdMode) { - return LandscapeEdMode->GetProceduralLayerWeight(InLayerIndex); + return LandscapeEdMode->GetProceduralLayerAlpha(InLayerIndex); } return 1.0f; } -void FLandscapeEditorCustomNodeBuilder_ProceduralLayers::SetLayerWeight(float InWeight, int32 InLayerIndex) +void FLandscapeEditorCustomNodeBuilder_ProceduralLayers::SetLayerAlpha(float InAlpha, int32 InLayerIndex) { FEdModeLandscape* LandscapeEdMode = GetEditorMode(); if (LandscapeEdMode) { - LandscapeEdMode->SetProceduralLayerWeight(InWeight, InLayerIndex); + const FScopedTransaction Transaction(LOCTEXT("Landscape_ProceduralLayers_SetAlpha", "Set Procedural Layer Alpha")); + LandscapeEdMode->SetProceduralLayerAlpha(InLayerIndex, InAlpha); } } -void FLandscapeEditorCustomNodeBuilder_ProceduralLayers::OnLayerVisibilityChanged(ECheckBoxState NewState, int32 InLayerIndex) +FReply FLandscapeEditorCustomNodeBuilder_ProceduralLayers::OnToggleVisibility(int32 InLayerIndex) { FEdModeLandscape* LandscapeEdMode = GetEditorMode(); - if (LandscapeEdMode) { - LandscapeEdMode->SetProceduralLayerVisibility(NewState == ECheckBoxState::Checked, InLayerIndex); + LandscapeEdMode->SetProceduralLayerVisibility(!LandscapeEdMode->IsProceduralLayerVisible(InLayerIndex), InLayerIndex); } + return FReply::Handled(); } -ECheckBoxState FLandscapeEditorCustomNodeBuilder_ProceduralLayers::IsLayerVisible(int32 InLayerIndex) const +const FSlateBrush* FLandscapeEditorCustomNodeBuilder_ProceduralLayers::GetVisibilityBrushForLayer(int32 InLayerIndex) const { FEdModeLandscape* LandscapeEdMode = GetEditorMode(); + bool bIsVisible = LandscapeEdMode && LandscapeEdMode->IsProceduralLayerVisible(InLayerIndex); + return bIsVisible ? FEditorStyle::GetBrush("Level.VisibleIcon16x") : FEditorStyle::GetBrush("Level.NotVisibleIcon16x"); +} +FReply FLandscapeEditorCustomNodeBuilder_ProceduralLayers::OnToggleLock(int32 InLayerIndex) +{ + FEdModeLandscape* LandscapeEdMode = GetEditorMode(); if (LandscapeEdMode) { - return LandscapeEdMode->IsProceduralLayerVisible(InLayerIndex) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; + LandscapeEdMode->SetProceduralLayerLocked(InLayerIndex, !LandscapeEdMode->IsProceduralLayerLocked(InLayerIndex)); + } + return FReply::Handled(); } - return ECheckBoxState::Unchecked; +const FSlateBrush* FLandscapeEditorCustomNodeBuilder_ProceduralLayers::GetLockBrushForLayer(int32 InLayerIndex) const +{ + FEdModeLandscape* LandscapeEdMode = GetEditorMode(); + bool bIsLocked = LandscapeEdMode && LandscapeEdMode->IsProceduralLayerLocked(InLayerIndex); + return bIsLocked ? FEditorStyle::GetBrush(TEXT("PropertyWindow.Locked")) : FEditorStyle::GetBrush(TEXT("PropertyWindow.Unlocked")); } FReply FLandscapeEditorCustomNodeBuilder_ProceduralLayers::HandleDragDetected(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent, int32 SlotIndex, SVerticalBox::FSlot* Slot) diff --git a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetailCustomization_ProceduralLayers.h b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetailCustomization_ProceduralLayers.h index 924b9b5a5554..8c2ad4501092 100644 --- a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetailCustomization_ProceduralLayers.h +++ b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetailCustomization_ProceduralLayers.h @@ -7,6 +7,7 @@ #include "Layout/Visibility.h" #include "Layout/Margin.h" #include "Styling/SlateColor.h" +#include "Styling/SlateBrush.h" #include "Input/Reply.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SWidget.h" @@ -65,13 +66,28 @@ protected: bool IsLayerSelected(int32 LayerIndex); void OnLayerSelectionChanged(int32 LayerIndex); - - void OnLayerTextCommitted(const FText& InText, ETextCommit::Type InCommitType, int32 InLayerIndex); + TSharedPtr OnLayerContextMenuOpening(int32 InLayerIndex); + void CreateLayer(); + void ClearLayer(int32 InLayerIndex); + void RenameLayer(int32 InLayerIndex); + void DeleteLayer(int32 InLayerIndex); + void ShowOnlySelectedLayer(int32 InLayerIndex); + void ShowAllLayers(); + bool CanRenameProceduralLayerTo(const FText& NewText, FText& OutErrorMessage, int32 InLayerIndex); + void SetProceduralLayerName(const FText& InText, ETextCommit::Type InCommitType, int32 InLayerIndex); FText GetLayerText(int32 InLayerIndex) const; - TOptional GetLayerWeight(int32 InLayerIndex) const; - void SetLayerWeight(float InWeight, int32 InLayerIndex); + TOptional GetLayerAlpha(int32 InLayerIndex) const; + void SetLayerAlpha(float InAlpha, int32 InLayerIndex); - void OnLayerVisibilityChanged(ECheckBoxState NewState, int32 InLayerIndex); - ECheckBoxState IsLayerVisible(int32 InLayerIndex) const; + FReply OnToggleVisibility(int32 InLayerIndex); + const FSlateBrush* GetVisibilityBrushForLayer(int32 InLayerIndex) const; + + FReply OnToggleLock(int32 InLayerIndex); + const FSlateBrush* GetLockBrushForLayer(int32 InLayerIndex) const; + +private: + + /** Widgets for displaying and editing the layer name */ + TArray< TSharedPtr< SInlineEditableTextBlock > > InlineTextBlocks; }; \ No newline at end of file diff --git a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetailCustomization_TargetLayers.cpp b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetailCustomization_TargetLayers.cpp index c7eac0b498c4..27b7ed3ff07f 100644 --- a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetailCustomization_TargetLayers.cpp +++ b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetailCustomization_TargetLayers.cpp @@ -22,6 +22,7 @@ #include "EditorModes.h" #include "LandscapeEditorModule.h" #include "LandscapeEditorObject.h" +#include "Landscape.h" #include "DetailLayoutBuilder.h" #include "IDetailPropertyRow.h" @@ -41,6 +42,7 @@ #include "IDetailGroup.h" #include "Widgets/SBoxPanel.h" #include "Editor/EditorStyle/Private/SlateEditorStyle.h" +#include "Settings/EditorExperimentalSettings.h" #define LOCTEXT_NAMESPACE "LandscapeEditor.TargetLayers" @@ -698,6 +700,24 @@ TSharedPtr FLandscapeEditorCustomNodeBuilder_TargetLayers::GenerateRow( ] + SVerticalBox::Slot() .AutoHeight() + [ + SNew(SHorizontalBox) + .Visibility_Static(&FLandscapeEditorCustomNodeBuilder_TargetLayers::GetProceduralLayerSubstractiveBlendVisibility, Target) + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(0, 2, 2, 2) + [ + SNew(SCheckBox) + .IsChecked_Static(&FLandscapeEditorCustomNodeBuilder_TargetLayers::ProceduralLayerSubstractiveBlendIsChecked, Target) + .OnCheckStateChanged_Static(&FLandscapeEditorCustomNodeBuilder_TargetLayers::OnProceduralLayerSubstractiveBlendChanged, Target) + [ + SNew(STextBlock) + .Text(LOCTEXT("ViewMode.Debug_None", "Substractive Blend")) + ] + ] + ] + + SVerticalBox::Slot() + .AutoHeight() [ SNew(SHorizontalBox) .Visibility_Static(&FLandscapeEditorCustomNodeBuilder_TargetLayers::GetDebugModeColorChannelVisibility, Target) @@ -919,7 +939,7 @@ TSharedPtr FLandscapeEditorCustomNodeBuilder_TargetLayers::OnTargetLaye // Rebuild material instances FUIAction RebuildAction = FUIAction(FExecuteAction::CreateStatic(&FLandscapeEditorCustomNodeBuilder_TargetLayers::OnRebuildMICs, Target)); - MenuBuilder.AddMenuEntry(LOCTEXT("LayerContextMenu.Rebuild", "Rebuild Materials"), LOCTEXT("LayerContextMenu.Rebuild_Tooltip", "Rebuild material instances used for this landscape."), FSlateIcon(), ClearAction); + MenuBuilder.AddMenuEntry(LOCTEXT("LayerContextMenu.Rebuild", "Rebuild Materials"), LOCTEXT("LayerContextMenu.Rebuild_Tooltip", "Rebuild material instances used for this landscape."), FSlateIcon(), RebuildAction); } else if (Target->TargetType == ELandscapeToolTargetType::Visibility) { @@ -1070,14 +1090,37 @@ void FLandscapeEditorCustomNodeBuilder_TargetLayers::OnFillLayer(const TSharedRe if (Target->LandscapeInfo.IsValid() && Target->LayerInfoObj.IsValid()) { FLandscapeEditDataInterface LandscapeEdit(Target->LandscapeInfo.Get()); - LandscapeEdit.FillLayer(Target->LayerInfoObj.Get()); + + FEdModeLandscape* LandscapeEdMode = GetEditorMode(); + if (LandscapeEdMode) + { + FScopedSetLandscapeCurrentEditingProceduralLayer Scope(LandscapeEdMode->GetLandscape(), LandscapeEdMode->GetCurrentProceduralLayerGuid(), [&] { LandscapeEdMode->RequestProceduralContentUpdate(true); }); + LandscapeEdit.FillLayer(Target->LayerInfoObj.Get()); + } } } void FLandscapeEditorCustomNodeBuilder_TargetLayers::FillEmptyLayers(ULandscapeInfo* LandscapeInfo, ULandscapeLayerInfoObject* LandscapeInfoObject) { - FLandscapeEditDataInterface LandscapeEdit(LandscapeInfo); - LandscapeEdit.FillEmptyLayers(LandscapeInfoObject); + FEdModeLandscape* LandscapeEdMode = GetEditorMode(); + if (LandscapeEdMode) + { + FLandscapeEditDataInterface LandscapeEdit(LandscapeInfo); + + if (GetMutableDefault()->bProceduralLandscape) + { + if (LandscapeEdMode->NeedToFillEmptyLayersForProcedural()) + { + FScopedSetLandscapeCurrentEditingProceduralLayer Scope(LandscapeEdMode->GetLandscape(), LandscapeEdMode->GetCurrentProceduralLayerGuid()); + LandscapeEdit.FillEmptyLayers(LandscapeInfoObject); + } + } + else + { + LandscapeEdit.FillEmptyLayers(LandscapeInfoObject); + } + } + } @@ -1086,8 +1129,13 @@ void FLandscapeEditorCustomNodeBuilder_TargetLayers::OnClearLayer(const TSharedR FScopedTransaction Transaction(LOCTEXT("Undo_ClearLayer", "Clearing Landscape Layer")); if (Target->LandscapeInfo.IsValid() && Target->LayerInfoObj.IsValid()) { - FLandscapeEditDataInterface LandscapeEdit(Target->LandscapeInfo.Get()); - LandscapeEdit.DeleteLayer(Target->LayerInfoObj.Get()); + FEdModeLandscape* LandscapeEdMode = GetEditorMode(); + if (LandscapeEdMode) + { + FScopedSetLandscapeCurrentEditingProceduralLayer Scope(LandscapeEdMode->GetLandscape(), LandscapeEdMode->GetCurrentProceduralLayerGuid(), [&] { LandscapeEdMode->RequestProceduralContentUpdate(true); }); + FLandscapeEditDataInterface LandscapeEdit(Target->LandscapeInfo.Get()); + LandscapeEdit.DeleteLayer(Target->LayerInfoObj.Get()); + } } } @@ -1377,6 +1425,36 @@ EVisibility FLandscapeEditorCustomNodeBuilder_TargetLayers::GetDebugModeLayerUsa return EVisibility::Visible; } +EVisibility FLandscapeEditorCustomNodeBuilder_TargetLayers::GetProceduralLayerSubstractiveBlendVisibility(const TSharedRef Target) +{ + if (GetMutableDefault()->bProceduralLandscape && Target->TargetType != ELandscapeToolTargetType::Heightmap && Target->LayerInfoObj.IsValid()) + { + return EVisibility::Visible; + } + + return EVisibility::Collapsed; +} + +ECheckBoxState FLandscapeEditorCustomNodeBuilder_TargetLayers::ProceduralLayerSubstractiveBlendIsChecked(const TSharedRef Target) +{ + FEdModeLandscape* LandscapeEdMode = GetEditorMode(); + if (LandscapeEdMode) + { + return LandscapeEdMode->IsCurrentProceduralLayerBlendSubstractive(Target.Get().LayerInfoObj) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; + } + + return ECheckBoxState::Unchecked; +} + +void FLandscapeEditorCustomNodeBuilder_TargetLayers::OnProceduralLayerSubstractiveBlendChanged(ECheckBoxState NewCheckedState, const TSharedRef Target) +{ + FEdModeLandscape* LandscapeEdMode = GetEditorMode(); + if (LandscapeEdMode) + { + LandscapeEdMode->SetCurrentProceduralLayerSubstractiveBlendStatus(NewCheckedState == ECheckBoxState::Checked, Target.Get().LayerInfoObj); + } +} + EVisibility FLandscapeEditorCustomNodeBuilder_TargetLayers::GetDebugModeColorChannelVisibility(const TSharedRef Target) { if (GLandscapeViewMode == ELandscapeViewMode::DebugLayer && Target->TargetType != ELandscapeToolTargetType::Heightmap && Target->LayerInfoObj.IsValid()) diff --git a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetailCustomization_TargetLayers.h b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetailCustomization_TargetLayers.h index 3dd7e06e2bf7..4e482f3894b0 100644 --- a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetailCustomization_TargetLayers.h +++ b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetailCustomization_TargetLayers.h @@ -94,6 +94,10 @@ protected: static ECheckBoxState DebugModeColorChannelIsChecked(const TSharedRef Target, int32 Channel); static void OnDebugModeColorChannelChanged(ECheckBoxState NewCheckedState, const TSharedRef Target, int32 Channel); + static EVisibility GetProceduralLayerSubstractiveBlendVisibility(const TSharedRef Target); + static ECheckBoxState ProceduralLayerSubstractiveBlendIsChecked(const TSharedRef Target); + static void OnProceduralLayerSubstractiveBlendChanged(ECheckBoxState NewCheckedState, const TSharedRef Target); + // Drag/Drop handling FReply HandleDragDetected(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent, int32 SlotIndex, SVerticalBox::FSlot* Slot); TOptional HandleCanAcceptDrop(const FDragDropEvent& DragDropEvent, SDragAndDropVerticalBox::EItemDropZone DropZone, SVerticalBox::FSlot* Slot); diff --git a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetails.cpp b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetails.cpp index 025fc5f8842c..008695d3f77b 100644 --- a/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetails.cpp +++ b/Engine/Source/Editor/LandscapeEditor/Private/LandscapeEditorDetails.cpp @@ -108,20 +108,20 @@ void FLandscapeEditorDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuild Customization_AlphaBrush = MakeShareable(new FLandscapeEditorDetailCustomization_AlphaBrush); Customization_AlphaBrush->CustomizeDetails(DetailBuilder); - // Target Layers: - Customization_TargetLayers = MakeShareable(new FLandscapeEditorDetailCustomization_TargetLayers); - Customization_TargetLayers->CustomizeDetails(DetailBuilder); - if (GetMutableDefault()->bProceduralLandscape) { - // Brush Stack - Customization_ProceduralBrushStack = MakeShareable(new FLandscapeEditorDetailCustomization_ProceduralBrushStack); - Customization_ProceduralBrushStack->CustomizeDetails(DetailBuilder); - // Procedural Layers Customization_ProceduralLayers = MakeShareable(new FLandscapeEditorDetailCustomization_ProceduralLayers); Customization_ProceduralLayers->CustomizeDetails(DetailBuilder); + + // Brush Stack + Customization_ProceduralBrushStack = MakeShareable(new FLandscapeEditorDetailCustomization_ProceduralBrushStack); + Customization_ProceduralBrushStack->CustomizeDetails(DetailBuilder); } + + // Target Layers: + Customization_TargetLayers = MakeShareable(new FLandscapeEditorDetailCustomization_TargetLayers); + Customization_TargetLayers->CustomizeDetails(DetailBuilder); } FText FLandscapeEditorDetails::GetLocalizedName(FString Name) diff --git a/Engine/Source/Editor/LandscapeEditor/Public/LandscapeEditorObject.h b/Engine/Source/Editor/LandscapeEditor/Public/LandscapeEditorObject.h index 23387747e4b2..3dae9a37e207 100644 --- a/Engine/Source/Editor/LandscapeEditor/Public/LandscapeEditorObject.h +++ b/Engine/Source/Editor/LandscapeEditor/Public/LandscapeEditorObject.h @@ -29,8 +29,11 @@ enum class ELandscapeToolFlattenMode : int8 /** Flatten may only lower values, values below the clicked point will be left unchanged */ Lower = 2, + /** Flatten to closest specific terrace interval at the clicked point */ + Interval = 3, + /** Flatten to specific terrace height intervals */ - Terrace = 3 + Terrace = 4 }; UENUM() diff --git a/Engine/Source/Editor/LandscapeEditor/Public/LandscapeToolInterface.h b/Engine/Source/Editor/LandscapeEditor/Public/LandscapeToolInterface.h index fe3e82ee3694..6b486ecb42cb 100644 --- a/Engine/Source/Editor/LandscapeEditor/Public/LandscapeToolInterface.h +++ b/Engine/Source/Editor/LandscapeEditor/Public/LandscapeToolInterface.h @@ -231,6 +231,15 @@ public: virtual FMatrix GetWidgetRotation() const { return FMatrix::Identity; } virtual bool DisallowMouseDeltaTracking() const { return false; } + /** Get override cursor visibility settings */ + virtual bool GetOverrideCursorVisibility(bool& bWantsOverride, bool& bHardwareCursorVisible, bool bSoftwareCursorVisible) const { return false; } + + /** Called before mouse movement is converted to drag/rot */ + virtual bool PreConvertMouseMovement(FEditorViewportClient* InViewportClient) { return false; } + + /** Called after mouse movement is converted to drag/rot */ + virtual bool PostConvertMouseMovement(FEditorViewportClient* InViewportClient) { return false; } + virtual void SetCanToolBeActivated(bool Value) { } virtual bool CanToolBeActivated() const { return true; } virtual void SetExternalModifierPressed(const bool bPressed) {}; diff --git a/Engine/Source/Editor/LevelEditor/Private/LevelEditorActions.cpp b/Engine/Source/Editor/LevelEditor/Private/LevelEditorActions.cpp index 4c8e2d2785a0..c87208aa2c16 100644 --- a/Engine/Source/Editor/LevelEditor/Private/LevelEditorActions.cpp +++ b/Engine/Source/Editor/LevelEditor/Private/LevelEditorActions.cpp @@ -1035,7 +1035,15 @@ void FLevelEditorActionCallbacks::RecompileGameCode_Clicked() ILiveCodingModule* LiveCoding = FModuleManager::GetModulePtr(LIVE_CODING_MODULE_NAME); if (LiveCoding != nullptr && LiveCoding->IsEnabledByDefault()) { - LiveCoding->Compile(); + LiveCoding->EnableForSession(true); + if (LiveCoding->IsEnabledForSession()) + { + LiveCoding->Compile(); + } + else + { + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("NoLiveCodingCompileAfterHotReload", "Live Coding cannot be enabled after hot-reload has been used. Please restart the editor.")); + } return; } #endif @@ -1078,6 +1086,11 @@ void FLevelEditorActionCallbacks::LiveCoding_ToggleEnabled() if (LiveCoding != nullptr) { LiveCoding->EnableByDefault(!LiveCoding->IsEnabledByDefault()); + + if (LiveCoding->IsEnabledByDefault() && !LiveCoding->IsEnabledForSession()) + { + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("NoEnableLiveCodingAfterHotReload", "Live Coding cannot be enabled after hot-reload has been used. Please restart the editor.")); + } } } @@ -1090,9 +1103,14 @@ bool FLevelEditorActionCallbacks::LiveCoding_IsEnabled( ) void FLevelEditorActionCallbacks::LiveCoding_StartSession_Clicked() { ILiveCodingModule* LiveCoding = FModuleManager::GetModulePtr(LIVE_CODING_MODULE_NAME); - if (LiveCoding!= nullptr) + if (LiveCoding != nullptr) { LiveCoding->EnableForSession(true); + + if (!LiveCoding->IsEnabledForSession()) + { + FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("NoStartedLiveCodingAfterHotReload", "Live Coding cannot be started after hot-reload has been used. Please restart the editor.")); + } } } diff --git a/Engine/Source/Editor/LevelEditor/Private/LevelEditorToolBar.cpp b/Engine/Source/Editor/LevelEditor/Private/LevelEditorToolBar.cpp index 55385abb2355..ce3f62155ba8 100644 --- a/Engine/Source/Editor/LevelEditor/Private/LevelEditorToolBar.cpp +++ b/Engine/Source/Editor/LevelEditor/Private/LevelEditorToolBar.cpp @@ -1281,6 +1281,23 @@ TSharedRef< SWidget > FLevelEditorToolBar::MakeLevelEditorToolBar( const TShared static FText GetPreviewModeText() { + UMaterialShaderQualitySettings* MaterialShaderQualitySettings = UMaterialShaderQualitySettings::Get(); + const FName& PreviewPlatform = MaterialShaderQualitySettings->GetPreviewPlatform(); + + EShaderPlatform ShaderPlatform = ShaderFormatToLegacyShaderPlatform(PreviewPlatform); + if (ShaderPlatform == SP_NumPlatforms) + { + ShaderPlatform = GetFeatureLevelShaderPlatform(GEditor->PreviewFeatureLevel); + } + + switch (ShaderPlatform) + { + case SP_VULKAN_ES3_1_ANDROID: + { + return LOCTEXT("PreviewModeES31_Vulkan_Text", "Vulkan Preview"); + } + } + switch (GEditor->PreviewFeatureLevel) { case ERHIFeatureLevel::SM4: diff --git a/Engine/Source/Editor/MaterialEditor/Private/MaterialEditor.cpp b/Engine/Source/Editor/MaterialEditor/Private/MaterialEditor.cpp index 08e47331b3c3..1984989305be 100644 --- a/Engine/Source/Editor/MaterialEditor/Private/MaterialEditor.cpp +++ b/Engine/Source/Editor/MaterialEditor/Private/MaterialEditor.cpp @@ -3927,6 +3927,8 @@ FText FMaterialEditor::GetOriginalObjectName() const void FMaterialEditor::UpdateMaterialAfterGraphChange() { + FlushRenderingCommands(); + Material->MaterialGraph->LinkMaterialExpressionsFromGraph(); // Update the current preview material. @@ -3951,6 +3953,8 @@ FMaterialRenderProxy* FMaterialEditor::GetExpressionPreview(UMaterialExpression* void FMaterialEditor::UndoGraphAction() { + FlushRenderingCommands(); + int32 NumExpressions = Material->Expressions.Num(); GEditor->UndoTransaction(); @@ -3962,6 +3966,8 @@ void FMaterialEditor::UndoGraphAction() void FMaterialEditor::RedoGraphAction() { + FlushRenderingCommands(); + // Clear selection, to avoid holding refs to nodes that go away GraphEditor->ClearSelectionSet(); diff --git a/Engine/Source/Editor/MaterialEditor/Private/MaterialEditorUtilities.cpp b/Engine/Source/Editor/MaterialEditor/Private/MaterialEditorUtilities.cpp index 25dba01937f8..dbbb94499ab1 100644 --- a/Engine/Source/Editor/MaterialEditor/Private/MaterialEditorUtilities.cpp +++ b/Engine/Source/Editor/MaterialEditor/Private/MaterialEditorUtilities.cpp @@ -705,7 +705,7 @@ void FMaterialEditorUtilities::BuildTextureStreamingData(UMaterialInterface* Upd }; // Here we need a full rebuild since the shader changed. Although don't wait for previous shaders to fasten the process. - if (CompileDebugViewModeShaders(DVSM_OutputMaterialTextureScales, QualityLevel, FeatureLevel, true, false, Materials, SlowTask)) + if (CompileDebugViewModeShaders(DVSM_OutputMaterialTextureScales, QualityLevel, FeatureLevel, true, false, Materials, &SlowTask)) { FMaterialUtilities::FExportErrorManager ExportErrors(FeatureLevel); for (UMaterialInterface* MaterialInterface : Materials) diff --git a/Engine/Source/Editor/MovieSceneCaptureDialog/Private/MovieSceneCaptureDialogModule.cpp b/Engine/Source/Editor/MovieSceneCaptureDialog/Private/MovieSceneCaptureDialogModule.cpp index 5b329f6a3622..66d1ab2b1f31 100644 --- a/Engine/Source/Editor/MovieSceneCaptureDialog/Private/MovieSceneCaptureDialogModule.cpp +++ b/Engine/Source/Editor/MovieSceneCaptureDialog/Private/MovieSceneCaptureDialogModule.cpp @@ -529,6 +529,12 @@ void FInEditorCapture::OnPIEViewportStarted() CapturingFromWorld->GetWorldSettings()->DefaultGameMode = CaptureObject->Settings.GameModeOverride; } + CachedEngineShowFlags = SlatePlayInEditorSession->SlatePlayInEditorWindowViewport->GetClient()->GetEngineShowFlags(); + if (CachedEngineShowFlags && Settings.bUsePathTracer) + { + CachedPathTracingMode = CachedEngineShowFlags->PathTracing; + CachedEngineShowFlags->SetPathTracing(true); + } CaptureObject->Initialize(SlatePlayInEditorSession->SlatePlayInEditorWindowViewport, Context.PIEInstance); OnCaptureStarted(); } @@ -566,6 +572,11 @@ void FInEditorCapture::Shutdown() { CapturingFromWorld->GetWorldSettings()->DefaultGameMode = CachedGameMode; } + + if (CachedEngineShowFlags) + { + CachedEngineShowFlags->SetPathTracing(CachedPathTracingMode); + } FObjectReader(GetMutableDefault(), BackedUpPlaySettings); diff --git a/Engine/Source/Editor/MovieSceneCaptureDialog/Public/MovieSceneCaptureDialogModule.h b/Engine/Source/Editor/MovieSceneCaptureDialog/Public/MovieSceneCaptureDialogModule.h index a18aacf1f575..50aae938108c 100644 --- a/Engine/Source/Editor/MovieSceneCaptureDialog/Public/MovieSceneCaptureDialogModule.h +++ b/Engine/Source/Editor/MovieSceneCaptureDialog/Public/MovieSceneCaptureDialogModule.h @@ -130,6 +130,8 @@ private: int32 BackedUpUseFixedPoolSize; TArray BackedUpPlaySettings; + bool CachedPathTracingMode = false; + struct FEngineShowFlags* CachedEngineShowFlags = nullptr; TSubclassOf CachedGameMode; }; diff --git a/Engine/Source/Editor/MovieSceneTools/Private/AutomatedLevelSequenceCapture.cpp b/Engine/Source/Editor/MovieSceneTools/Private/AutomatedLevelSequenceCapture.cpp index be8c3bf2d581..0fa44935bde1 100644 --- a/Engine/Source/Editor/MovieSceneTools/Private/AutomatedLevelSequenceCapture.cpp +++ b/Engine/Source/Editor/MovieSceneTools/Private/AutomatedLevelSequenceCapture.cpp @@ -88,6 +88,7 @@ UAutomatedLevelSequenceCapture::UAutomatedLevelSequenceCapture(const FObjectInit WarmUpFrameCount = 0; DelayBeforeWarmUp = 0.0f; DelayBeforeShotWarmUp = 0.0f; + DelayEveryFrame = 0.0f; bWriteEditDecisionList = true; bWriteFinalCutProXML = true; @@ -158,6 +159,17 @@ void UAutomatedLevelSequenceCapture::Initialize(TSharedPtr InVie { DelayBeforeShotWarmUp = DelayBeforeShotWarmUpOverride; } + + float DelayEveryFrameOverride; + if (FParse::Value(FCommandLine::Get(), TEXT("-MovieDelayEveryFrame="), DelayEveryFrameOverride)) + { + DelayEveryFrame = DelayEveryFrameOverride; + } + } + + if (Settings.bUsePathTracer) + { + DelayEveryFrame = float(Settings.FrameRate.AsSeconds(Settings.PathTracerSamplePerPixel)); } ALevelSequenceActor* Actor = LevelSequenceActor.Get(); @@ -542,11 +554,11 @@ void UAutomatedLevelSequenceCapture::OnTick(float DeltaSeconds) StartWarmup(); - if (DelayBeforeWarmUp + DelayBeforeShotWarmUp > 0) + if (DelayBeforeWarmUp + DelayBeforeShotWarmUp + DelayEveryFrame > 0) { CaptureState = ELevelSequenceCaptureState::DelayBeforeWarmUp; - Actor->GetWorld()->GetTimerManager().SetTimer(DelayTimer, FTimerDelegate::CreateUObject(this, &UAutomatedLevelSequenceCapture::DelayBeforeWarmupFinished), DelayBeforeWarmUp + DelayBeforeShotWarmUp, false); + Actor->GetWorld()->GetTimerManager().SetTimer(DelayTimer, FTimerDelegate::CreateUObject(this, &UAutomatedLevelSequenceCapture::DelayBeforeWarmupFinished), DelayBeforeWarmUp + DelayBeforeShotWarmUp + DelayEveryFrame, false); } else { @@ -677,10 +689,14 @@ void UAutomatedLevelSequenceCapture::SequenceUpdated(const UMovieSceneSequencePl if (Actor && Actor->SequencePlayer) { // If this is a new shot, set the state to shot warm up and pause on this frame until warmed up - bool bHasMultipleShots = PreviousState.CurrentShotName != PreviousState.MasterName; - bool bNewShot = bHasMultipleShots && PreviousState.ShotID != CachedState.ShotID; - - if (bNewShot && Actor->SequencePlayer->IsPlaying() && DelayBeforeShotWarmUp > 0) + const bool bHasMultipleShots = PreviousState.CurrentShotName != PreviousState.MasterName; + const bool bNewShot = bHasMultipleShots && PreviousState.ShotID != CachedState.ShotID; + const bool bNewFrame = PreviousTime != CurrentTime; + + const bool bDelayingBeforeShotWarmUp = (bNewShot && DelayBeforeShotWarmUp > 0); + const bool bDelayingEveryFrame = (bNewFrame && DelayEveryFrame > 0); + + if (Actor->SequencePlayer->IsPlaying() && ( bDelayingBeforeShotWarmUp || bDelayingEveryFrame )) { if (bIsAudioCapturePass) { @@ -701,7 +717,7 @@ void UAutomatedLevelSequenceCapture::SequenceUpdated(const UMovieSceneSequencePl CaptureState = ELevelSequenceCaptureState::Paused; - Actor->GetWorld()->GetTimerManager().SetTimer(DelayTimer, FTimerDelegate::CreateUObject(this, &UAutomatedLevelSequenceCapture::PauseFinished), DelayBeforeShotWarmUp, false); + Actor->GetWorld()->GetTimerManager().SetTimer(DelayTimer, FTimerDelegate::CreateUObject(this, &UAutomatedLevelSequenceCapture::PauseFinished), DelayBeforeShotWarmUp + DelayEveryFrame, false); CachedPlayRate = Actor->SequencePlayer->GetPlayRate(); Actor->SequencePlayer->SetPlayRate(0.f); } diff --git a/Engine/Source/Editor/MovieSceneTools/Public/AutomatedLevelSequenceCapture.h b/Engine/Source/Editor/MovieSceneTools/Public/AutomatedLevelSequenceCapture.h index 0a729463c028..c5b3008adb96 100644 --- a/Engine/Source/Editor/MovieSceneTools/Public/AutomatedLevelSequenceCapture.h +++ b/Engine/Source/Editor/MovieSceneTools/Public/AutomatedLevelSequenceCapture.h @@ -56,7 +56,7 @@ public: float DelayBeforeWarmUp; /** The number of seconds to wait (in real-time) at shot boundaries. Useful for allowing post processing effects to settle down before capturing the animation. */ - UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category=Animation, AdvancedDisplay, meta=(Units=Seconds, ClampMin=0)) + UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category = Animation, AdvancedDisplay, meta = (Units = Seconds, ClampMin = 0)) float DelayBeforeShotWarmUp; UPROPERTY(Transient, EditAnywhere, BlueprintReadWrite, Category=CaptureSettings, AdvancedDisplay, meta=(EditInline)) @@ -167,6 +167,9 @@ private: /** The current shot movie that is rendering */ int32 ShotIndex; + /** The number of seconds to wait (in real-time) at every frame. Useful for allowing post processing effects to settle down before capturing the animation. */ + float DelayEveryFrame; + FLevelSequencePlayerSnapshot CachedState; TOptional CachedPlayRate; diff --git a/Engine/Source/Editor/Persona/Private/AnimViewportShowCommands.cpp b/Engine/Source/Editor/Persona/Private/AnimViewportShowCommands.cpp index 83975b37f2d2..b65206208b1a 100644 --- a/Engine/Source/Editor/Persona/Private/AnimViewportShowCommands.cpp +++ b/Engine/Source/Editor/Persona/Private/AnimViewportShowCommands.cpp @@ -17,6 +17,7 @@ void FAnimViewportShowCommands::RegisterCommands() UI_COMMAND( ShowBound, "Bound", "Show bound on preview mesh", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( UseInGameBound, "In-game Bound", "Use in-game bound on preview mesh when showing bounds. Otherwise bounds will always be calculated from bones alone.", EUserInterfaceActionType::ToggleButton, FInputChord()); UI_COMMAND( UseFixedBounds, "Fixed Bounds", "Preview using the 'Fixed Bounds' option, which will use bounds from skel mesh and not bones at all, for speed.", EUserInterfaceActionType::ToggleButton, FInputChord()); + UI_COMMAND( UsePreSkinnedBounds, "Pre-skinned Bounds", "Preview using the 'Pre-skinned' option, which will use local space pre-skinned(base pose) bounds from skel mesh. Overrides all other bounds preview settings.", EUserInterfaceActionType::ToggleButton, FInputChord()); UI_COMMAND( ShowPreviewMesh, "Mesh", "Show the preview mesh", EUserInterfaceActionType::ToggleButton, FInputChord() ); UI_COMMAND( ShowMorphTargets, "Morph Targets", "Display applied morph targets of the mesh", EUserInterfaceActionType::ToggleButton, FInputChord() ); diff --git a/Engine/Source/Editor/Persona/Private/AnimViewportShowCommands.h b/Engine/Source/Editor/Persona/Private/AnimViewportShowCommands.h index 36a9976f0f6a..8ebc123d3c2b 100644 --- a/Engine/Source/Editor/Persona/Private/AnimViewportShowCommands.h +++ b/Engine/Source/Editor/Persona/Private/AnimViewportShowCommands.h @@ -51,6 +51,9 @@ public: /** Use in-game Bound of preview mesh */ TSharedPtr< FUICommandInfo > UseFixedBounds; + /** Use pre-skinned Bound of preview mesh */ + TSharedPtr< FUICommandInfo > UsePreSkinnedBounds; + /** Show/hide the preview mesh */ TSharedPtr< FUICommandInfo > ShowPreviewMesh; diff --git a/Engine/Source/Editor/Persona/Private/PersonaMeshDetails.cpp b/Engine/Source/Editor/Persona/Private/PersonaMeshDetails.cpp index 2469a95803ce..b9f2725c1ff6 100644 --- a/Engine/Source/Editor/Persona/Private/PersonaMeshDetails.cpp +++ b/Engine/Source/Editor/Persona/Private/PersonaMeshDetails.cpp @@ -1055,8 +1055,8 @@ void FPersonaMeshDetails::AddLODLevelCategories(IDetailLayoutBuilder& DetailLayo .ButtonFlags(ButtonFlag) .OnApplyLODChangeClicked(this, &FPersonaMeshDetails::RegenerateLOD, LODIndex) .OnRemoveLODClicked(this, &FPersonaMeshDetails::RemoveOneLOD, LODIndex) - .OnReimportClicked(this, &FPersonaMeshDetails::OnReimportLodClicked, &DetailLayout, EReimportButtonType::Reimport, LODIndex) - .OnReimportNewFileClicked(this, &FPersonaMeshDetails::OnReimportLodClicked, &DetailLayout, EReimportButtonType::ReimportWithNewFile, LODIndex) + .OnReimportClicked(this, &FPersonaMeshDetails::OnReimportLodClicked, EReimportButtonType::Reimport, LODIndex) + .OnReimportNewFileClicked(this, &FPersonaMeshDetails::OnReimportLodClicked, EReimportButtonType::ReimportWithNewFile, LODIndex) ]; } } @@ -1897,7 +1897,7 @@ void FPersonaMeshDetails::OnSetPostProcessBlueprint(const FAssetData& AssetData, } } -FReply FPersonaMeshDetails::OnReimportLodClicked(IDetailLayoutBuilder* DetailLayout, EReimportButtonType InReimportType, int32 InLODIndex) +FReply FPersonaMeshDetails::OnReimportLodClicked(EReimportButtonType InReimportType, int32 InLODIndex) { if(USkeletalMesh* SkelMesh = GetPersonaToolkit()->GetMesh()) { @@ -1922,14 +1922,6 @@ FReply FPersonaMeshDetails::OnReimportLodClicked(IDetailLayoutBuilder* DetailLay SkelMesh->GetLODInfo(InLODIndex)->SourceImportFilename = SourceFilenameBackup; } - //Regenerate dependent LODs - RegenerateDependentLODs(InLODIndex); - - if(DetailLayout) - { - DetailLayout->ForceRefreshDetails(); - } - return FReply::Handled(); } diff --git a/Engine/Source/Editor/Persona/Private/PersonaMeshDetails.h b/Engine/Source/Editor/Persona/Private/PersonaMeshDetails.h index 7cd7a4bef446..88356da46c6d 100644 --- a/Engine/Source/Editor/Persona/Private/PersonaMeshDetails.h +++ b/Engine/Source/Editor/Persona/Private/PersonaMeshDetails.h @@ -404,7 +404,7 @@ private: }; // Handler for reimport buttons in LOD details - FReply OnReimportLodClicked(IDetailLayoutBuilder* DetailLayout, EReimportButtonType InReimportType, int32 InLODIndex); + FReply OnReimportLodClicked(EReimportButtonType InReimportType, int32 InLODIndex); void OnCopySectionList(int32 LODIndex); bool OnCanCopySectionList(int32 LODIndex) const; diff --git a/Engine/Source/Editor/Persona/Private/SAnimViewportToolBar.cpp b/Engine/Source/Editor/Persona/Private/SAnimViewportToolBar.cpp index fa1c4dd2a7fb..5e00c4b00211 100644 --- a/Engine/Source/Editor/Persona/Private/SAnimViewportToolBar.cpp +++ b/Engine/Source/Editor/Persona/Private/SAnimViewportToolBar.cpp @@ -602,6 +602,7 @@ TSharedRef SAnimViewportToolBar::GenerateCharacterMenu() const SubMenuBuilder.AddMenuEntry(FAnimViewportShowCommands::Get().ShowBound ); SubMenuBuilder.AddMenuEntry(FAnimViewportShowCommands::Get().UseInGameBound); SubMenuBuilder.AddMenuEntry(FAnimViewportShowCommands::Get().UseFixedBounds); + SubMenuBuilder.AddMenuEntry(FAnimViewportShowCommands::Get().UsePreSkinnedBounds); SubMenuBuilder.AddMenuEntry(FAnimViewportShowCommands::Get().ShowPreviewMesh ); SubMenuBuilder.AddMenuEntry(FAnimViewportShowCommands::Get().ShowMorphTargets ); } diff --git a/Engine/Source/Editor/Persona/Private/SAnimationEditorViewport.cpp b/Engine/Source/Editor/Persona/Private/SAnimationEditorViewport.cpp index dc0af8067698..7b855a7deca5 100644 --- a/Engine/Source/Editor/Persona/Private/SAnimationEditorViewport.cpp +++ b/Engine/Source/Editor/Persona/Private/SAnimationEditorViewport.cpp @@ -640,6 +640,12 @@ void SAnimationEditorViewportTabBody::BindCommands() FCanExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::CanUseFixedBounds), FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsUsingFixedBounds)); + CommandList.MapAction( + ViewportShowMenuCommands.UsePreSkinnedBounds, + FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::UsePreSkinnedBounds), + FCanExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::CanUsePreSkinnedBounds), + FIsActionChecked::CreateSP(this, &SAnimationEditorViewportTabBody::IsUsingPreSkinnedBounds)); + CommandList.MapAction( ViewportShowMenuCommands.ShowPreviewMesh, FExecuteAction::CreateSP(this, &SAnimationEditorViewportTabBody::ToggleShowPreviewMesh), @@ -1388,6 +1394,27 @@ bool SAnimationEditorViewportTabBody::IsUsingFixedBounds() const return PreviewComponent != NULL && PreviewComponent->bComponentUseFixedSkelBounds; } +void SAnimationEditorViewportTabBody::UsePreSkinnedBounds() +{ + UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); + if (PreviewComponent != NULL) + { + PreviewComponent->UsePreSkinnedBounds(!PreviewComponent->IsUsingPreSkinnedBounds()); + } +} + +bool SAnimationEditorViewportTabBody::CanUsePreSkinnedBounds() const +{ + UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); + return PreviewComponent != NULL && IsShowBoundEnabled(); +} + +bool SAnimationEditorViewportTabBody::IsUsingPreSkinnedBounds() const +{ + UDebugSkelMeshComponent* PreviewComponent = GetPreviewScene()->GetPreviewMeshComponent(); + return PreviewComponent != NULL && PreviewComponent->IsUsingPreSkinnedBounds(); +} + void SAnimationEditorViewportTabBody::HandlePreviewMeshChanged(class USkeletalMesh* OldSkeletalMesh, class USkeletalMesh* NewSkeletalMesh) { PopulateNumUVChannels(); diff --git a/Engine/Source/Editor/Persona/Private/SAnimationEditorViewport.h b/Engine/Source/Editor/Persona/Private/SAnimationEditorViewport.h index b519d90c81a0..7147c0778b64 100644 --- a/Engine/Source/Editor/Persona/Private/SAnimationEditorViewport.h +++ b/Engine/Source/Editor/Persona/Private/SAnimationEditorViewport.h @@ -415,6 +415,11 @@ private: bool CanUseFixedBounds() const; bool IsUsingFixedBounds() const; + /** Called to toggle 'pre-skinned' option to preview */ + void UsePreSkinnedBounds(); + bool CanUsePreSkinnedBounds() const; + bool IsUsingPreSkinnedBounds() const; + /** Called by UV channel combo box on selection change */ void ComboBoxSelectionChanged( TSharedPtr NewSelection, ESelectInfo::Type SelectInfo ); diff --git a/Engine/Source/Editor/PropertyEditor/Private/SDetailsViewBase.cpp b/Engine/Source/Editor/PropertyEditor/Private/SDetailsViewBase.cpp index 3b7cf91eb527..4dcdf349c061 100644 --- a/Engine/Source/Editor/PropertyEditor/Private/SDetailsViewBase.cpp +++ b/Engine/Source/Editor/PropertyEditor/Private/SDetailsViewBase.cpp @@ -327,8 +327,20 @@ void SDetailsViewBase::UpdatePropertyMaps() // We need to be able to create a new detail layout and properly clean up the old one in the process check(!LayoutData.DetailLayout.IsValid() || LayoutData.DetailLayout.IsUnique()); + // Allow customizations to perform cleanup as the delete occurs later on + for (TSharedPtr& DetailCustomization : LayoutData.CustomizationClassInstances) + { + if (DetailCustomization.IsValid()) + { + DetailCustomization->PendingDelete(); + } + } + // All the current customization instances need to be deleted when it is safe CustomizationClassInstancesPendingDelete.Append(LayoutData.CustomizationClassInstances); + + // All the current detail layouts need to be deleted when it is safe + DetailLayoutsPendingDelete.Add(LayoutData.DetailLayout); } FRootPropertyNodeList& RootPropertyNodes = GetRootNodes(); @@ -697,6 +709,9 @@ void SDetailsViewBase::Tick( const FGeometry& AllottedGeometry, const double InC // Empty all the customization instances that need to be deleted CustomizationClassInstancesPendingDelete.Empty(); + // Empty all the detail layouts that need to be deleted + DetailLayoutsPendingDelete.Empty(); + FRootPropertyNodeList& RootPropertyNodes = GetRootNodes(); for(TSharedPtr& RootPropertyNode : RootPropertyNodes) diff --git a/Engine/Source/Editor/PropertyEditor/Private/SDetailsViewBase.h b/Engine/Source/Editor/PropertyEditor/Private/SDetailsViewBase.h index d2e47f981496..0416baf0af92 100644 --- a/Engine/Source/Editor/PropertyEditor/Private/SDetailsViewBase.h +++ b/Engine/Source/Editor/PropertyEditor/Private/SDetailsViewBase.h @@ -363,6 +363,8 @@ protected: TSharedPtr SearchBox; /** Customization instances that need to be destroyed when safe to do so */ TArray< TSharedPtr > CustomizationClassInstancesPendingDelete; + /** Detail layouts that need to be destroyed when safe to do so */ + TArray< TSharedPtr > DetailLayoutsPendingDelete; /** Map of nodes that are requesting an automatic expansion/collapse due to being filtered */ TMap< TSharedRef, bool > FilteredNodesRequestingExpansionState; /** Current set of expanded detail nodes (by path) that should be saved when the details panel closes */ diff --git a/Engine/Source/Editor/PropertyEditor/Public/IDetailCustomization.h b/Engine/Source/Editor/PropertyEditor/Public/IDetailCustomization.h index dfdb4e419af7..30a8fa31ee32 100644 --- a/Engine/Source/Editor/PropertyEditor/Public/IDetailCustomization.h +++ b/Engine/Source/Editor/PropertyEditor/Public/IDetailCustomization.h @@ -13,6 +13,9 @@ class IDetailCustomization : public TSharedFromThis public: virtual ~IDetailCustomization() {} + /** Called when no longer used and will be deleted */ + virtual void PendingDelete() {} + /** Called when details should be customized */ virtual void CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) = 0; diff --git a/Engine/Source/Editor/Sequencer/Private/Sequencer.cpp b/Engine/Source/Editor/Sequencer/Private/Sequencer.cpp index b0440c85d77b..bc4802e89ca4 100644 --- a/Engine/Source/Editor/Sequencer/Private/Sequencer.cpp +++ b/Engine/Source/Editor/Sequencer/Private/Sequencer.cpp @@ -2299,6 +2299,9 @@ void FSequencer::SetLocalTimeDirectly(FFrameTime NewTime) void FSequencer::SetGlobalTime(FFrameTime NewTime) { + // Clear focus before setting time in case there's a key editor value selected that gets committed to a newly selected key on UserMovedFocus + FSlateApplication::Get().ClearKeyboardFocus(EFocusCause::Cleared); + NewTime = ConvertFrameTime(NewTime, GetRootTickResolution(), PlayPosition.GetInputRate()); if (PlayPosition.GetEvaluationType() == EMovieSceneEvaluationType::FrameLocked) { diff --git a/Engine/Source/Editor/Sequencer/Public/ISequencer.h b/Engine/Source/Editor/Sequencer/Public/ISequencer.h index 6e5fd0ffe689..73959e5b039f 100644 --- a/Engine/Source/Editor/Sequencer/Public/ISequencer.h +++ b/Engine/Source/Editor/Sequencer/Public/ISequencer.h @@ -224,9 +224,10 @@ public: * * @param Object The asset, class, or actor to add a spawnable for * @param ActorFactory Optional actor factory to use to create spawnable type + * @param bSetupDefaults Setup default tracks for this spawnable * @return The spawnable guid for the spawnable, or an invalid Guid if we were not able to create a spawnable */ - virtual FGuid MakeNewSpawnable(UObject& SourceObject, UActorFactory* ActorFactory = nullptr, bool bSetupDefaults = false) = 0; + virtual FGuid MakeNewSpawnable(UObject& SourceObject, UActorFactory* ActorFactory = nullptr, bool bSetupDefaults = true) = 0; /** * Add actors as possessable objects to sequencer. diff --git a/Engine/Source/Editor/SkeletalMeshEditor/Private/SkeletalMeshEditorMode.cpp b/Engine/Source/Editor/SkeletalMeshEditor/Private/SkeletalMeshEditorMode.cpp index 8eab31ae7a01..bddeee972fbe 100644 --- a/Engine/Source/Editor/SkeletalMeshEditor/Private/SkeletalMeshEditorMode.cpp +++ b/Engine/Source/Editor/SkeletalMeshEditor/Private/SkeletalMeshEditorMode.cpp @@ -35,7 +35,7 @@ FSkeletalMeshEditorMode::FSkeletalMeshEditorMode(TSharedRef (SkeletalMeshEditor->HandleGetAsset()), SkeletalMeshEditor->OnPostUndo)); - TabLayout = FTabManager::NewLayout("Standalone_SkeletalMeshEditor_Layout_v3.1") + TabLayout = FTabManager::NewLayout("Standalone_SkeletalMeshEditor_Layout_v3.2") ->AddArea ( FTabManager::NewPrimaryArea() @@ -57,8 +57,9 @@ FSkeletalMeshEditorMode::FSkeletalMeshEditorMode(TSharedRefSetSizeCoefficient(0.2f) ->SetHideTabWell(false) - ->AddTab(SkeletalMeshEditorTabs::SkeletonTreeTab, ETabState::ClosedTab) ->AddTab(SkeletalMeshEditorTabs::AssetDetailsTab, ETabState::OpenedTab) + ->AddTab(SkeletalMeshEditorTabs::SkeletonTreeTab, ETabState::OpenedTab) + ->SetForegroundTab(SkeletalMeshEditorTabs::AssetDetailsTab) ) ->Split ( diff --git a/Engine/Source/Editor/StatsViewer/Private/StatsPages/PrimitiveStatsPage.cpp b/Engine/Source/Editor/StatsViewer/Private/StatsPages/PrimitiveStatsPage.cpp index 6a39e9d8c792..34434295e4f1 100644 --- a/Engine/Source/Editor/StatsViewer/Private/StatsPages/PrimitiveStatsPage.cpp +++ b/Engine/Source/Editor/StatsViewer/Private/StatsPages/PrimitiveStatsPage.cpp @@ -352,7 +352,6 @@ struct PrimitiveStatsGenerator NewStatsEntry->Sections += FMath::Square(CurrentComponent->NumSubsections); // count resource usage of landscape - //TODO: take into consideration all the editing RT/heightmap, etc. bool bNotUnique = false; UniqueTextures.Add(CurrentComponent->GetHeightmap(), &bNotUnique); if (!bNotUnique) @@ -370,12 +369,14 @@ struct PrimitiveStatsGenerator } } - for (auto ItWeightmaps = CurrentComponent->WeightmapTextures.CreateConstIterator(); ItWeightmaps; ++ItWeightmaps) + const TArray& ComponentWeightmapTextures = CurrentComponent->GetWeightmapTextures(); + + for (UTexture2D* Weightmap : ComponentWeightmapTextures) { - UniqueTextures.Add((*ItWeightmaps), &bNotUnique); + UniqueTextures.Add(Weightmap, &bNotUnique); if (!bNotUnique) { - const SIZE_T WeightmapResourceSize = (*ItWeightmaps)->GetResourceSizeBytes(EResourceSizeMode::EstimatedTotal); + const SIZE_T WeightmapResourceSize = Weightmap->GetResourceSizeBytes(EResourceSizeMode::EstimatedTotal); NewStatsEntry->ResourceSize += (float)WeightmapResourceSize / 1024.f; } } diff --git a/Engine/Source/Editor/UMGEditor/Private/Customizations/DynamicEntryBoxDetails.cpp b/Engine/Source/Editor/UMGEditor/Private/Customizations/DynamicEntryBoxDetails.cpp index e36687ba49de..f1512d18e682 100644 --- a/Engine/Source/Editor/UMGEditor/Private/Customizations/DynamicEntryBoxDetails.cpp +++ b/Engine/Source/Editor/UMGEditor/Private/Customizations/DynamicEntryBoxDetails.cpp @@ -7,12 +7,16 @@ #include "Components/DynamicEntryBox.h" #include "PropertyCustomizationHelpers.h" -TSharedRef FDynamicEntryBoxDetails::MakeInstance() +////////////////////////////////////////////////////////////////////////// +// FDynamicEntryBoxBaseDetails +////////////////////////////////////////////////////////////////////////// + +TSharedRef FDynamicEntryBoxBaseDetails::MakeInstance() { - return MakeShareable(new FDynamicEntryBoxDetails()); + return MakeShareable(new FDynamicEntryBoxBaseDetails()); } -void FDynamicEntryBoxDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) +void FDynamicEntryBoxBaseDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { TArray> Objects; DetailLayout.GetObjectsBeingCustomized(Objects); @@ -29,38 +33,56 @@ void FDynamicEntryBoxDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayou IDetailCategoryBuilder& EntryLayoutCategory = DetailLayout.EditCategory(TEXT("EntryLayout")); const TAttribute CanEditAignmentAttribute(this, &FDynamicEntryBoxDetails::CanEditAlignment); - EntryLayoutCategory.AddProperty(DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UDynamicEntryBox, EntryHorizontalAlignment))) + EntryLayoutCategory.AddProperty(DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UDynamicEntryBoxBase, EntryHorizontalAlignment))) .IsEnabled(CanEditAignmentAttribute); - EntryLayoutCategory.AddProperty(DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UDynamicEntryBox, EntryVerticalAlignment))) + EntryLayoutCategory.AddProperty(DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UDynamicEntryBoxBase, EntryVerticalAlignment))) .IsEnabled(CanEditAignmentAttribute); - EntryLayoutCategory.AddProperty(DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UDynamicEntryBox, MaxElementSize))) + EntryLayoutCategory.AddProperty(DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UDynamicEntryBoxBase, MaxElementSize))) .IsEnabled(TAttribute(this, &FDynamicEntryBoxDetails::CanEditMaxElementSize)); - EntryLayoutCategory.AddProperty(DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UDynamicEntryBox, EntrySpacing))) + EntryLayoutCategory.AddProperty(DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UDynamicEntryBoxBase, EntrySpacing))) .IsEnabled(TAttribute(this, &FDynamicEntryBoxDetails::CanEditEntrySpacing)); - EntryLayoutCategory.AddProperty(DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UDynamicEntryBox, SpacingPattern))) + EntryLayoutCategory.AddProperty(DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UDynamicEntryBoxBase, SpacingPattern))) .IsEnabled(TAttribute(this, &FDynamicEntryBoxDetails::CanEditSpacingPattern)); - - AddEntryClassPicker(*EntryBox, EntryLayoutCategory, DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UDynamicEntryBox, EntryWidgetClass))); } -bool FDynamicEntryBoxDetails::CanEditSpacingPattern() const +bool FDynamicEntryBoxBaseDetails::CanEditSpacingPattern() const { return EntryBox->GetBoxType() == EDynamicBoxType::Overlay; } -bool FDynamicEntryBoxDetails::CanEditEntrySpacing() const +bool FDynamicEntryBoxBaseDetails::CanEditEntrySpacing() const { return EntryBox->SpacingPattern.Num() == 0; } -bool FDynamicEntryBoxDetails::CanEditAlignment() const +bool FDynamicEntryBoxBaseDetails::CanEditAlignment() const { return EntryBox->GetBoxType() != EDynamicBoxType::Overlay || CanEditEntrySpacing(); } -bool FDynamicEntryBoxDetails::CanEditMaxElementSize() const +bool FDynamicEntryBoxBaseDetails::CanEditMaxElementSize() const { const EDynamicBoxType BoxType = EntryBox->GetBoxType(); return BoxType == EDynamicBoxType::Horizontal || BoxType == EDynamicBoxType::Vertical; } + +////////////////////////////////////////////////////////////////////////// +// FDynamicEntryBoxDetails +////////////////////////////////////////////////////////////////////////// + +TSharedRef FDynamicEntryBoxDetails::MakeInstance() +{ + return MakeShareable(new FDynamicEntryBoxDetails()); +} + +void FDynamicEntryBoxDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) +{ + FDynamicEntryBoxBaseDetails::CustomizeDetails(DetailLayout); + + if (EntryBox.IsValid()) + { + IDetailCategoryBuilder& EntryLayoutCategory = DetailLayout.EditCategory(TEXT("EntryLayout")); + AddEntryClassPicker(*EntryBox, EntryLayoutCategory, DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UDynamicEntryBox, EntryWidgetClass))); + } +} diff --git a/Engine/Source/Editor/UMGEditor/Private/Customizations/DynamicEntryBoxDetails.h b/Engine/Source/Editor/UMGEditor/Private/Customizations/DynamicEntryBoxDetails.h index daa2c508a43b..2481b89e266d 100644 --- a/Engine/Source/Editor/UMGEditor/Private/Customizations/DynamicEntryBoxDetails.h +++ b/Engine/Source/Editor/UMGEditor/Private/Customizations/DynamicEntryBoxDetails.h @@ -4,11 +4,14 @@ #include "DynamicEntryWidgetDetailsBase.h" - class IPropertyHandle; -class UDynamicEntryBox; +class UDynamicEntryBoxBase; -class FDynamicEntryBoxDetails : public FDynamicEntryWidgetDetailsBase +////////////////////////////////////////////////////////////////////////// +// FDynamicEntryBoxBaseDetails +////////////////////////////////////////////////////////////////////////// + +class FDynamicEntryBoxBaseDetails : public FDynamicEntryWidgetDetailsBase { public: static TSharedRef MakeInstance(); @@ -16,11 +19,25 @@ public: /* Main customization of details */ virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override; +protected: + TWeakObjectPtr EntryBox; + private: bool CanEditSpacingPattern() const; bool CanEditEntrySpacing() const; bool CanEditAlignment() const; bool CanEditMaxElementSize() const; +}; - TWeakObjectPtr EntryBox; +////////////////////////////////////////////////////////////////////////// +// FDynamicEntryBoxDetails +////////////////////////////////////////////////////////////////////////// + +class FDynamicEntryBoxDetails : public FDynamicEntryBoxBaseDetails +{ +public: + static TSharedRef MakeInstance(); + + /* Main customization of details */ + virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override; }; \ No newline at end of file diff --git a/Engine/Source/Editor/UMGEditor/Private/UMGEditorModule.cpp b/Engine/Source/Editor/UMGEditor/Private/UMGEditorModule.cpp index 8853f6c9a3b2..5d6f5a3a8a6c 100644 --- a/Engine/Source/Editor/UMGEditor/Private/UMGEditorModule.cpp +++ b/Engine/Source/Editor/UMGEditor/Private/UMGEditorModule.cpp @@ -83,6 +83,7 @@ public: // Class detail customizations FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked(TEXT("PropertyEditor")); + PropertyModule.RegisterCustomClassLayout(TEXT("DynamicEntryBoxBase"), FOnGetDetailCustomizationInstance::CreateStatic(&FDynamicEntryBoxBaseDetails::MakeInstance)); PropertyModule.RegisterCustomClassLayout(TEXT("DynamicEntryBox"), FOnGetDetailCustomizationInstance::CreateStatic(&FDynamicEntryBoxDetails::MakeInstance)); PropertyModule.RegisterCustomClassLayout(TEXT("ListViewBase"), FOnGetDetailCustomizationInstance::CreateStatic(&FListViewBaseDetails::MakeInstance)); } diff --git a/Engine/Source/Editor/UMGEditor/Private/Widgets/SGraphNodeCreateWidget.cpp b/Engine/Source/Editor/UMGEditor/Private/Widgets/SGraphNodeCreateWidget.cpp index 08fc74458f36..3f4ddabeb812 100644 --- a/Engine/Source/Editor/UMGEditor/Private/Widgets/SGraphNodeCreateWidget.cpp +++ b/Engine/Source/Editor/UMGEditor/Private/Widgets/SGraphNodeCreateWidget.cpp @@ -1,104 +1,9 @@ // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "Widgets/SGraphNodeCreateWidget.h" -#include "Modules/ModuleManager.h" -#include "Widgets/SBoxPanel.h" -#include "Widgets/Layout/SBox.h" -#include "Editor.h" -#include "EdGraphSchema_K2.h" + #include "Nodes/K2Node_CreateWidget.h" -#include "KismetPins/SGraphPinObject.h" -#include "NodeFactory.h" -#include "ClassViewerModule.h" -#include "ClassViewerFilter.h" -#include "ScopedTransaction.h" -#include "Blueprint/UserWidget.h" - -#define LOCTEXT_NAMESPACE "SGraphPinUserWidgetBasedClass" - -////////////////////////////////////////////////////////////////////////// -// SGraphPinUserWidgetBasedClass - -/** - * GraphPin can select only UUserWidget classes. - * Instead of asset picker, a class viewer is used. - */ -class SGraphPinUserWidgetBasedClass : public SGraphPinObject -{ - class FUserWidgetBasedClassFilter : public IClassViewerFilter - { - public: - - virtual bool IsClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const UClass* InClass, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs ) override - { - if(NULL != InClass) - { - const bool bUserWidgetBased = InClass->IsChildOf(UUserWidget::StaticClass()); - const bool bBlueprintType = UEdGraphSchema_K2::IsAllowableBlueprintVariableType(InClass); - const bool bNotAbstract = !InClass->HasAnyClassFlags(CLASS_Abstract); - return bUserWidgetBased && bBlueprintType && bNotAbstract; - } - return false; - } - - virtual bool IsUnloadedClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const TSharedRef< const IUnloadedBlueprintData > InUnloadedClassData, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs) override - { - const bool bUserWidgetBased = InUnloadedClassData->IsChildOf(UUserWidget::StaticClass()); - const bool bNotAbstract = !InUnloadedClassData->HasAnyClassFlags(CLASS_Abstract); - return bUserWidgetBased && bNotAbstract; - } - }; - -protected: - - void OnPickedNewClass(UClass* ChosenClass) - { - if (GraphPinObj->DefaultObject != ChosenClass) - { - const FScopedTransaction Transaction(NSLOCTEXT("GraphEditor", "ChangeClassPinValue", "Change Class Pin Value")); - GraphPinObj->Modify(); - - AssetPickerAnchor->SetIsOpen(false); - GraphPinObj->GetSchema()->TrySetDefaultObject(*GraphPinObj, ChosenClass); - } - } - - virtual TSharedRef GenerateAssetPicker() override - { - FClassViewerModule& ClassViewerModule = FModuleManager::LoadModuleChecked("ClassViewer"); - - FClassViewerInitializationOptions Options; - Options.Mode = EClassViewerMode::ClassPicker; - Options.bIsActorsOnly = false; - Options.DisplayMode = EClassViewerDisplayMode::DefaultView; - Options.bShowUnloadedBlueprints = true; - Options.bShowNoneOption = false; - Options.bShowObjectRootClass = true; - TSharedPtr< FUserWidgetBasedClassFilter > Filter = MakeShareable(new FUserWidgetBasedClassFilter); - Options.ClassFilter = Filter; - - return - SNew(SBox) - .WidthOverride(280) - [ - SNew(SVerticalBox) - +SVerticalBox::Slot() - .AutoHeight() - .MaxHeight(500) - [ - SNew(SBorder) - .Padding(4) - .BorderImage( FEditorStyle::GetBrush("ToolPanel.GroupBorder") ) - [ - ClassViewerModule.CreateClassViewer(Options, FOnClassPicked::CreateSP(this, &SGraphPinUserWidgetBasedClass::OnPickedNewClass)) - ] - ] - ]; - } -}; - -////////////////////////////////////////////////////////////////////////// -// SGraphNodeCreateWidget +#include "KismetPins/SGraphPinClass.h" TSharedPtr SGraphNodeCreateWidget::CreatePinWidget(UEdGraphPin* Pin) const { @@ -106,11 +11,10 @@ TSharedPtr SGraphNodeCreateWidget::CreatePinWidget(UEdGraphPin* Pin) UEdGraphPin* ClassPin = CreateWidgetNode->GetClassPin(); if ((ClassPin == Pin) && (!ClassPin->bHidden || (ClassPin->LinkedTo.Num() > 0))) { - TSharedPtr NewPin = SNew(SGraphPinUserWidgetBasedClass, ClassPin); + TSharedPtr NewPin = SNew(SGraphPinClass, ClassPin); check(NewPin.IsValid()); + NewPin->SetAllowAbstractClasses(false); return NewPin; } return SGraphNodeK2Default::CreatePinWidget(Pin); } - -#undef LOCTEXT_NAMESPACE diff --git a/Engine/Source/Editor/UnrealEd/Classes/Animation/DebugSkelMeshComponent.h b/Engine/Source/Editor/UnrealEd/Classes/Animation/DebugSkelMeshComponent.h index 8f9c22a6c7bb..95e3dc5c5a25 100644 --- a/Engine/Source/Editor/UnrealEd/Classes/Animation/DebugSkelMeshComponent.h +++ b/Engine/Source/Editor/UnrealEd/Classes/Animation/DebugSkelMeshComponent.h @@ -269,6 +269,10 @@ class UNREALED_API UDebugSkelMeshComponent : public USkeletalMeshComponent UPROPERTY(transient) bool bIsUsingInGameBounds; + /** Does this component use pre-skinned bounds? This overrides other bounds settings */ + UPROPERTY(transient) + bool bIsUsingPreSkinnedBounds; + /** Base skel mesh has support for suspending clothing, but single ticks are more of a debug feature when stepping through an animation * So we control that using this flag */ @@ -342,6 +346,16 @@ class UNREALED_API UDebugSkelMeshComponent : public USkeletalMeshComponent */ void UseInGameBounds(bool bUseInGameBounds); + /** + * Does it use pre-skinned bounds + */ + bool IsUsingPreSkinnedBounds() const; + + /** + * Set to use pre-skinned bounds + */ + void UsePreSkinnedBounds(bool bUsePreSkinnedBounds); + /** * Test if in-game bounds are as big as preview bounds */ diff --git a/Engine/Source/Editor/UnrealEd/Classes/Commandlets/DiffAssetRegistriesCommandlet.h b/Engine/Source/Editor/UnrealEd/Classes/Commandlets/DiffAssetRegistriesCommandlet.h index 8915edf2f445..5a3258fbf305 100644 --- a/Engine/Source/Editor/UnrealEd/Classes/Commandlets/DiffAssetRegistriesCommandlet.h +++ b/Engine/Source/Editor/UnrealEd/Classes/Commandlets/DiffAssetRegistriesCommandlet.h @@ -113,6 +113,12 @@ class UDiffAssetRegistriesCommandlet : public UCommandlet } }; + struct FChunkChangeInfo + { + TSet IncludedAssets; + TMap ChangesByClass; + }; + GENERATED_UCLASS_BODY() public: @@ -138,9 +144,12 @@ private: FName GetClassName(FAssetRegistryState& InRegistryState, FName InAssetPath); + TArray GetAssetChunks(FAssetRegistryState& InRegistryState, FName InAssetPath); + bool IsInRelevantChunk(FAssetRegistryState& InRegistryState, FName InAssetPath); void LogChangedFiles(FArchive *CSVFile, const FString &OldPath, const FString &NewPath); + void LogClassSummary(FArchive *CSVFile, const FString& HeaderPrefix, const TMap& InChangeInfoByAsset, bool bDoWarnings); void SummarizeDeterminism(); void PopulateChangelistMap(const FString &Branch, const FString &CL, bool bEnginePackages); @@ -153,6 +162,8 @@ private: bool bMatchChangelists; + bool bGroupByChunk; + FString CSVFilename; // Don't report any classes of assets with less than this number of changes @@ -179,16 +190,32 @@ private: SortOrder ReportedFileOrder; - FChangeInfo ChangeSummary; - FChangeInfo NondeterministicSummary; - FChangeInfo IndirectNondeterministicSummary; - TMap ChangeSummaryByClass; - TMap ChangeInfoByAsset; - TMap ChangeSummaryByChangelist; - TMap AssetPathToClassName; - TMap AssetPathToChangelist; - TMap AssetPathFlags; + FChangeInfo ChangeSummary; + FChangeInfo NondeterministicSummary; + FChangeInfo IndirectNondeterministicSummary; + TMap ChangeSummaryByClass; + TMap ChangeInfoByAsset; + TMap ChangeSummaryByChangelist; + TMap AssetPathToClassName; + TMap AssetPathToChangelist; + TMap AssetPathFlags; + TMap ChangesByChunk; + TMultiMap ChunkIdByAssetPath; UPROPERTY(config) TArray AssetRegistrySearchPath; + + UPROPERTY(config) + FString P4Repository; + UPROPERTY(config) + FString P4EngineBasePath; + UPROPERTY(config) + FString P4EngineAssetPath; + + UPROPERTY(config) + FString P4GameBasePath; + UPROPERTY(config) + FString P4GameAssetPath; + UPROPERTY(config) + FString RegexBranchCL; }; diff --git a/Engine/Source/Editor/UnrealEd/Classes/Factories/FbxSkeletalMeshImportData.h b/Engine/Source/Editor/UnrealEd/Classes/Factories/FbxSkeletalMeshImportData.h index a379d13efd57..9c5cff4fe26a 100644 --- a/Engine/Source/Editor/UnrealEd/Classes/Factories/FbxSkeletalMeshImportData.h +++ b/Engine/Source/Editor/UnrealEd/Classes/Factories/FbxSkeletalMeshImportData.h @@ -21,26 +21,6 @@ enum EFBXImportContentType FBXICT_MAX, }; -namespace NSSkeletalMeshSourceFileLabels -{ - static FText GeoAndSkinningText() - { - static FText GeoAndSkinningText = (NSLOCTEXT("FBXReimport", "ImportContentTypeAll", "Geometry and Skinning Weights")); - return GeoAndSkinningText; - } - - static FText GeometryText() - { - static FText GeometryText = (NSLOCTEXT("FBXReimport", "ImportContentTypeGeometry", "Geometry")); - return GeometryText; - } - static FText SkinningText() - { - static FText SkinningText = (NSLOCTEXT("FBXReimport", "ImportContentTypeSkinning", "Skinning Weights")); - return SkinningText; - } -} - /** * Import data and options used when importing a static mesh from fbx */ diff --git a/Engine/Source/Editor/UnrealEd/Private/Animation/DebugSkelMeshComponent.cpp b/Engine/Source/Editor/UnrealEd/Private/Animation/DebugSkelMeshComponent.cpp index 2cd6822d37de..0c2043918549 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Animation/DebugSkelMeshComponent.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Animation/DebugSkelMeshComponent.cpp @@ -47,6 +47,12 @@ UDebugSkelMeshComponent::UDebugSkelMeshComponent(const FObjectInitializer& Objec FBoxSphereBounds UDebugSkelMeshComponent::CalcBounds(const FTransform& LocalToWorld) const { + // Override bounds with pre-skinned bounds if asking for them + if (IsUsingPreSkinnedBounds()) + { + return GetPreSkinnedLocalBounds(); + } + FBoxSphereBounds Result = Super::CalcBounds(LocalToWorld); if (!IsUsingInGameBounds()) @@ -92,6 +98,16 @@ void UDebugSkelMeshComponent::UseInGameBounds(bool bUseInGameBounds) bIsUsingInGameBounds = bUseInGameBounds; } +bool UDebugSkelMeshComponent::IsUsingPreSkinnedBounds() const +{ + return bIsUsingPreSkinnedBounds; +} + +void UDebugSkelMeshComponent::UsePreSkinnedBounds(bool bUsePreSkinnedBounds) +{ + bIsUsingPreSkinnedBounds = bUsePreSkinnedBounds; +} + bool UDebugSkelMeshComponent::CheckIfBoundsAreCorrrect() { if (GetPhysicsAsset()) diff --git a/Engine/Source/Editor/UnrealEd/Private/Commandlets/AssetRegistryGenerator.cpp b/Engine/Source/Editor/UnrealEd/Private/Commandlets/AssetRegistryGenerator.cpp index b9420629937e..8d3d9ecc087b 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Commandlets/AssetRegistryGenerator.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Commandlets/AssetRegistryGenerator.cpp @@ -83,6 +83,7 @@ FAssetRegistryGenerator::FAssetRegistryGenerator(const ITargetPlatform* InPlatfo , TargetPlatform(InPlatform) , bGenerateChunks(false) , bUseAssetManager(false) + , HighestChunkId(0) { DependencyInfo = GetMutableDefault(); @@ -101,6 +102,8 @@ FAssetRegistryGenerator::FAssetRegistryGenerator(const ITargetPlatform* InPlatfo UAssetManager::Get().UpdateManagementDatabase(); } + + InitializeChunkIdPakchunkIndexMapping(); } FAssetRegistryGenerator::~FAssetRegistryGenerator() @@ -244,36 +247,37 @@ bool FAssetRegistryGenerator::GenerateStreamingInstallManifest(int64 InExtraFlav } int32 ChunkID = UAssetManager::Get().GetContentEncryptionGroupChunkID(GroupName); - if (ChunkID >= FinalChunkManifests.Num()) + int32 PakchunkIndex = GetPakchunkIndex(ChunkID); + if (PakchunkIndex >= FinalChunkManifests.Num()) { - FinalChunkManifests.AddZeroed(ChunkID - FinalChunkManifests.Num() + 1); + FinalChunkManifests.AddZeroed(PakchunkIndex - FinalChunkManifests.Num() + 1); } - checkf(ChunkID < FinalChunkManifests.Num(), TEXT("Chunk %i out of range. %i manifests available"), ChunkID, FinalChunkManifests.Num() - 1); - checkf(FinalChunkManifests[ChunkID] == nullptr, TEXT("Manifest already exists for chunk %i"), ChunkID); - FinalChunkManifests[ChunkID] = NewManifest; + checkf(PakchunkIndex < FinalChunkManifests.Num(), TEXT("Chunk %i out of range. %i manifests available"), PakchunkIndex, FinalChunkManifests.Num() - 1); + checkf(FinalChunkManifests[PakchunkIndex] == nullptr, TEXT("Manifest already exists for chunk %i"), PakchunkIndex); + FinalChunkManifests[PakchunkIndex] = NewManifest; } } } // generate per-chunk pak list files - for (int32 Index = 0; Index < FinalChunkManifests.Num(); ++Index) + for (int32 PakchunkIndex = 0; PakchunkIndex < FinalChunkManifests.Num(); ++PakchunkIndex) { // Is this chunk empty? - if (!FinalChunkManifests[Index] || FinalChunkManifests[Index]->Num() == 0) + if (!FinalChunkManifests[PakchunkIndex] || FinalChunkManifests[PakchunkIndex]->Num() == 0) { continue; } int32 FilenameIndex = 0; TArray ChunkFilenames; - FinalChunkManifests[Index]->GenerateValueArray(ChunkFilenames); + FinalChunkManifests[PakchunkIndex]->GenerateValueArray(ChunkFilenames); int32 SubChunkIndex = 0; while ( true ) { - FString PakChunkFilename = FString::Printf(TEXT("pakchunk%d.txt"), Index); + FString PakChunkFilename = FString::Printf(TEXT("pakchunk%d.txt"), PakchunkIndex); if ( SubChunkIndex > 0 ) { - PakChunkFilename = FString::Printf(TEXT("pakchunk%d_s%d.txt"), Index, SubChunkIndex); + PakChunkFilename = FString::Printf(TEXT("pakchunk%d_s%d.txt"), PakchunkIndex, SubChunkIndex); } FString PakChunkOptions; @@ -288,7 +292,8 @@ bool FAssetRegistryGenerator::GenerateStreamingInstallManifest(int64 InExtraFlav if (bUseAssetManager) { - FGuid Guid = UAssetManager::Get().GetChunkEncryptionKeyGuid(Index); + // For encryption chunks, PakchunkIndex equals ChunkID + FGuid Guid = UAssetManager::Get().GetChunkEncryptionKeyGuid(PakchunkIndex); if (Guid.IsValid()) { PakChunkOptions += TEXT(" encryptionkeyguid=") + Guid.ToString(); @@ -296,7 +301,8 @@ bool FAssetRegistryGenerator::GenerateStreamingInstallManifest(int64 InExtraFlav // If this chunk has a seperate unique asset registry, add it to first subchunk's manifest here if (SubChunkIndex == 0) { - FName RegistryName = UAssetManager::Get().GetUniqueAssetRegistryName(Index); + // For chunks with unique asset registry name, pakchunkIndex should equal chunkid + FName RegistryName = UAssetManager::Get().GetUniqueAssetRegistryName(PakchunkIndex); if (RegistryName != NAME_None) { FString AssetRegistryFilename = FString::Printf(TEXT("%s%sAssetRegistry%s.bin"), *InSandboxFile->GetSandboxDirectory(), *InSandboxFile->GetGameSandboxDirectoryName(), *RegistryName.ToString()); @@ -307,7 +313,7 @@ bool FAssetRegistryGenerator::GenerateStreamingInstallManifest(int64 InExtraFlav } ++SubChunkIndex; - FString PakListFilename = FString::Printf(TEXT("%s/%s"), *TmpPackagingDir, *PakChunkFilename, Index); + FString PakListFilename = FString::Printf(TEXT("%s/%s"), *TmpPackagingDir, *PakChunkFilename); TUniquePtr PakListFile(IFileManager::Get().CreateFileWriter(*PakListFilename)); if (!PakListFile) @@ -357,7 +363,7 @@ bool FAssetRegistryGenerator::GenerateStreamingInstallManifest(int64 InExtraFlav PakChunkListFile->Serialize(TCHAR_TO_ANSI(*PakChunkListLine), PakChunkListLine.Len()); int32 TargetLayer = 0; - FGameDelegates::Get().GetAssignLayerChunkDelegate().ExecuteIfBound(FinalChunkManifests[Index], Platform, Index, TargetLayer); + FGameDelegates::Get().GetAssignLayerChunkDelegate().ExecuteIfBound(FinalChunkManifests[PakchunkIndex], Platform, PakchunkIndex, TargetLayer); FString LayerString = FString::Printf(TEXT("%d\r\n"), TargetLayer); @@ -529,30 +535,30 @@ bool FAssetRegistryGenerator::SaveManifests(FSandboxPlatformFile* InSandboxFile, } // Generate map for the platform abstraction - TMultiMap ChunkMap; // asset -> ChunkIDs map - TSet ChunkIDsInUse; + TMultiMap PakchunkMap; // asset -> ChunkIDs map + TSet PakchunkIndicesInUse; const FString PlatformName = TargetPlatform->PlatformName(); // Collect all unique chunk indices and map all files to their chunks - for (int32 ChunkIndex = 0; ChunkIndex < FinalChunkManifests.Num(); ++ChunkIndex) + for (int32 PakchunkIndex = 0; PakchunkIndex < FinalChunkManifests.Num(); ++PakchunkIndex) { - if (FinalChunkManifests[ChunkIndex] && FinalChunkManifests[ChunkIndex]->Num()) + if (FinalChunkManifests[PakchunkIndex] && FinalChunkManifests[PakchunkIndex]->Num()) { - ChunkIDsInUse.Add(ChunkIndex); - for (auto& Filename : *FinalChunkManifests[ChunkIndex]) + PakchunkIndicesInUse.Add(PakchunkIndex); + for (auto& Filename : *FinalChunkManifests[PakchunkIndex]) { FString PlatFilename = Filename.Value.Replace(TEXT("[Platform]"), *PlatformName); - ChunkMap.Add(PlatFilename, ChunkIndex); + PakchunkMap.Add(PlatFilename, PakchunkIndex); } } } // Sort our chunk IDs and file paths - ChunkMap.KeySort(TLess()); - ChunkIDsInUse.Sort(TLess()); + PakchunkMap.KeySort(TLess()); + PakchunkIndicesInUse.Sort(TLess()); // Platform abstraction will generate any required platform-specific files for the chunks - if (!TargetPlatform->GenerateStreamingInstallManifest(ChunkMap, ChunkIDsInUse)) + if (!TargetPlatform->GenerateStreamingInstallManifest(PakchunkMap, PakchunkIndicesInUse)) { return false; } @@ -936,9 +942,9 @@ bool FAssetRegistryGenerator::SaveAssetRegistry(const FString& SandboxPath, bool // Pass over all chunks and build a mapping of chunk index to asset registry name. All chunks that don't have a unique registry are assigned to the "generic bucket" // which will be written to the master asset registry in chunk 0 - for (int32 ChunkID = 0; ChunkID < FinalChunkManifests.Num(); ++ChunkID) + for (int32 PakchunkIndex = 0; PakchunkIndex < FinalChunkManifests.Num(); ++PakchunkIndex) { - FChunkPackageSet* Manifest = FinalChunkManifests[ChunkID]; + FChunkPackageSet* Manifest = FinalChunkManifests[PakchunkIndex]; if (Manifest == nullptr) { continue; @@ -948,18 +954,19 @@ bool FAssetRegistryGenerator::SaveAssetRegistry(const FString& SandboxPath, bool if (bUseAssetManager) { - FName RegistryName = UAssetManager::Get().GetUniqueAssetRegistryName(ChunkID); + // For chunks with unique asset registry name, pakchunkIndex should equal chunkid + FName RegistryName = UAssetManager::Get().GetUniqueAssetRegistryName(PakchunkIndex); if (RegistryName != NAME_None) { - ChunkBuckets.FindOrAdd(ChunkID).Add(ChunkID); - ChunkBucketNames.FindOrAdd(ChunkID) = RegistryName.ToString(); + ChunkBuckets.FindOrAdd(PakchunkIndex).Add(PakchunkIndex); + ChunkBucketNames.FindOrAdd(PakchunkIndex) = RegistryName.ToString(); bAddToGenericBucket = false; } } if (bAddToGenericBucket) { - ChunkBuckets.FindOrAdd(GenericChunkBucket).Add(ChunkID); + ChunkBuckets.FindOrAdd(GenericChunkBucket).Add(PakchunkIndex); } } @@ -1194,7 +1201,7 @@ bool FAssetRegistryGenerator::GenerateAssetChunkInformationCSV(const FString& Ou return A.ObjectPath < B.ObjectPath; }); - for (int32 ChunkID = 0, ChunkNum = FinalChunkManifests.Num(); ChunkID < ChunkNum; ++ChunkID) + for (int32 PakchunkIndex = 0; PakchunkIndex < FinalChunkManifests.Num(); ++PakchunkIndex) { FString PerChunkManifestCSV = HeaderText; for (const FAssetData* AssetDataPtr : AssetDataList) @@ -1204,19 +1211,19 @@ bool FAssetRegistryGenerator::GenerateAssetChunkInformationCSV(const FString& Ou if (AssetData.ChunkIDs.Num() > 0) { const FAssetPackageData* PackageData = State.GetAssetPackageData(AssetData.PackageName); - if (AssetData.ChunkIDs.Contains(ChunkID) && PackageData && PackageData->DiskSize >= 0) + if (AssetData.ChunkIDs.Contains(PakchunkIndex) && PackageData && PackageData->DiskSize >= 0) { int64 FileSize = PackageData->DiskSize; FString SoftChain; bool bHardChunk = false; - if (ChunkID < ChunkManifests.Num()) + if (PakchunkIndex < ChunkManifests.Num()) { - bHardChunk = ChunkManifests[ChunkID] && ChunkManifests[ChunkID]->Contains(AssetData.PackageName); + bHardChunk = ChunkManifests[PakchunkIndex] && ChunkManifests[PakchunkIndex]->Contains(AssetData.PackageName); if (!bHardChunk) { // - SoftChain = GetShortestReferenceChain(AssetData.PackageName, ChunkID); + SoftChain = GetShortestReferenceChain(AssetData.PackageName, PakchunkIndex); } } if (SoftChain.IsEmpty()) @@ -1224,7 +1231,7 @@ bool FAssetRegistryGenerator::GenerateAssetChunkInformationCSV(const FString& Ou SoftChain = TEXT("Soft: Possibly Unassigned Asset"); } - TmpString = FString::Printf(TEXT("%d,%s,%s,%s,%lld,"), ChunkID, *AssetData.PackageName.ToString(), *AssetData.AssetClass.ToString(), bHardChunk ? TEXT("Hard") : *SoftChain, FileSize); + TmpString = FString::Printf(TEXT("%d,%s,%s,%s,%lld,"), PakchunkIndex, *AssetData.PackageName.ToString(), *AssetData.AssetClass.ToString(), bHardChunk ? TEXT("Hard") : *SoftChain, FileSize); CSVString += TmpString; PerChunkManifestCSV += TmpString; if (AssetData.ChunkIDs.Num() == 1) @@ -1236,7 +1243,7 @@ bool FAssetRegistryGenerator::GenerateAssetChunkInformationCSV(const FString& Ou { for (const auto& OtherChunk : AssetData.ChunkIDs) { - if (OtherChunk != ChunkID) + if (OtherChunk != PakchunkIndex) { TmpString = FString::Printf(TEXT("%d "), OtherChunk); CSVString += TmpString; @@ -1252,7 +1259,7 @@ bool FAssetRegistryGenerator::GenerateAssetChunkInformationCSV(const FString& Ou if (bWriteIndividualFiles) { - FFileHelper::SaveStringToFile(PerChunkManifestCSV, *FPaths::Combine(*OutputPath, *FString::Printf(TEXT("Chunks%dInfo.csv"), ChunkID))); + FFileHelper::SaveStringToFile(PerChunkManifestCSV, *FPaths::Combine(*OutputPath, *FString::Printf(TEXT("Chunks%dInfo.csv"), PakchunkIndex))); } } @@ -1261,15 +1268,18 @@ bool FAssetRegistryGenerator::GenerateAssetChunkInformationCSV(const FString& Ou void FAssetRegistryGenerator::AddPackageToManifest(const FString& PackageSandboxPath, FName PackageName, int32 ChunkId) { - while (ChunkId >= ChunkManifests.Num()) + HighestChunkId = ChunkId > HighestChunkId ? ChunkId : HighestChunkId; + int32 PakchunkIndex = GetPakchunkIndex(ChunkId); + + while (PakchunkIndex >= ChunkManifests.Num()) { ChunkManifests.Add(nullptr); } - if (!ChunkManifests[ChunkId]) + if (!ChunkManifests[PakchunkIndex]) { - ChunkManifests[ChunkId] = new FChunkPackageSet(); + ChunkManifests[PakchunkIndex] = new FChunkPackageSet(); } - ChunkManifests[ChunkId]->Add(PackageName, PackageSandboxPath); + ChunkManifests[PakchunkIndex]->Add(PackageName, PackageSandboxPath); //Safety check, it the package happens to exist in the unassigned list remove it now. UnassignedPackageSet.Remove(PackageName); } @@ -1277,9 +1287,11 @@ void FAssetRegistryGenerator::AddPackageToManifest(const FString& PackageSandbox void FAssetRegistryGenerator::RemovePackageFromManifest(FName PackageName, int32 ChunkId) { - if (ChunkManifests[ChunkId]) + int32 PakchunkIndex = GetPakchunkIndex(ChunkId); + + if (ChunkManifests[PakchunkIndex]) { - ChunkManifests[ChunkId]->Remove(PackageName); + ChunkManifests[PakchunkIndex]->Remove(PackageName); } } @@ -1350,9 +1362,9 @@ bool FAssetRegistryGenerator::CheckChunkAssetsAreNotInChild(const FChunkDependen return true; } -void FAssetRegistryGenerator::AddPackageAndDependenciesToChunk(FChunkPackageSet* ThisPackageSet, FName InPkgName, const FString& InSandboxFile, int32 ChunkID, FSandboxPlatformFile* SandboxPlatformFile) +void FAssetRegistryGenerator::AddPackageAndDependenciesToChunk(FChunkPackageSet* ThisPackageSet, FName InPkgName, const FString& InSandboxFile, int32 PakchunkIndex, FSandboxPlatformFile* SandboxPlatformFile) { - FChunkPackageSet* InitialPackageSetForThisChunk = ChunkManifests.IsValidIndex(ChunkID) ? ChunkManifests[ChunkID] : nullptr; + FChunkPackageSet* InitialPackageSetForThisChunk = ChunkManifests.IsValidIndex(PakchunkIndex) ? ChunkManifests[PakchunkIndex] : nullptr; //Add this asset ThisPackageSet->Add(InPkgName, InSandboxFile); @@ -1370,7 +1382,7 @@ void FAssetRegistryGenerator::AddPackageAndDependenciesToChunk(FChunkPackageSet* for (const auto& PkgName : DependentPackageNames) { bool bSkip = false; - if (ChunkID != 0 && FinalChunkManifests[0]) + if (PakchunkIndex != 0 && FinalChunkManifests[0]) { // Do not add if this asset was assigned to the 0 chunk. These assets always exist on disk bSkip = FinalChunkManifests[0]->Contains(PkgName); @@ -1394,7 +1406,7 @@ void FAssetRegistryGenerator::AddPackageAndDependenciesToChunk(FChunkPackageSet* if (UE_LOG_ACTIVE(LogAssetRegistryGenerator, Verbose)) { // It was not assigned to this chunk and we're forcing it to be dragged in, let the user known - UE_LOG(LogAssetRegistryGenerator, Verbose, TEXT("Adding %s to chunk %i because %s depends on it."), *FilteredPackageName.ToString(), ChunkID, *InPkgName.ToString()); + UE_LOG(LogAssetRegistryGenerator, Verbose, TEXT("Adding %s to chunk %i because %s depends on it."), *FilteredPackageName.ToString(), PakchunkIndex, *InPkgName.ToString()); TSet VisitedPackages; TArray DependencyChain; @@ -1425,21 +1437,27 @@ void FAssetRegistryGenerator::FixupPackageDependenciesForChunks(FSandboxPlatform } FinalChunkManifests.Empty(); - for (int32 ChunkID = 0, MaxChunk = ChunkManifests.Num(); ChunkID < MaxChunk; ++ChunkID) + for (int32 PakchunkIndex = 0, MaxPakchunk = ChunkManifests.Num(); PakchunkIndex < MaxPakchunk; ++PakchunkIndex) { FinalChunkManifests.Add(nullptr); - if (!ChunkManifests[ChunkID]) + if (!ChunkManifests[PakchunkIndex]) { continue; } - FinalChunkManifests[ChunkID] = new FChunkPackageSet(); - for (auto It = ChunkManifests[ChunkID]->CreateConstIterator(); It; ++It) + FinalChunkManifests[PakchunkIndex] = new FChunkPackageSet(); + for (auto It = ChunkManifests[PakchunkIndex]->CreateConstIterator(); It; ++It) { - AddPackageAndDependenciesToChunk(FinalChunkManifests[ChunkID], It.Key(), It.Value(), ChunkID, InSandboxFile); + AddPackageAndDependenciesToChunk(FinalChunkManifests[PakchunkIndex], It.Key(), It.Value(), PakchunkIndex, InSandboxFile); } } - const FChunkDependencyTreeNode* ChunkDepGraph = DependencyInfo->GetOrBuildChunkDependencyGraph(ChunkManifests.Num() - 1); + FConfigFile PlatformIniFile; + FConfigCacheIni::LoadLocalIniFile(PlatformIniFile, TEXT("Engine"), true, *TargetPlatform->IniPlatformName()); + bool bSkipResolveChunkDependencyGraph = false; + PlatformIniFile.GetBool(TEXT("Script/UnrealEd.ChunkDependencyInfo"), TEXT("bSkipResolveChunkDependencyGraph"), bSkipResolveChunkDependencyGraph); + + const FChunkDependencyTreeNode* ChunkDepGraph = DependencyInfo->GetOrBuildChunkDependencyGraph(!bSkipResolveChunkDependencyGraph ? HighestChunkId : 0); + //Once complete, Add any remaining assets (that are not assigned to a chunk) to the first chunk. if (FinalChunkManifests.Num() == 0) { @@ -1467,52 +1485,51 @@ void FAssetRegistryGenerator::FixupPackageDependenciesForChunks(FSandboxPlatform //Finally, if the previous step may added any extra packages to the 0 chunk. Pull them out of other chunks and save space ResolveChunkDependencyGraph(*ChunkDepGraph, FChunkPackageSet(), PackagesRemovedFromChunks); - for (int32 i = 0; i < ChunkManifests.Num(); ++i) + for (int32 PakchunkIndex = 0; PakchunkIndex < ChunkManifests.Num(); ++PakchunkIndex) { if (!bUseAssetManager) { - FName CollectionName(*FString::Printf(TEXT("PackagesRemovedFromChunk%i"), i)); + FName CollectionName(*FString::Printf(TEXT("PackagesRemovedFromChunk%i"), PakchunkIndex)); if (CreateOrEmptyCollection(CollectionName)) { - WriteCollection(CollectionName, PackagesRemovedFromChunks[i]); + WriteCollection(CollectionName, PackagesRemovedFromChunks[PakchunkIndex]); } } } - for (int32 ChunkID = 0, MaxChunk = ChunkManifests.Num(); ChunkID < MaxChunk; ++ChunkID) + for (int32 PakchunkIndex = 0, MaxPakchunk = ChunkManifests.Num(); PakchunkIndex < MaxPakchunk; ++PakchunkIndex) { - const int32 ChunkManifestNum = ChunkManifests[ChunkID] ? ChunkManifests[ChunkID]->Num() : 0; - const int32 FinalChunkManifestNum = FinalChunkManifests[ChunkID] ? FinalChunkManifests[ChunkID]->Num() : 0; - UE_LOG(LogAssetRegistryGenerator, Log, TEXT("Chunk: %i, Started with %i packages, Final after dependency resolve: %i"), ChunkID, ChunkManifestNum, FinalChunkManifestNum); + const int32 ChunkManifestNum = ChunkManifests[PakchunkIndex] ? ChunkManifests[PakchunkIndex]->Num() : 0; + const int32 FinalChunkManifestNum = FinalChunkManifests[PakchunkIndex] ? FinalChunkManifests[PakchunkIndex]->Num() : 0; + UE_LOG(LogAssetRegistryGenerator, Log, TEXT("Chunk: %i, Started with %i packages, Final after dependency resolve: %i"), PakchunkIndex, ChunkManifestNum, FinalChunkManifestNum); } // Fix up the asset registry to reflect this chunk layout - for (int32 ChunkID = 0, MaxChunk = FinalChunkManifests.Num(); ChunkID < MaxChunk; ++ChunkID) + for (int32 PakchunkIndex = 0 ; PakchunkIndex < FinalChunkManifests.Num(); ++PakchunkIndex) { - if (!FinalChunkManifests[ChunkID]) + if (PakchunkIndex >= FinalChunkManifests.Num() || !FinalChunkManifests[PakchunkIndex]) { continue; } - for (const TPair& Asset : *FinalChunkManifests[ChunkID]) + for (const TPair& Asset : *FinalChunkManifests[PakchunkIndex]) { const TArray AssetIndexArray = State.GetAssetsByPackageName(Asset.Key); for (const FAssetData* AssetData : AssetIndexArray) { // Chunk Ids are safe to modify in place - const_cast(AssetData)->ChunkIDs.AddUnique(ChunkID); + const_cast(AssetData)->ChunkIDs.AddUnique(PakchunkIndex); } } } } - -void FAssetRegistryGenerator::FindShortestReferenceChain(TArray PackageNames, int32 ChunkID, uint32& OutParentIndex, FString& OutChainPath) +void FAssetRegistryGenerator::FindShortestReferenceChain(TArray PackageNames, int32 PakchunkIndex, uint32& OutParentIndex, FString& OutChainPath) { TArray ReferencesToCheck; uint32 Index = 0; for (const auto& Pkg : PackageNames) { - if (ChunkManifests[ChunkID] && ChunkManifests[ChunkID]->Contains(Pkg.PackageName)) + if (ChunkManifests[PakchunkIndex] && ChunkManifests[PakchunkIndex]->Contains(Pkg.PackageName)) { OutChainPath += TEXT("Soft: "); OutChainPath += Pkg.PackageName.ToString(); @@ -1536,7 +1553,7 @@ void FAssetRegistryGenerator::FindShortestReferenceChain(TArray if (ReferencesToCheck.Num() > 0) { uint32 ParentIndex = INDEX_NONE; - FindShortestReferenceChain(ReferencesToCheck, ChunkID, ParentIndex, OutChainPath); + FindShortestReferenceChain(ReferencesToCheck, PakchunkIndex, ParentIndex, OutChainPath); if (ParentIndex < (uint32)PackageNames.Num()) { @@ -1554,7 +1571,7 @@ void FAssetRegistryGenerator::FindShortestReferenceChain(TArray } } -FString FAssetRegistryGenerator::GetShortestReferenceChain(FName PackageName, int32 ChunkID) +FString FAssetRegistryGenerator::GetShortestReferenceChain(FName PackageName, int32 PakchunkIndex) { FString StringChain; TArray ReferencesToCheck; @@ -1562,7 +1579,7 @@ FString FAssetRegistryGenerator::GetShortestReferenceChain(FName PackageName, in ReferencesToCheck.Add(FReferencePair(PackageName, 0)); InspectedNames.Empty(); InspectedNames.Add(PackageName); - FindShortestReferenceChain(ReferencesToCheck, ChunkID, ParentIndex, StringChain); + FindShortestReferenceChain(ReferencesToCheck, PakchunkIndex, ParentIndex, StringChain); return StringChain; } @@ -1614,4 +1631,38 @@ void FAssetRegistryGenerator::WriteCollection(FName CollectionName, const TArray } } +int32 FAssetRegistryGenerator::GetPakchunkIndex(int32 ChunkId) +{ + if (ChunkIdPakchunkIndexMapping.Contains(ChunkId)) + { + int32 NewChunkId = ChunkIdPakchunkIndexMapping[ChunkId]; + check(NewChunkId >= 0); + return NewChunkId; + } + + return ChunkId; +} + +void FAssetRegistryGenerator::InitializeChunkIdPakchunkIndexMapping() +{ + FConfigFile PlatformIniFile; + FConfigCacheIni::LoadLocalIniFile(PlatformIniFile, TEXT("Game"), true, *TargetPlatform->IniPlatformName()); + TArray ChunkMapping; + PlatformIniFile.GetArray(TEXT("/Script/UnrealEd.ProjectPackagingSettings"), TEXT("ChunkIdPakchunkIndexMapping"), ChunkMapping); + + FPlatformMisc::ParseChunkIdPakchunkIndexMapping(ChunkMapping, ChunkIdPakchunkIndexMapping); + + // Validate ChunkIdPakchunkIndexMapping + TArray AllChunkIDs; + ChunkIdPakchunkIndexMapping.GetKeys(AllChunkIDs); + for (int32 ChunkID : AllChunkIDs) + { + if(UAssetManager::Get().GetChunkEncryptionKeyGuid(ChunkID).IsValid() + || UAssetManager::Get().GetUniqueAssetRegistryName(ChunkID) != NAME_None) + { + UE_LOG(LogAssetRegistryGenerator, Error, TEXT("Chunks with encryption key guid or unique assetregistry name (Chunk %d) can not be mapped with ChunkIdPakchunkIndexMapping. Mapping is removed."), ChunkID); + ChunkIdPakchunkIndexMapping.Remove(ChunkID); + } + } +} #undef LOCTEXT_NAMESPACE diff --git a/Engine/Source/Editor/UnrealEd/Private/Commandlets/DiffAssetRegistriesCommandlet.cpp b/Engine/Source/Editor/UnrealEd/Private/Commandlets/DiffAssetRegistriesCommandlet.cpp index 637685028ef3..de77b2705d5f 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Commandlets/DiffAssetRegistriesCommandlet.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Commandlets/DiffAssetRegistriesCommandlet.cpp @@ -64,9 +64,9 @@ void UDiffAssetRegistriesCommandlet::PopulateChangelistMap(const FString &Branch // skip the game packages if we're doing engine packages only if (!bEnginePackages) { - FillChangelists(Branch, CLRange, TEXT("/FortniteGame/Content/"), TEXT("/Game/")); + FillChangelists(Branch, CLRange, P4GameBasePath, P4GameAssetPath); } - FillChangelists(Branch, CLRange, TEXT("/Engine/Content/"), TEXT("/Engine/")); + FillChangelists(Branch, CLRange, P4EngineBasePath, P4EngineAssetPath); } // save out the new table @@ -194,15 +194,6 @@ int32 UDiffAssetRegistriesCommandlet::Main(const FString& FullCommandLine) if (NewPathVal) { FindAssetRegistryPath(*NewPathVal, NewPath); - - const FString FortniteText = TEXT("++Fortnite+"); - - if (NewPathVal->StartsWith(*FortniteText)) - { - // might be able to figure out branch / cl from this - FString TempBranchCL = NewPathVal->Right(NewPathVal->Len() - FortniteText.Len()); - TempBranchCL.Split(TEXT("-CL-"), &Branch, &CL); - } } bMatchChangelists = false; @@ -274,7 +265,7 @@ void UDiffAssetRegistriesCommandlet::FillChangelists(FString Branch, FString CL, { TArray Results; int32 ReturnCode = 0; - if (LaunchP4(TEXT("files ") + FString(TEXT("//Fortnite/")) + Branch + BasePath + TEXT("....uasset@") + CL, Results, ReturnCode)) + if (LaunchP4(TEXT("files ") + P4Repository + Branch + BasePath + TEXT("....uasset@") + CL, Results, ReturnCode)) { if (ReturnCode == 0) { @@ -312,7 +303,7 @@ void UDiffAssetRegistriesCommandlet::FillChangelists(FString Branch, FString CL, } } } - if (LaunchP4(TEXT("files ") + FString(TEXT("//Fortnite/")) + Branch + BasePath + TEXT("....umap@") + CL, Results, ReturnCode)) + if (LaunchP4(TEXT("files ") + P4Repository + Branch + BasePath + TEXT("....umap@") + CL, Results, ReturnCode)) { if (ReturnCode == 0) { @@ -720,6 +711,16 @@ void UDiffAssetRegistriesCommandlet::LogChangedFiles(FArchive *CSVFile, FString if (CSVFile) { + CSVFile->Logf(TEXT("Type Key")); + CSVFile->Logf(TEXT("a, file added")); + CSVFile->Logf(TEXT("r, file removed")); + CSVFile->Logf(TEXT("e, explicit edit (this file specifically has been modified)")); + CSVFile->Logf(TEXT("d, dependency edit (this file is different likely because a dependency has also been changed)")); + CSVFile->Logf(TEXT("n, indirect non deterministic (a dependency file changed but wasn't changed directly (Indicates the dependency was either non determinisitc or another indirect non deterministic file))")); + CSVFile->Logf(TEXT("c, non deterministic (the hashes for all dependencies are the same but this file is not)")); + CSVFile->Logf(TEXT("x, no binary change (shouldn't ever happen)")); + CSVFile->Logf(TEXT("")); + CSVFile->Logf(TEXT("Modification,Name,Class,NewSize,OldSize,Changelist")); UE_LOG(LogDiffAssets, Display, TEXT("Saving CSV results to %s"), *CSVFilename); diff --git a/Engine/Source/Editor/UnrealEd/Private/Commandlets/PackageUtilities.cpp b/Engine/Source/Editor/UnrealEd/Private/Commandlets/PackageUtilities.cpp index e24d9226536e..2e3b5e1eb112 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Commandlets/PackageUtilities.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Commandlets/PackageUtilities.cpp @@ -1553,7 +1553,7 @@ int32 UPkgInfoCommandlet::Main( const FString& Params ) if (!bDumpProperties) { TGuardValue GuardAllowUnversionedContentInEditor(GAllowUnversionedContentInEditor, true); - TRefCountPtr LoadContext(new FUObjectSerializeContext()); + TRefCountPtr LoadContext(FUObjectThreadContext::Get().GetSerializeContext()); BeginLoad(LoadContext); Linker = CreateLinkerForFilename(LoadContext, Filename); EndLoad(Linker->GetSerializeContext()); diff --git a/Engine/Source/Editor/UnrealEd/Private/Editor.cpp b/Engine/Source/Editor/UnrealEd/Private/Editor.cpp index 6941a5de359f..38151e6c7cfe 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Editor.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Editor.cpp @@ -276,9 +276,10 @@ bool FReimportManager::Reimport( UObject* Obj, bool bAskForNewFileIfMissing, boo if(CanReimportHandler != nullptr) { TArray MissingFileIndex; + // Check all filenames for missing files bool bMissingFiles = false; - if (SourceFilenames.Num() > 0) + if (!bForceNewFile && SourceFilenames.Num() > 0) { for (int32 FileIndex = 0; FileIndex < SourceFilenames.Num(); ++FileIndex) { @@ -294,7 +295,22 @@ bool FReimportManager::Reimport( UObject* Obj, bool bAskForNewFileIfMissing, boo } else { - MissingFileIndex.AddUnique(SourceFileIndex == INDEX_NONE ? 0 : SourceFileIndex); + int32 RealSourceFileIndex = SourceFileIndex == INDEX_NONE ? 0 : SourceFileIndex; + if (bForceNewFile) + { + if (SourceFilenames.IsValidIndex(RealSourceFileIndex)) + { + SourceFilenames[RealSourceFileIndex].Empty(); + } + else + { + //Add the missing entries + SourceFilenames.AddDefaulted(RealSourceFileIndex - (SourceFilenames.Num() - 1)); + } + bAskForNewFileIfMissing = true; + } + + MissingFileIndex.AddUnique(RealSourceFileIndex); bMissingFiles = true; } diff --git a/Engine/Source/Editor/UnrealEd/Private/EditorBuildUtils.cpp b/Engine/Source/Editor/UnrealEd/Private/EditorBuildUtils.cpp index fadf4216981e..a504cc3b9a3e 100644 --- a/Engine/Source/Editor/UnrealEd/Private/EditorBuildUtils.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/EditorBuildUtils.cpp @@ -1103,14 +1103,14 @@ bool FEditorBuildUtils::EditorBuildTextureStreaming(UWorld* InWorld, EViewModeIn if (bNeedsMaterialData) { TSet Materials; - if (!GetUsedMaterialsInWorld(InWorld, Materials, BuildTextureStreamingTask)) + if (!GetUsedMaterialsInWorld(InWorld, Materials, &BuildTextureStreamingTask)) { return false; } if (Materials.Num()) { - if (!CompileDebugViewModeShaders(DVSM_OutputMaterialTextureScales, QualityLevel, FeatureLevel, SelectedViewMode == VMI_Unknown, true, Materials, BuildTextureStreamingTask)) + if (!CompileDebugViewModeShaders(DVSM_OutputMaterialTextureScales, QualityLevel, FeatureLevel, SelectedViewMode == VMI_Unknown, true, Materials, &BuildTextureStreamingTask)) { return false; } @@ -1228,7 +1228,7 @@ bool FEditorBuildUtils::EditorBuildMaterialTextureStreamingData(UPackage* Packag const float OneOverNumMaterials = 1.f / FMath::Max(1.f, (float)Materials.Num()); bool bAnyPackagesDirtied = false; - if (CompileDebugViewModeShaders(DVSM_OutputMaterialTextureScales, QualityLevel, FeatureLevel, true, true, Materials, SlowTask)) + if (CompileDebugViewModeShaders(DVSM_OutputMaterialTextureScales, QualityLevel, FeatureLevel, true, true, Materials, &SlowTask)) { FMaterialUtilities::FExportErrorManager ExportErrors(FeatureLevel); for (UMaterialInterface* MaterialInterface : Materials) @@ -1304,7 +1304,7 @@ bool FEditorBuildUtils::CompileViewModeShaders(UWorld* InWorld, EViewModeIndex S CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS); TSet Materials; - if (!GetUsedMaterialsInWorld(InWorld, Materials, CompileShaderTask)) + if (!GetUsedMaterialsInWorld(InWorld, Materials, &CompileShaderTask)) { return false; } @@ -1321,7 +1321,7 @@ bool FEditorBuildUtils::CompileViewModeShaders(UWorld* InWorld, EViewModeIndex S } else if (SelectedViewMode == VMI_RequiredTextureResolution)*/ { - if (!CompileDebugViewModeShaders(DebugViewMode, QualityLevel, FeatureLevel, false, true, Materials, CompileShaderTask)) + if (!CompileDebugViewModeShaders(DebugViewMode, QualityLevel, FeatureLevel, false, true, Materials, &CompileShaderTask)) { return false; } @@ -1360,7 +1360,7 @@ bool FEditorBuildUtils::CompileShadersComplexityViewMode(EMaterialQualityLevel:: check(Materials.Num()); // Finish compiling pending shaders first. - if (!WaitForShaderCompilation(LOCTEXT("CompileShaders_Complexity_FinishPendingShadersCompilation", "Waiting For Pending Shaders Compilation"), ProgressTask)) + if (!WaitForShaderCompilation(LOCTEXT("CompileShaders_Complexity_FinishPendingShadersCompilation", "Waiting For Pending Shaders Compilation"), &ProgressTask)) { return false; } @@ -1391,7 +1391,7 @@ bool FEditorBuildUtils::CompileShadersComplexityViewMode(EMaterialQualityLevel:: } // wait for compilation to be done and copy the number of instruction from the compiled shaders to the emulated shader set - if (WaitForShaderCompilation(LOCTEXT("OfflineShaderCompilation", "Offline Shader Compilation"), ProgressTask)) + if (WaitForShaderCompilation(LOCTEXT("OfflineShaderCompilation", "Offline Shader Compilation"), &ProgressTask)) { FSuspendRenderingThread SuspendObject(false); diff --git a/Engine/Source/Editor/UnrealEd/Private/EditorEngine.cpp b/Engine/Source/Editor/UnrealEd/Private/EditorEngine.cpp index 7707c66adcfc..14a76bd767c2 100644 --- a/Engine/Source/Editor/UnrealEd/Private/EditorEngine.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/EditorEngine.cpp @@ -2346,14 +2346,7 @@ void UEditorEngine::CloseEditedWorldAssets(UWorld* InWorld) if (AssetWorld && ClosingWorlds.Contains(AssetWorld)) { - const TArray AssetEditors = EditorManager.FindEditorsForAsset(Asset); - for (IAssetEditorInstance* EditorInstance : AssetEditors ) - { - if (EditorInstance != NULL) - { - EditorInstance->CloseWindow(); - } - } + EditorManager.CloseAllEditorsForAsset(Asset); } } } diff --git a/Engine/Source/Editor/UnrealEd/Private/EditorExporters.cpp b/Engine/Source/Editor/UnrealEd/Private/EditorExporters.cpp index 8163af2e072f..d1185843c736 100644 --- a/Engine/Source/Editor/UnrealEd/Private/EditorExporters.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/EditorExporters.cpp @@ -853,13 +853,17 @@ static void AddActorToOBJs(AActor* Actor, TArray& Objects, TSetNumSubsections; int32 ChannelOffsets[4] = {(int32)STRUCT_OFFSET(FColor,R),(int32)STRUCT_OFFSET(FColor,G),(int32)STRUCT_OFFSET(FColor,B),(int32)STRUCT_OFFSET(FColor,A)}; - for( int32 AllocIdx=0;AllocIdx < Component->WeightmapLayerAllocations.Num(); AllocIdx++ ) + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(); + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(); + + for( int32 AllocIdx=0;AllocIdx < ComponentWeightmapLayerAllocations.Num(); AllocIdx++ ) { - FWeightmapLayerAllocationInfo& AllocInfo = Component->WeightmapLayerAllocations[AllocIdx]; + FWeightmapLayerAllocationInfo& AllocInfo = ComponentWeightmapLayerAllocations[AllocIdx]; if( AllocInfo.LayerInfo == ALandscapeProxy::VisibilityLayer ) { TexIndex = AllocInfo.WeightmapTextureIndex; - Component->WeightmapTextures[TexIndex]->Source.GetMipData(RawVisData, 0); + + ComponentWeightmapTextures[TexIndex]->Source.GetMipData(RawVisData, 0); VisDataMap = RawVisData.GetData() + ChannelOffsets[AllocInfo.WeightmapTextureChannel]; } } diff --git a/Engine/Source/Editor/UnrealEd/Private/EditorModeManager.cpp b/Engine/Source/Editor/UnrealEd/Private/EditorModeManager.cpp index 9f59f4840468..c55cbd1f666c 100644 --- a/Engine/Source/Editor/UnrealEd/Private/EditorModeManager.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/EditorModeManager.cpp @@ -924,6 +924,36 @@ bool FEditorModeTools::GetCursor(EMouseCursor::Type& OutCursor) const return bHandled; } +bool FEditorModeTools::GetOverrideCursorVisibility(bool& bWantsOverride, bool& bHardwareCursorVisible, bool bSoftwareCursorVisible) const +{ + bool bHandled = false; + for (const auto& Mode : Modes) + { + bHandled |= Mode->GetOverrideCursorVisibility(bWantsOverride, bHardwareCursorVisible, bSoftwareCursorVisible); + } + return bHandled; +} + +bool FEditorModeTools::PreConvertMouseMovement(FEditorViewportClient* InViewportClient) +{ + bool bHandled = false; + for (const auto& Mode : Modes) + { + bHandled |= Mode->PreConvertMouseMovement(InViewportClient); + } + return bHandled; +} + +bool FEditorModeTools::PostConvertMouseMovement(FEditorViewportClient* InViewportClient) +{ + bool bHandled = false; + for (const auto& Mode : Modes) + { + bHandled |= Mode->PostConvertMouseMovement(InViewportClient); + } + return bHandled; +} + /** * Used to cycle widget modes */ diff --git a/Engine/Source/Editor/UnrealEd/Private/EditorModeRegistry.cpp b/Engine/Source/Editor/UnrealEd/Private/EditorModeRegistry.cpp index ee82f6f1c346..d34fdfc8026c 100644 --- a/Engine/Source/Editor/UnrealEd/Private/EditorModeRegistry.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/EditorModeRegistry.cpp @@ -145,8 +145,9 @@ void FEditorModeRegistry::RegisterMode(FEditorModeID ModeID, TSharedRef 0) + { + OnModeUnregisteredEvent.Broadcast(ModeID); + RegisteredModesChanged.Broadcast(); + } } diff --git a/Engine/Source/Editor/UnrealEd/Private/EditorServer.cpp b/Engine/Source/Editor/UnrealEd/Private/EditorServer.cpp index 47b7adbf1b16..7ea369774042 100644 --- a/Engine/Source/Editor/UnrealEd/Private/EditorServer.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/EditorServer.cpp @@ -2075,6 +2075,14 @@ void UEditorEngine::EditorDestroyWorld( FWorldContext & Context, const FText& Cl } FEditorSupportDelegates::PrepareToCleanseEditorObject.Broadcast(ContextWorld); + for (ULevel* Level : ContextWorld->GetLevels()) + { + UWorld* LevelWorld = Level->GetTypedOuter(); + if (ensureAlways(LevelWorld) && LevelWorld != ContextWorld && LevelWorld != NewWorld) + { + FEditorSupportDelegates::PrepareToCleanseEditorObject.Broadcast(LevelWorld); + } + } ContextWorld->DestroyWorld( true, NewWorld ); Context.SetCurrentWorld(NULL); diff --git a/Engine/Source/Editor/UnrealEd/Private/EditorViewportClient.cpp b/Engine/Source/Editor/UnrealEd/Private/EditorViewportClient.cpp index 1e2c223d7888..011d2fb7b919 100644 --- a/Engine/Source/Editor/UnrealEd/Private/EditorViewportClient.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/EditorViewportClient.cpp @@ -2088,6 +2088,10 @@ void FEditorViewportClient::UpdateMouseDelta() const bool bIsUsingTrackpad = FSlateApplication::Get().IsUsingTrackpad(); const bool bIsNonOrbitMiddleMouse = MiddleMouseButtonDown && !IsAltPressed(); + // If a tool is overriding current widget mode behavior, it may need to + // temporarily set a different widget mode while converting mouse movement. + ModeTools->PreConvertMouseMovement(this); + // Convert the movement delta into drag/rotation deltas FVector Drag; FRotator Rot; @@ -2124,6 +2128,8 @@ void FEditorViewportClient::UpdateMouseDelta() } } + ModeTools->PostConvertMouseMovement(this); + const bool bInputHandledByGizmos = InputWidgetDelta( Viewport, CurrentAxis, Drag, Rot, Scale ); if( !Rot.IsZero() ) @@ -3628,14 +3634,12 @@ void FEditorViewportClient::Draw(FViewport* InViewport, FCanvas* Canvas) ViewFamily.EngineShowFlags = EngineShowFlags; - if (GIsEditor && World && ViewFamily.GetDebugViewShaderMode() != DVSM_None && HasMissingDebugViewModeShaders(true)) + if (World && ViewFamily.GetDebugViewShaderMode() != DVSM_None && HasMissingDebugViewModeShaders(true)) { - FScopedSlowTask CompileShaderTask(3.f, LOCTEXT("CompileMissingViewModeShaders", "Compiling Missing ViewMode Shaders")); // { Get Used Materials, Sync Pending Shader, Wait for Compilation } - // CompileShaderTask.MakeDialog(true); TSet Materials; - if (GetUsedMaterialsInWorld(World, Materials, CompileShaderTask)) + if (GetUsedMaterialsInWorld(World, Materials, nullptr)) { - CompileDebugViewModeShaders(ViewFamily.GetDebugViewShaderMode(), GetCachedScalabilityCVars().MaterialQualityLevel, ViewFamily.GetFeatureLevel(), false, false, Materials, CompileShaderTask); + CompileDebugViewModeShaders(ViewFamily.GetDebugViewShaderMode(), GetCachedScalabilityCVars().MaterialQualityLevel, ViewFamily.GetFeatureLevel(), false, false, Materials, nullptr); } } @@ -4580,6 +4584,18 @@ void FEditorViewportClient::UpdateRequiredCursorVisibility() bool ShiftDown = IsShiftPressed(); bool ControlDown = IsCtrlPressed(); + bool bOverrideCursorVisibility = false; + bool bHardwareCursorVisible = false; + bool bSoftwareCursorVisible = false; + if (ModeTools->GetOverrideCursorVisibility(bOverrideCursorVisibility, bHardwareCursorVisible, bSoftwareCursorVisible)) + { + if (bOverrideCursorVisibility) + { + SetRequiredCursor(bHardwareCursorVisible, bSoftwareCursorVisible); + return; + } + } + if (GetViewportType() == LVT_None) { SetRequiredCursor(true, false); @@ -4680,6 +4696,11 @@ void FEditorViewportClient::SetRequiredCursorOverride( bool WantOverride, EMouse RequiredCursorVisibiltyAndAppearance.RequiredCursor = RequiredCursor; } +void FEditorViewportClient::SetWidgetModeOverride(FWidget::EWidgetMode InWidgetMode) +{ + ModeTools->SetWidgetModeOverride(InWidgetMode); +} + EAxisList::Type FEditorViewportClient::GetCurrentWidgetAxis() const { return Widget->GetCurrentAxis(); diff --git a/Engine/Source/Editor/UnrealEd/Private/Factories/EditorFactories.cpp b/Engine/Source/Editor/UnrealEd/Private/Factories/EditorFactories.cpp index 3bf3d83adebf..1f198f11ea22 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Factories/EditorFactories.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Factories/EditorFactories.cpp @@ -5632,7 +5632,17 @@ void UReimportFbxSkeletalMeshFactory::SetReimportPaths( UObject* Obj, const FStr { SkeletalMesh->Modify(); UFbxSkeletalMeshImportData* ImportData = UFbxSkeletalMeshImportData::GetImportDataForSkeletalMesh(SkeletalMesh, ImportUI->SkeletalMeshImportData); - ImportData->UpdateFilenameOnly(NewReimportPath, SourceFileIndex); + int32 RealSourceFileIndex = SourceFileIndex == INDEX_NONE ? 0 : SourceFileIndex; + if (RealSourceFileIndex < ImportData->GetSourceFileCount()) + { + ImportData->UpdateFilenameOnly(NewReimportPath, SourceFileIndex); + } + else + { + //Create a source file entry, this case happen when user import a specific content for the first time + FString SourceIndexLabel = USkeletalMesh::GetSourceFileLabelFromIndex(RealSourceFileIndex).ToString(); + ImportData->AddFileName(NewReimportPath, RealSourceFileIndex, SourceIndexLabel); + } } } @@ -5727,9 +5737,9 @@ EReimportResult::Type UReimportFbxSkeletalMeshFactory::Reimport( UObject* Obj, i AbsoluteFilenames.Reset(); ImportDataPtr->ExtractFilenames(AbsoluteFilenames); //Set both geo and skinning filepath. Reuse existing file path if possible. Use the first filename(geo and skin) if it has to be create. - FString FilenameToAdd = SourceIndex == 1 ? OutFilename : AbsoluteFilenames.Num() > SourceIndex ? AbsoluteFilenames[1] : AbsoluteFilenames[0]; + FString FilenameToAdd = SourceIndex == 1 ? OutFilename : AbsoluteFilenames.IsValidIndex(1) ? AbsoluteFilenames[1] : AbsoluteFilenames[0]; ImportDataPtr->AddFileName(FilenameToAdd, 1, NSSkeletalMeshSourceFileLabels::GeometryText().ToString()); - FilenameToAdd = SourceIndex == 2 ? OutFilename : AbsoluteFilenames.Num() > SourceIndex ? AbsoluteFilenames[2] : AbsoluteFilenames[0]; + FilenameToAdd = SourceIndex == 2 ? OutFilename : AbsoluteFilenames.IsValidIndex(2) ? AbsoluteFilenames[2] : AbsoluteFilenames[0]; ImportDataPtr->AddFileName(FilenameToAdd, 2, NSSkeletalMeshSourceFileLabels::SkinningText().ToString()); } return true; diff --git a/Engine/Source/Editor/UnrealEd/Private/Factories/SkeletalMeshImport.cpp b/Engine/Source/Editor/UnrealEd/Private/Factories/SkeletalMeshImport.cpp index 6b6bf02bb2c9..5db815a92042 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Factories/SkeletalMeshImport.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Factories/SkeletalMeshImport.cpp @@ -542,6 +542,33 @@ ExistingSkelMeshData* SaveExistingSkelMeshData(USkeletalMesh* ExistingSkelMesh, return ExistingMeshDataPtr; } +void RegenerateDependentLODs(USkeletalMesh* SkelMesh, int32 LODIndex) +{ + check(SkelMesh); + + IMeshReductionModule& ReductionModule = FModuleManager::Get().LoadModuleChecked("MeshReductionInterface"); + IMeshReduction* MeshReduction = ReductionModule.GetSkeletalMeshReductionInterface(); + if (MeshReduction && MeshReduction->IsSupported()) + { + FSkeletalMeshUpdateContext UpdateContext; + UpdateContext.SkeletalMesh = SkelMesh; + TArray DependentLODs; + DependentLODs.AddZeroed(SkelMesh->GetLODNum()); + DependentLODs[LODIndex] = true; + for (int32 CurrentLODIndex = LODIndex + 1; CurrentLODIndex < DependentLODs.Num(); ++CurrentLODIndex) + { + FSkeletalMeshLODInfo& CurrentLODInfo = *(SkelMesh->GetLODInfo(CurrentLODIndex)); + FSkeletalMeshOptimizationSettings& Settings = CurrentLODInfo.ReductionSettings; + if (CurrentLODInfo.bHasBeenSimplified && DependentLODs[Settings.BaseLOD]) + { + DependentLODs[CurrentLODIndex] = true; + //Regenerate this LOD + FLODUtilities::SimplifySkeletalMeshLOD(UpdateContext, CurrentLODIndex, false); + } + } + } +} + void TryRegenerateLODs(ExistingSkelMeshData* MeshData, USkeletalMesh* SkeletalMesh) { check(SkeletalMesh != nullptr); @@ -816,12 +843,16 @@ void RestoreExistingSkelMeshData(ExistingSkelMeshData* MeshData, USkeletalMesh* { SkeletalMeshImportedModel->OriginalReductionSourceMeshData[ReimportLODIndex]->EmptyBulkData(); } - //Regenerate the reimport LOD - GWarn->BeginSlowTask(LOCTEXT("RegenReimportedLOD", "Generating reimported LOD"), true); - FSkeletalMeshUpdateContext UpdateContext; - UpdateContext.SkeletalMesh = SkeletalMesh; - FLODUtilities::SimplifySkeletalMeshLOD(UpdateContext, ReimportLODIndex, false); - GWarn->EndSlowTask(); + + if (SkeletalMesh->IsReductionActive(ReimportLODIndex)) + { + //Regenerate the reimport LOD + GWarn->BeginSlowTask(LOCTEXT("RegenReimportedLOD", "Generating reimported LOD"), true); + FSkeletalMeshUpdateContext UpdateContext; + UpdateContext.SkeletalMesh = SkeletalMesh; + FLODUtilities::SimplifySkeletalMeshLOD(UpdateContext, ReimportLODIndex, false); + GWarn->EndSlowTask(); + } } //Do everything we need for base LOD re-import @@ -1028,6 +1059,10 @@ void RestoreExistingSkelMeshData(ExistingSkelMeshData* MeshData, USkeletalMesh* SkeletalMesh->SetSamplingInfo(MeshData->ExistingSamplingInfo); } + else + { + RegenerateDependentLODs(SkeletalMesh, ReimportLODIndex); + } //Restore the section change only for the reimport LOD, other LOD are not affected since the material array can only grow. if (MeshData->UseMaterialNameSlotWorkflow) diff --git a/Engine/Source/Editor/UnrealEd/Private/Fbx/FbxMainExport.cpp b/Engine/Source/Editor/UnrealEd/Private/Fbx/FbxMainExport.cpp index b3298849854c..9410b7c76091 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Fbx/FbxMainExport.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Fbx/FbxMainExport.cpp @@ -4272,10 +4272,11 @@ void FFbxExporter::ExportLandscapeToFbx(ALandscapeProxy* Landscape, const TCHAR* FLandscapeComponentDataInterface CDI(Component, Landscape->ExportLOD); const int32 BaseVertIndex = SelectedComponentIndex++ * VertexCountPerComponent; + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(); TArray CompVisData; - for (int32 AllocIdx = 0; AllocIdx < Component->WeightmapLayerAllocations.Num(); AllocIdx++) + for (int32 AllocIdx = 0; AllocIdx < ComponentWeightmapLayerAllocations.Num(); AllocIdx++) { - FWeightmapLayerAllocationInfo& AllocInfo = Component->WeightmapLayerAllocations[AllocIdx]; + FWeightmapLayerAllocationInfo& AllocInfo = ComponentWeightmapLayerAllocations[AllocIdx]; if (AllocInfo.LayerInfo == ALandscapeProxy::VisibilityLayer) { CDI.GetWeightmapTextureData(AllocInfo.LayerInfo, CompVisData); diff --git a/Engine/Source/Editor/UnrealEd/Private/Fbx/FbxSceneImportFactory.cpp b/Engine/Source/Editor/UnrealEd/Private/Fbx/FbxSceneImportFactory.cpp index c1fdbf6c51b8..b29026048aaf 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Fbx/FbxSceneImportFactory.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Fbx/FbxSceneImportFactory.cpp @@ -134,18 +134,21 @@ bool GetFbxSceneImportOptions(UnFbx::FFbxImporter* FbxImporter GlobalImportSettings->OverrideMaterials.Reset(); - TSharedPtr ParentWindow; - if (FModuleManager::Get().IsModuleLoaded("MainFrame")) + // Don't show the import options in unattended mode + if (!GIsRunningUnattendedScript) { - IMainFrameModule& MainFrame = FModuleManager::LoadModuleChecked("MainFrame"); - ParentWindow = MainFrame.GetParentWindow(); - } - TSharedRef Window = SNew(SWindow) - .ClientSize(FVector2D(820.f, 650.f)) - .Title(NSLOCTEXT("UnrealEd", "FBXSceneImportOpionsTitle", "FBX Scene Import Options")); - TSharedPtr FbxSceneOptionWindow; + TSharedPtr ParentWindow; + if (FModuleManager::Get().IsModuleLoaded("MainFrame")) + { + IMainFrameModule& MainFrame = FModuleManager::LoadModuleChecked("MainFrame"); + ParentWindow = MainFrame.GetParentWindow(); + } + TSharedRef Window = SNew(SWindow) + .ClientSize(FVector2D(820.f, 650.f)) + .Title(NSLOCTEXT("UnrealEd", "FBXSceneImportOpionsTitle", "FBX Scene Import Options")); + TSharedPtr FbxSceneOptionWindow; - Window->SetContent + Window->SetContent ( SAssignNew(FbxSceneOptionWindow, SFbxSceneOptionWindow) .SceneInfo(SceneInfoPtr) @@ -156,13 +159,14 @@ bool GetFbxSceneImportOptions(UnFbx::FFbxImporter* FbxImporter .SceneImportOptionsSkeletalMeshDisplay(SkeletalMeshImportData) .OwnerWindow(Window) .FullPath(Path) - ); + ); - FSlateApplication::Get().AddModalWindow(Window, ParentWindow, false); + FSlateApplication::Get().AddModalWindow(Window, ParentWindow, false); - if (!FbxSceneOptionWindow->ShouldImport()) - { - return false; + if (!FbxSceneOptionWindow->ShouldImport()) + { + return false; + } } //setup all options diff --git a/Engine/Source/Editor/UnrealEd/Private/Kismet2/BlueprintEditorUtils.cpp b/Engine/Source/Editor/UnrealEd/Private/Kismet2/BlueprintEditorUtils.cpp index 0d700c0efe54..eb5dd2437729 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Kismet2/BlueprintEditorUtils.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Kismet2/BlueprintEditorUtils.cpp @@ -9174,7 +9174,7 @@ void FBlueprintEditorUtils::BuildComponentInstancingData(UActorComponent* Compon FComponentInstancingDataUtils::RecursivePropertyGather(ComponentTemplateClass, (uint8*)ComponentTemplate, (uint8*)ComponentDefaults, OutData); // Flag that cooked data has been built and is now considered to be valid. - OutData.bIsValid = true; + OutData.bHasValidCookedData = true; } } diff --git a/Engine/Source/Editor/UnrealEd/Private/Kismet2/Kismet2.cpp b/Engine/Source/Editor/UnrealEd/Private/Kismet2/Kismet2.cpp index 5cbd2d78fc15..3992d849d44a 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Kismet2/Kismet2.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Kismet2/Kismet2.cpp @@ -1399,6 +1399,10 @@ void FKismetEditorUtilities::AddComponentsToBlueprint(UBlueprint* Blueprint, TAr TArray SceneComponentNodes; SceneComponentNodes.Reserve(Components.Num()); + // Array of the valid ActorComponents + TArray ActorComponents; + ActorComponents.Reserve(Components.Num()); + // The boolean indicate if the scene component was added to the SceneComponentNodes array TMap SceneComponentsMap; SceneComponentsMap.Reserve(Components.Num()); @@ -1407,23 +1411,17 @@ void FKismetEditorUtilities::AddComponentsToBlueprint(UBlueprint* Blueprint, TAr { UActorComponent* ActorComponent = Components[CompIndex]; - // Clear out nulls and the components we won't be able to create. - if (ActorComponent == nullptr || !ActorComponent->GetClass()->HasMetaData(FBlueprintMetadata::MD_BlueprintSpawnableComponent)) - { - Components.RemoveAtSwap(CompIndex, 1, false); - // We don't want to skip the component swapped in - CompIndex--; - } - else + // Filter out nulls and the components we won't be able to create. + if (ActorComponent && ActorComponent->GetClass()->HasMetaData(FBlueprintMetadata::MD_BlueprintSpawnableComponent)) { USceneComponent* SceneComponent = Cast(Components[CompIndex]); - // Remove the scene components from the array as they will be added in the array SceneComponentNodes if (SceneComponent) { SceneComponentsMap.Add(SceneComponent, false); - Components.RemoveAtSwap(CompIndex, 1, false); - // We don't want to skip the component swapped in - CompIndex--; + } + else + { + ActorComponents.Add(ActorComponent); } } } @@ -1465,9 +1463,9 @@ void FKismetEditorUtilities::AddComponentsToBlueprint(UBlueprint* Blueprint, TAr } // The easy part to add the non-scene components. - for (int32 CompIndex = 0; CompIndex < Components.Num(); ++CompIndex) + for (int32 CompIndex = 0; CompIndex < ActorComponents.Num(); ++CompIndex) { - UActorComponent* ActorComponent = Components[CompIndex]; + UActorComponent* ActorComponent = ActorComponents[CompIndex]; AActor* Actor = ActorComponent->GetOwner(); check(Actor); diff --git a/Engine/Source/Editor/UnrealEd/Private/Kismet2/KismetDebugUtilities.cpp b/Engine/Source/Editor/UnrealEd/Private/Kismet2/KismetDebugUtilities.cpp index 06345d49c5ba..c6213e6aa7ae 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Kismet2/KismetDebugUtilities.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Kismet2/KismetDebugUtilities.cpp @@ -1113,7 +1113,8 @@ FKismetDebugUtilities::EWatchTextResult FKismetDebugUtilities::GetWatchText(FStr void* DataPtr = nullptr; void* DeltaPtr = nullptr; UObject* ParentObj = nullptr; - FKismetDebugUtilities::EWatchTextResult Result = FindDebuggingData(Blueprint, ActiveObject, WatchPin, PropertyToDebug, DataPtr, DeltaPtr, ParentObj); + TArray SeenObjects; + FKismetDebugUtilities::EWatchTextResult Result = FindDebuggingData(Blueprint, ActiveObject, WatchPin, PropertyToDebug, DataPtr, DeltaPtr, ParentObj, SeenObjects); if (Result == FKismetDebugUtilities::EWatchTextResult::EWTR_Valid) { @@ -1129,7 +1130,8 @@ FKismetDebugUtilities::EWatchTextResult FKismetDebugUtilities::GetDebugInfo(FDeb void* DataPtr = nullptr; void* DeltaPtr = nullptr; UObject* ParentObj = nullptr; - FKismetDebugUtilities::EWatchTextResult Result = FindDebuggingData(Blueprint, ActiveObject, WatchPin, PropertyToDebug, DataPtr, DeltaPtr, ParentObj); + TArray SeenObjects; + FKismetDebugUtilities::EWatchTextResult Result = FindDebuggingData(Blueprint, ActiveObject, WatchPin, PropertyToDebug, DataPtr, DeltaPtr, ParentObj, SeenObjects); if (Result == FKismetDebugUtilities::EWatchTextResult::EWTR_Valid) { @@ -1139,7 +1141,7 @@ FKismetDebugUtilities::EWatchTextResult FKismetDebugUtilities::GetDebugInfo(FDeb return Result; } -FKismetDebugUtilities::EWatchTextResult FKismetDebugUtilities::FindDebuggingData(UBlueprint* Blueprint, UObject* ActiveObject, const UEdGraphPin* WatchPin, UProperty*& OutProperty, void*& OutData, void*& OutDelta, UObject*& OutParent) +FKismetDebugUtilities::EWatchTextResult FKismetDebugUtilities::FindDebuggingData(UBlueprint* Blueprint, UObject* ActiveObject, const UEdGraphPin* WatchPin, UProperty*& OutProperty, void*& OutData, void*& OutDelta, UObject*& OutParent, TArray& SeenObjects) { FKismetDebugUtilitiesData& Data = FKismetDebugUtilitiesData::Get(); @@ -1267,7 +1269,8 @@ FKismetDebugUtilities::EWatchTextResult FKismetDebugUtilities::FindDebuggingData void* SelfPinData = nullptr; void* SelfPinDelta = nullptr; UObject* SelfPinParent = nullptr; - FKismetDebugUtilities::EWatchTextResult Result = FindDebuggingData(Blueprint, ActiveObject, SelfPin, SelfPinProperty, SelfPinData, SelfPinDelta, SelfPinParent); + SeenObjects.AddUnique(ActiveObject); + FKismetDebugUtilities::EWatchTextResult Result = FindDebuggingData(Blueprint, ActiveObject, SelfPin, SelfPinProperty, SelfPinData, SelfPinDelta, SelfPinParent, SeenObjects); UObjectPropertyBase* SelfPinPropertyBase = Cast(SelfPinProperty); if (Result == EWTR_Valid && SelfPinPropertyBase != nullptr) { @@ -1275,7 +1278,10 @@ FKismetDebugUtilities::EWatchTextResult FKismetDebugUtilities::FindDebuggingData UObject* TempActiveObject = SelfPinPropertyBase->GetObjectPropertyValue(PropertyValue); if (TempActiveObject && TempActiveObject != ActiveObject) { - return FindDebuggingData(Blueprint, TempActiveObject, WatchPin, OutProperty, OutData, OutDelta, OutParent); + if (!SeenObjects.Contains(TempActiveObject)) + { + return FindDebuggingData(Blueprint, TempActiveObject, WatchPin, OutProperty, OutData, OutDelta, OutParent, SeenObjects); + } } } } diff --git a/Engine/Source/Editor/UnrealEd/Private/LevelEditorViewport.cpp b/Engine/Source/Editor/UnrealEd/Private/LevelEditorViewport.cpp index d961788177d3..fbc7791da18d 100644 --- a/Engine/Source/Editor/UnrealEd/Private/LevelEditorViewport.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/LevelEditorViewport.cpp @@ -1636,6 +1636,7 @@ FLevelEditorViewportClient::FLevelEditorViewportClient(const TSharedPtrGetEditorWorldContext()); @@ -2507,8 +2508,10 @@ bool FLevelEditorViewportClient::InputWidgetDelta(FViewport* InViewport, EAxisLi if( IsShiftPressed() ) { + bApplyCameraSpeedScaleByDistance = false; FVector CameraDelta( Drag ); MoveViewportCamera( CameraDelta, FRotator::ZeroRotator ); + bApplyCameraSpeedScaleByDistance = true; } TArray ActiveModes; @@ -2528,6 +2531,11 @@ bool FLevelEditorViewportClient::InputWidgetDelta(FViewport* InViewport, EAxisLi return bHandled; } +bool FLevelEditorViewportClient::ShouldScaleCameraSpeedByDistance() const +{ + return bApplyCameraSpeedScaleByDistance && FEditorViewportClient::ShouldScaleCameraSpeedByDistance(); +} + TSharedPtr FLevelEditorViewportClient::MakeDragTool( EDragTool::Type DragToolType ) { // Let the drag tool handle the transaction diff --git a/Engine/Source/Editor/UnrealEd/Private/Lightmass/LightmassLandscapeRender.cpp b/Engine/Source/Editor/UnrealEd/Private/Lightmass/LightmassLandscapeRender.cpp index fbfdb4ecab3b..3b0a6686068c 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Lightmass/LightmassLandscapeRender.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Lightmass/LightmassLandscapeRender.cpp @@ -195,7 +195,9 @@ void GetLandscapeOpacityData(const FLandscapeStaticLightingMesh* LandscapeMesh, ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentX, ComponentY)); if (Component) { - if (Component->WeightmapLayerAllocations.ContainsByPredicate([](const FWeightmapLayerAllocationInfo& Allocation) { return Allocation.LayerInfo == ALandscapeProxy::VisibilityLayer; })) + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(); + + if (ComponentWeightmapLayerAllocations.ContainsByPredicate([](const FWeightmapLayerAllocationInfo& Allocation) { return Allocation.LayerInfo == ALandscapeProxy::VisibilityLayer; })) { // filled by GetWeightDataFast() } diff --git a/Engine/Source/Editor/UnrealEd/Private/Lightmass/LightmassRender.cpp b/Engine/Source/Editor/UnrealEd/Private/Lightmass/LightmassRender.cpp index 5aa9f7e2fa33..604179786d07 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Lightmass/LightmassRender.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Lightmass/LightmassRender.cpp @@ -116,6 +116,11 @@ struct FLightmassMaterialCompiler : public FProxyMaterialCompiler return Compiler->Constant3(0,0,0); } + virtual int32 PreSkinnedLocalBounds() override + { + return Compiler->Constant3(0, 0, 0); + } + virtual int32 DistanceCullFade() override { return Compiler->Constant(1.0f); diff --git a/Engine/Source/Editor/UnrealEd/Private/PlayLevel.cpp b/Engine/Source/Editor/UnrealEd/Private/PlayLevel.cpp index a996e53ba2b7..ef91a5fb6990 100644 --- a/Engine/Source/Editor/UnrealEd/Private/PlayLevel.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/PlayLevel.cpp @@ -578,7 +578,7 @@ void UEditorEngine::TeardownPlaySession(FWorldContext& PieWorldContext) { check(PieWorldContext.WorldType == EWorldType::PIE); PlayWorld = PieWorldContext.World(); - PlayWorld->bIsTearingDown = true; + PlayWorld->BeginTearingDown(); if (!PieWorldContext.RunAsDedicated) { diff --git a/Engine/Source/Editor/UnrealEd/Private/Toolkits/AssetEditorManager.cpp b/Engine/Source/Editor/UnrealEd/Private/Toolkits/AssetEditorManager.cpp index 1824830d3609..007c21923c21 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Toolkits/AssetEditorManager.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Toolkits/AssetEditorManager.cpp @@ -108,7 +108,7 @@ void FAssetEditorManager::AddReferencedObjects( FReferenceCollector& Collector ) } // If the pointer got deleted or swapped out due to hot reload. - for ( auto& Entry : ModifiedKeys ) + for ( TPair& Entry : ModifiedKeys ) { if ( Entry.Value ) { @@ -167,9 +167,12 @@ int32 FAssetEditorManager::CloseAllEditorsForAsset(UObject* Asset) { TArray EditorInstances = FindEditorsForAsset(Asset); - for( auto EditorIter : EditorInstances ) + for (IAssetEditorInstance* EditorInstance : EditorInstances) { - EditorIter->CloseWindow(); + if (EditorInstance) + { + EditorInstance->CloseWindow(); + } } AssetEditorRequestCloseEvent.Broadcast(Asset, EAssetEditorCloseReason::CloseAllEditorsForAsset); @@ -181,9 +184,12 @@ void FAssetEditorManager::RemoveAssetFromAllEditors(UObject* Asset) { TArray EditorInstances = FindEditorsForAsset(Asset); - for (auto EditorIter : EditorInstances) + for (IAssetEditorInstance* EditorIter : EditorInstances) { - EditorIter->RemoveEditingAsset(Asset); + if (EditorIter) + { + EditorIter->RemoveEditingAsset(Asset); + } } AssetEditorRequestCloseEvent.Broadcast(Asset, EAssetEditorCloseReason::RemoveAssetFromAllEditors); @@ -342,7 +348,7 @@ bool FAssetEditorManager::OpenEditorForAsset(UObject* Asset, const EToolkitMode: TWeakPtr AssetTypeActions = AssetToolsModule.Get().GetAssetTypeActionsForClass( Asset->GetClass() ); - auto ActualToolkitMode = ToolkitMode; + EToolkitMode::Type ActualToolkitMode = ToolkitMode; if( AssetTypeActions.IsValid() ) { if( AssetTypeActions.Pin()->ShouldForceWorldCentric() ) @@ -413,7 +419,7 @@ bool FAssetEditorManager::OpenEditorForAsset(UObject* Asset, const EToolkitMode: } -bool FAssetEditorManager::OpenEditorForAssets( const TArray< UObject* >& Assets, const EToolkitMode::Type ToolkitMode, TSharedPtr< IToolkitHost > OpenedFromLevelEditor ) +bool FAssetEditorManager::OpenEditorForAssets( const TArray & Assets, const EToolkitMode::Type ToolkitMode, TSharedPtr< IToolkitHost > OpenedFromLevelEditor ) { if( Assets.Num() == 1 ) { @@ -422,7 +428,7 @@ bool FAssetEditorManager::OpenEditorForAssets( const TArray< UObject* >& Assets, else if (Assets.Num() > 0) { TArray SkipOpenAssets; - for (auto Asset : Assets) + for (UObject* Asset : Assets) { // If any of the assets are already open or the package is cooked, // remove them from the list of assets to open an editor for @@ -435,7 +441,7 @@ bool FAssetEditorManager::OpenEditorForAssets( const TArray< UObject* >& Assets, // Verify that all the assets are of the same class bool bAssetClassesMatch = true; - auto AssetClass = Assets[0]->GetClass(); + UClass* AssetClass = Assets[0]->GetClass(); for (int32 i = 1; i < Assets.Num(); i++) { if (Assets[i]->GetClass() != AssetClass) @@ -456,7 +462,7 @@ bool FAssetEditorManager::OpenEditorForAssets( const TArray< UObject* >& Assets, GWarn->BeginSlowTask(LOCTEXT("OpenEditors", "Opening Editor(s)..."), true); // Determine the appropriate toolkit mode for the asset type - auto ActualToolkitMode = ToolkitMode; + EToolkitMode::Type ActualToolkitMode = ToolkitMode; if (AssetTypeActions.Pin()->ShouldForceWorldCentric()) { // This asset type prefers a specific toolkit mode @@ -488,7 +494,7 @@ bool FAssetEditorManager::OpenEditorForAssets( const TArray< UObject* >& Assets, TArray AssetInfoList; AssetInfoList.Reserve(Assets.Num()); - for (auto Asset : Assets) + for (UObject* Asset : Assets) { AssetInfoList.Add(FLocalAssetInfo(Asset, Asset->GetPathName())); } @@ -499,8 +505,8 @@ bool FAssetEditorManager::OpenEditorForAssets( const TArray< UObject* >& Assets, // If any assets were destroyed, attempt to find them if they were recreated for (int32 i = 0; i < Assets.Num(); i++) { - auto& AssetInfo = AssetInfoList[i]; - auto Asset = Assets[i]; + const FLocalAssetInfo& AssetInfo = AssetInfoList[i]; + UObject* Asset = Assets[i]; if (!AssetInfo.WeakAsset.IsValid() && !AssetInfo.AssetPath.IsEmpty()) { @@ -517,7 +523,7 @@ bool FAssetEditorManager::OpenEditorForAssets( const TArray< UObject* >& Assets, else { // Asset types don't match or some are already open, so just open individual editors for the unopened ones - for (auto Asset : Assets) + for (UObject* Asset : Assets) { if (!SkipOpenAssets.Contains(Asset)) { @@ -734,7 +740,7 @@ void FAssetEditorManager::SaveOpenAssetEditors(bool bOnShutdown) // Don't save a list of assets to restore if we are running under a debugger if(!FPlatformMisc::IsDebuggerPresent()) { - for (auto EditorPair : OpenedEditors) + for (const TPair& EditorPair : OpenedEditors) { IAssetEditorInstance* Editor = EditorPair.Key; if (Editor != NULL) diff --git a/Engine/Source/Editor/UnrealEd/Private/Toolkits/AssetEditorToolkit.cpp b/Engine/Source/Editor/UnrealEd/Private/Toolkits/AssetEditorToolkit.cpp index 027034aca738..97aa8f0b2285 100644 --- a/Engine/Source/Editor/UnrealEd/Private/Toolkits/AssetEditorToolkit.cpp +++ b/Engine/Source/Editor/UnrealEd/Private/Toolkits/AssetEditorToolkit.cpp @@ -1117,18 +1117,10 @@ void FAssetEditorToolkit::RemoveAllToolbarWidgets() void FAssetEditorToolkit::FGCEditingObjects::AddReferencedObjects(FReferenceCollector& Collector) { - // Remove null objects as a safe guard against assets being forcibly GC'd - TArray EditingObjectsCopy = OwnerToolkit.EditingObjects; Collector.AddReferencedObjects(OwnerToolkit.EditingObjects); - - OwnerToolkit.EditingObjects.Reset(); - for (UObject* Object : EditingObjectsCopy) - { - if (Object != nullptr) - { - OwnerToolkit.EditingObjects.Add(Object); - } - } + + // Remove null objects as a safe guard against assets being forcibly GC'd + OwnerToolkit.EditingObjects.RemoveAllSwap([](UObject* Obj) { return Obj == nullptr; } ); } diff --git a/Engine/Source/Editor/UnrealEd/Public/Commandlets/AssetRegistryGenerator.h b/Engine/Source/Editor/UnrealEd/Public/Commandlets/AssetRegistryGenerator.h index 8d7f148a3530..32f82ed09e94 100644 --- a/Engine/Source/Editor/UnrealEd/Public/Commandlets/AssetRegistryGenerator.h +++ b/Engine/Source/Editor/UnrealEd/Public/Commandlets/AssetRegistryGenerator.h @@ -176,6 +176,8 @@ private: bool bGenerateChunks; /** True if we should use the AssetManager, false to use the deprecated path */ bool bUseAssetManager; + /** Highest chunk id, being used for geneating dependency tree */ + int32 HighestChunkId; /** Array of Maps with chunks<->packages assignments */ TArray ChunkManifests; /** Map of packages that has not been assigned to chunks */ @@ -192,6 +194,9 @@ private: /** Dependency type to follow when adding package dependencies to chunks.*/ EAssetRegistryDependencyType::Type DependencyType; + /** Mapping from chunk id to pakchunk file index. If not defined, Pakchunk index will be the same as chunk id by default */ + TMap ChunkIdPakchunkIndexMapping; + struct FReferencePair { FReferencePair() {} @@ -228,6 +233,14 @@ private: */ void RemovePackageFromManifest(FName PackageName, int32 ChunkId); + /** + * Get pakchunk file index from ChunkID + * + * @param ChunkID + * @return Index of target pakchunk file + */ + int32 GetPakchunkIndex(int32 ChunkId); + /** * Walks the dependency graph of assets and assigns packages to correct chunks. * @@ -240,7 +253,7 @@ private: */ void InjectEncryptionData(FAssetRegistryState& TargetState); - void AddPackageAndDependenciesToChunk(FChunkPackageSet* ThisPackageSet, FName InPkgName, const FString& InSandboxFile, int32 ChunkID, FSandboxPlatformFile* SandboxPlatformFile); + void AddPackageAndDependenciesToChunk(FChunkPackageSet* ThisPackageSet, FName InPkgName, const FString& InSandboxFile, int32 PakchunkIndex, FSandboxPlatformFile* SandboxPlatformFile); /** * Returns the path of the temporary packaging directory for the specified platform. @@ -332,5 +345,7 @@ private: /** Helper function to fill a given collection with a set of packages */ void WriteCollection(FName CollectionName, const TArray& PackageNames); - + + /** Initialize ChunkIdPakchunkIndexMapping and PakchunkIndexChunkIdMapping. */ + void InitializeChunkIdPakchunkIndexMapping(); }; diff --git a/Engine/Source/Editor/UnrealEd/Public/EdMode.h b/Engine/Source/Editor/UnrealEd/Public/EdMode.h index 79a0f6690e4f..80f053b20592 100644 --- a/Engine/Source/Editor/UnrealEd/Public/EdMode.h +++ b/Engine/Source/Editor/UnrealEd/Public/EdMode.h @@ -132,6 +132,15 @@ public: */ virtual bool GetCursor(EMouseCursor::Type& OutCursor) const { return false; } + /** Get override cursor visibility settings */ + virtual bool GetOverrideCursorVisibility(bool& bWantsOverride, bool& bHardwareCursorVisible, bool bSoftwareCursorVisible) const { return false; } + + /** Called before mouse movement is converted to drag/rot */ + virtual bool PreConvertMouseMovement(FEditorViewportClient* InViewportClient) { return false; } + + /** Called after mouse movement is converted to drag/rot */ + virtual bool PostConvertMouseMovement(FEditorViewportClient* InViewportClient) { return false;} + virtual bool ShouldDrawBrushWireframe( AActor* InActor ) const { return true; } virtual bool GetCustomDrawingCoordinateSystem(FMatrix& InMatrix, void* InData); virtual bool GetCustomInputCoordinateSystem( FMatrix& InMatrix, void* InData ) { return 0; } diff --git a/Engine/Source/Editor/UnrealEd/Public/EditorModeManager.h b/Engine/Source/Editor/UnrealEd/Public/EditorModeManager.h index 83f2a9ae59ac..b83d2772564b 100644 --- a/Engine/Source/Editor/UnrealEd/Public/EditorModeManager.h +++ b/Engine/Source/Editor/UnrealEd/Public/EditorModeManager.h @@ -262,6 +262,15 @@ public: /** Get a cursor to override the default with, if any */ bool GetCursor(EMouseCursor::Type& OutCursor) const; + /** Get override cursor visibility settings */ + bool GetOverrideCursorVisibility(bool& bWantsOverride, bool& bHardwareCursorVisible, bool bSoftwareCursorVisible) const; + + /** Called before converting mouse movement to drag/rot */ + bool PreConvertMouseMovement(FEditorViewportClient* InViewportClient); + + /** Called after converting mouse movement to drag/rot */ + bool PostConvertMouseMovement(FEditorViewportClient* InViewportClient); + /** * Returns a good location to draw the widget at. */ diff --git a/Engine/Source/Editor/UnrealEd/Public/EditorViewportClient.h b/Engine/Source/Editor/UnrealEd/Public/EditorViewportClient.h index 6b0064a1606a..3d3ac1c8fa23 100644 --- a/Engine/Source/Editor/UnrealEd/Public/EditorViewportClient.h +++ b/Engine/Source/Editor/UnrealEd/Public/EditorViewportClient.h @@ -977,8 +977,12 @@ public: EAxisList::Type GetCurrentWidgetAxis() const; + /** Overrides current cursor. */ void SetRequiredCursorOverride( bool WantOverride, EMouseCursor::Type RequiredCursor = EMouseCursor::Default ); + /** Overrides current widget mode */ + void SetWidgetModeOverride(FWidget::EWidgetMode InWidgetMode); + /** Get the camera speed for this viewport */ float GetCameraSpeed() const; diff --git a/Engine/Source/Editor/UnrealEd/Public/Kismet2/KismetDebugUtilities.h b/Engine/Source/Editor/UnrealEd/Public/Kismet2/KismetDebugUtilities.h index afdef0820943..6f7134446986 100644 --- a/Engine/Source/Editor/UnrealEd/Public/Kismet2/KismetDebugUtilities.h +++ b/Engine/Source/Editor/UnrealEd/Public/Kismet2/KismetDebugUtilities.h @@ -251,7 +251,7 @@ protected: // helper function for converting between blueprint and debuggable data // output params are only valid if the return result is EWatchTextResult::EWTR_Valid - static EWatchTextResult FindDebuggingData(UBlueprint* Blueprint, UObject* ActiveObject, const UEdGraphPin* WatchPin, UProperty*& OutProperty, void*& OutData, void*& OutDelta, UObject*& OutParent); + static EWatchTextResult FindDebuggingData(UBlueprint* Blueprint, UObject* ActiveObject, const UEdGraphPin* WatchPin, UProperty*& OutProperty, void*& OutData, void*& OutDelta, UObject*& OutParent, TArray& SeenObjects); private: FKismetDebugUtilities() {} diff --git a/Engine/Source/Editor/UnrealEd/Public/LevelEditorViewport.h b/Engine/Source/Editor/UnrealEd/Public/LevelEditorViewport.h index 07e5eb220277..cc6e6435e933 100644 --- a/Engine/Source/Editor/UnrealEd/Public/LevelEditorViewport.h +++ b/Engine/Source/Editor/UnrealEd/Public/LevelEditorViewport.h @@ -181,6 +181,7 @@ public: virtual void EndCameraMovement() override; virtual void SetVREditView(bool bGameViewEnable) override; virtual bool GetPivotForOrbit(FVector& Pivot) const override; + virtual bool ShouldScaleCameraSpeedByDistance() const override; virtual bool OverrideHighResScreenshotCaptureRegion(FIntRect& OutCaptureRegion) override; @@ -800,4 +801,6 @@ private: /** Stores the previous frame's value of bEditorCameraCut in order to reset it back to false on the next frame */ bool bWasEditorCameraCut; + + bool bApplyCameraSpeedScaleByDistance; }; diff --git a/Engine/Source/Editor/VREditor/Public/VREditorMode.h b/Engine/Source/Editor/VREditor/Public/VREditorMode.h index 8980c45b9a86..bc13a7d9baf6 100644 --- a/Engine/Source/Editor/VREditor/Public/VREditorMode.h +++ b/Engine/Source/Editor/VREditor/Public/VREditorMode.h @@ -68,9 +68,6 @@ public: /** When the user actually enters the VR Editor mode */ void Enter(); - /** When Blueprints are reinstanced so we can re-connect our interactors. */ - void OnBlueprintReinstanced(); - /** When the user leaves the VR Editor mode */ void Exit( const bool bShouldDisableStereo ); diff --git a/Engine/Source/Editor/VREditor/UI/VREditorUISystem.cpp b/Engine/Source/Editor/VREditor/UI/VREditorUISystem.cpp index aef464a83114..3b677da9e536 100644 --- a/Engine/Source/Editor/VREditor/UI/VREditorUISystem.cpp +++ b/Engine/Source/Editor/VREditor/UI/VREditorUISystem.cpp @@ -2456,8 +2456,12 @@ void UVREditorUISystem::UpdateSequencerUI() void UVREditorUISystem::UpdateActorPreviewUI( TSharedRef InWidget, int32 Index, AActor *Actor ) { - check(Actor != nullptr) - FString PanelString = ActorPreviewPrefix + FString::FromInt( Actor->GetUniqueID() ); + + FString PanelString = ActorPreviewPrefix; + if (Actor) + { + PanelString += FString::FromInt(Actor->GetUniqueID()); + } VREditorPanelID PreviewPanelID = FName(*PanelString); AVREditorFloatingUI* PreviewPanel = GetPanel(PreviewPanelID); diff --git a/Engine/Source/Editor/VREditor/VREditorAvatarActor.h b/Engine/Source/Editor/VREditor/VREditorAvatarActor.h index 8e03383de7f9..2e1dc550a283 100644 --- a/Engine/Source/Editor/VREditor/VREditorAvatarActor.h +++ b/Engine/Source/Editor/VREditor/VREditorAvatarActor.h @@ -34,6 +34,12 @@ public: return true; } + virtual bool HasLocalNetOwner() const override + { + // TODO: When using Multi-User with two VRMode editors, we might have to do something else here for when this is called by MotionControllerComponent::PollControllerState() + return true; + } + private: /** Our avatar's head mesh */ diff --git a/Engine/Source/Editor/VREditor/VREditorMode.cpp b/Engine/Source/Editor/VREditor/VREditorMode.cpp index a58d74ee9ec9..3747d22243fc 100644 --- a/Engine/Source/Editor/VREditor/VREditorMode.cpp +++ b/Engine/Source/Editor/VREditor/VREditorMode.cpp @@ -138,8 +138,6 @@ void UVREditorMode::Init() check( WorldInteraction != nullptr ); } - GEditor->OnBlueprintReinstanced().AddUObject( this, &UVREditorMode::OnBlueprintReinstanced ); - // Setup the asset container. AssetContainer = &LoadAssetContainer(); check(AssetContainer != nullptr); @@ -150,114 +148,6 @@ void UVREditorMode::Init() bIsFullyInitialized = true; } -// If we're recompiling Blueprints, we'll need to re-create and re-connect our interactors. -void UVREditorMode::OnBlueprintReinstanced() -{ - // Tear down - { - DestroyTransientActor( AvatarActor ); - AvatarActor = nullptr; - FlashlightComponent = nullptr; - } - - // Kill subsystems - if (UISystem != nullptr) - { - UISystem->Shutdown(); - UISystem->MarkPendingKill(); - UISystem = nullptr; - } - - if (PlacementSystem != nullptr) - { - PlacementSystem->Shutdown(); - PlacementSystem->MarkPendingKill(); - PlacementSystem = nullptr; - } - - if (TeleportActor != nullptr) - { - DestroyTransientActor( TeleportActor ); - TeleportActor = nullptr; - } - - if (AutoScalerSystem != nullptr) - { - AutoScalerSystem->Shutdown(); - AutoScalerSystem->MarkPendingKill(); - AutoScalerSystem = nullptr; - } - - // Rebuild - if (bActuallyUsingVR) - { - // Tell Slate to require a larger pixel distance threshold before the drag starts. This is important for things - // like Content Browser drag and drop. - SavedEditorState.DragTriggerDistance = FSlateApplication::Get().GetDragTriggerDistance(); - FSlateApplication::Get().SetDragTriggerDistance( VREd::SlateDragDistanceOverride->GetFloat() ); - - // When actually in VR, make sure the transform gizmo is big! - SavedEditorState.TransformGizmoScale = WorldInteraction->GetTransformGizmoScale(); - WorldInteraction->SetTransformGizmoScale( GetDefault()->GizmoScale ); - WorldInteraction->SetShouldSuppressExistingCursor( true ); - WorldInteraction->SetInVR( true ); - } - - // Setup our avatar - if (AvatarActor == nullptr) - { - const bool bWithSceneComponent = true; - AvatarActor = SpawnTransientSceneActor( TEXT( "AvatarActor" ), bWithSceneComponent ); - AvatarActor->Init( this ); - - WorldInteraction->AddActorToExcludeFromHitTests( AvatarActor ); - } - - // Setup sub systems - { - // Setup world interaction - // We need input preprocessing for VR so that we can receive motion controller input without any viewports having - // to be focused. This is mainly because Slate UI injected into the 3D world can cause focus to be lost unexpectedly, - // but we need the user to still be able to interact with UI. - WorldInteraction->SetUseInputPreprocessor( true ); - - // Motion controllers -// LeftHandInteractor->SetControllerHandSide( FXRMotionControllerBase::LeftHandSourceId ); - -// RightHandInteractor->SetControllerHandSide( FXRMotionControllerBase::RightHandSourceId ); - - WorldInteraction->PairInteractors(GetHandInteractor(EControllerHand::Left), GetHandInteractor(EControllerHand::Right)); - - for (UVREditorInteractor* Interactor : Interactors) - { - Interactor->Init( this ); - WorldInteraction->AddInteractor( Interactor ); - } - - // Setup the UI system - UISystem = NewObject(); - UISystem->Init( this ); - - PlacementSystem = NewObject(); - PlacementSystem->Init( this ); - - // Setup teleporter - TeleportActor = SpawnTransientSceneActor( TEXT( "Teleporter" ), true ); - TeleportActor->Init( this ); - WorldInteraction->AddActorToExcludeFromHitTests( TeleportActor ); - - // Setup autoscaler - AutoScalerSystem = NewObject(); - AutoScalerSystem->Init( this ); - } - - for (UVREditorInteractor* Interactor : Interactors) - { - Interactor->SetupComponent( AvatarActor ); - } -} - - /* * @EventName Editor.Usage.EnterVRMode * @@ -288,8 +178,6 @@ void UVREditorMode::Shutdown() GEnableVREditorHacks = false; FEditorDelegates::EndPIE.RemoveAll(this); - - GEditor->OnBlueprintReinstanced().RemoveAll( this ); } void UVREditorMode::AllocateInteractors() diff --git a/Engine/Source/Editor/VREditor/VREditorModeManager.cpp b/Engine/Source/Editor/VREditor/VREditorModeManager.cpp index 90fb25391929..457c6398c9a4 100644 --- a/Engine/Source/Editor/VREditor/VREditorModeManager.cpp +++ b/Engine/Source/Editor/VREditor/VREditorModeManager.cpp @@ -146,11 +146,19 @@ bool FVREditorModeManager::IsVREditorActive() const return CurrentVREditorMode != nullptr && CurrentVREditorMode->IsActive(); } - +const static FName WMRSytemName = FName(TEXT("WindowsMixedRealityHMD")); bool FVREditorModeManager::IsVREditorAvailable() const { - const bool bHasHMDDevice = GEngine->XRSystem.IsValid() && GEngine->XRSystem->GetHMDDevice() && GEngine->XRSystem->GetHMDDevice()->IsHMDEnabled(); - return bHasHMDDevice && !GEditor->bIsSimulatingInEditor; + if (GEngine->XRSystem.IsValid() && GEngine->XRSystem->GetHMDDevice() && GEngine->XRSystem->GetHMDDevice()->IsHMDEnabled()) + { + // TODO: UE-71871 Work around for avoiding starting VRMode when using WMR + const bool bIsWMR = GEngine->XRSystem->GetSystemName() == WMRSytemName; + return !bIsWMR && !GEditor->bIsSimulatingInEditor; + } + else + { + return false; + } } bool FVREditorModeManager::IsVREditorButtonActive() const diff --git a/Engine/Source/Editor/VREditor/VREditorPlacement.cpp b/Engine/Source/Editor/VREditor/VREditorPlacement.cpp index 21a0b01eae10..a90854b67e66 100644 --- a/Engine/Source/Editor/VREditor/VREditorPlacement.cpp +++ b/Engine/Source/Editor/VREditor/VREditorPlacement.cpp @@ -372,25 +372,34 @@ void UVREditorPlacement::StartPlacingObjects( const TArray& ObjectsToP FEditorDelegates::OnNewActorsDropped.Broadcast( DroppedObjects, AllNewActors ); } - FBox BoundsOfAllActors; + static const auto CVarAllowCarryingCertainObjects = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("VI.AllowCarryingCertainObjects")); + const bool bCanBeCarried = ViewportWorldInteraction->GetTransformables().Num() == 1 && ViewportWorldInteraction->GetTransformables()[0].Get()->ShouldBeCarried(); + const bool bShouldInterpolateScaleFromDragLocation = bShouldInterpolateFromDragLocation && (CVarAllowCarryingCertainObjects->GetValueOnAnyThread() == 0 || !bCanBeCarried); + + float DesiredScale = 1.f; + if( bShouldInterpolateScaleFromDragLocation ) { - BoundsOfAllActors.Init(); + FBox BoundsOfAllActors; + { + BoundsOfAllActors.Init(); + for( AActor* NewActor : AllNewActors ) + { + BoundsOfAllActors += NewActor->CalculateComponentsBoundingBoxInLocalSpace(); + } + } + const float BoundsOfAllActorsSize = BoundsOfAllActors.GetSize().GetAbsMax(); + const float UsedBoundsOfAllActorsSize = BoundsOfAllActorsSize == 0 ? 1 : BoundsOfAllActorsSize; + DesiredScale = (VREd::SizeOfActorsOverContentBrowserThumbnail->GetFloat() / UsedBoundsOfAllActorsSize) * ViewportWorldInteraction->GetWorldScaleFactor(); + + // Start the placed objects off scaled down to match the content browser thumbnail for( AActor* NewActor : AllNewActors ) { - BoundsOfAllActors += NewActor->CalculateComponentsBoundingBoxInLocalSpace(); + NewActor->SetActorScale3D(FVector(DesiredScale)); } } - const float BoundsOfAllActorsSize = BoundsOfAllActors.GetSize().GetAbsMax(); - const float UsedBoundsOfAllActorsSize = BoundsOfAllActorsSize == 0 ? 1 : BoundsOfAllActorsSize; - const float DesiredScale = ( VREd::SizeOfActorsOverContentBrowserThumbnail->GetFloat() / UsedBoundsOfAllActorsSize ) * ViewportWorldInteraction->GetWorldScaleFactor(); - - // Start the placed objects off scaled down to match the content browser thumbnail - if( bShouldInterpolateFromDragLocation ) + else if( bShouldInterpolateFromDragLocation ) { - for( AActor* NewActor : AllNewActors ) - { - NewActor->SetActorScale3D( FVector( DesiredScale ) ); - } + PlaceAt = PlacingWithInteractor->GetInteractorData().Transform.GetLocation(); } // We changed the initial scale of selected actors, so make sure our transformables and gizmo start transform are up to date. diff --git a/Engine/Source/Programs/AutomationTool/AutomationUtils/AutomationException.cs b/Engine/Source/Programs/AutomationTool/AutomationUtils/AutomationException.cs index 559ac6cae236..eb604eddf0d9 100644 --- a/Engine/Source/Programs/AutomationTool/AutomationUtils/AutomationException.cs +++ b/Engine/Source/Programs/AutomationTool/AutomationUtils/AutomationException.cs @@ -1,4 +1,4 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Linq; @@ -63,6 +63,27 @@ namespace AutomationTool Error_AndroidOBBError = 155, }; + /// + /// How the exception should be output + /// + public enum AutomationExceptionOutputFormat + { + /// + /// Do not output any message; just output the exception details to the log. + /// + Silent, + + /// + /// Output a brief summary to the console, and the exception details to the log + /// + Minimal, + + /// + /// Treat the exception as an error + /// + Error + } + /// /// Exception class used by the AutomationTool to throw exceptions. Allows setting an exit code that will be passed to the entry routine to return to the system on program exit. /// If no exit code is given, Error_Unkonwn is used. @@ -70,6 +91,7 @@ namespace AutomationTool public class AutomationException : System.Exception { public ExitCode ErrorCode = ExitCode.Error_Unknown; + public AutomationExceptionOutputFormat OutputFormat = AutomationExceptionOutputFormat.Error; public AutomationException(string Msg) :base(Msg) diff --git a/Engine/Source/Programs/AutomationTool/AutomationUtils/BuildCommand.cs b/Engine/Source/Programs/AutomationTool/AutomationUtils/BuildCommand.cs index 9b31f71898b5..8343c0b834ac 100644 --- a/Engine/Source/Programs/AutomationTool/AutomationUtils/BuildCommand.cs +++ b/Engine/Source/Programs/AutomationTool/AutomationUtils/BuildCommand.cs @@ -226,6 +226,27 @@ namespace AutomationTool } } + /// + /// Checks that all of the required params are present, throws an exception if not + /// + /// + public void CheckParamsArePresent(params string[] Args) + { + List MissingParams = new List(); + foreach (string Arg in Args) + { + if (ParseParamValue(Arg, null) == null) + { + MissingParams.Add(Arg); + } + } + + if (MissingParams.Count > 0) + { + throw new AutomationException("Params {0} are missing but required. Required params are {1}", string.Join(",", MissingParams), string.Join(",", Args)); + } + } + /// /// Build command entry point. Throws AutomationExceptions on failure. /// diff --git a/Engine/Source/Programs/AutomationTool/AutomationUtils/CommandUtils.cs b/Engine/Source/Programs/AutomationTool/AutomationUtils/CommandUtils.cs index 7f84809a841c..6ff4b4cb9c34 100644 --- a/Engine/Source/Programs/AutomationTool/AutomationUtils/CommandUtils.cs +++ b/Engine/Source/Programs/AutomationTool/AutomationUtils/CommandUtils.cs @@ -123,29 +123,6 @@ namespace AutomationTool #region Logging - /// - /// Writes formatted text to log (with LogEventType.Console). - /// - /// Format string - /// Parameters - [MethodImplAttribute(MethodImplOptions.NoInlining)] - [Obsolete("CommandUtils.Log() has been deprecated. Use CommandUtils.LogInformation() instead.", false)] - public static void Log(string Format, params object[] Args) - { - Tools.DotNETCommon.Log.WriteLine(1, Tools.DotNETCommon.LogEventType.Console, Format, Args); - } - - /// - /// Writes formatted text to log (with LogEventType.Console). - /// - /// Text - [MethodImplAttribute(MethodImplOptions.NoInlining)] - [Obsolete("CommandUtils.Log() has been deprecated. Use CommandUtils.LogInformation() instead.", false)] - public static void Log(string Message) - { - Tools.DotNETCommon.Log.WriteLine(1, Tools.DotNETCommon.LogEventType.Console, Message); - } - /// /// Writes formatted text to log (with LogEventType.Console). /// diff --git a/Engine/Source/Programs/AutomationTool/AutomationUtils/MCPPublic.cs b/Engine/Source/Programs/AutomationTool/AutomationUtils/MCPPublic.cs index ac242da6c95c..7755ddbbd4f6 100644 --- a/Engine/Source/Programs/AutomationTool/AutomationUtils/MCPPublic.cs +++ b/Engine/Source/Programs/AutomationTool/AutomationUtils/MCPPublic.cs @@ -877,6 +877,10 @@ namespace EpicGames.MCP.Automation /// The file path to the update manifest. New data will be added to the cloud directory that this manifest is in. /// public string DestinationManifest; + /// + /// Specifies in bytes, an upper limit for original diffs to try to enhance. + /// + public ulong DiffAbortThreshold; } static BuildPatchToolBase Handler = null; diff --git a/Engine/Source/Programs/AutomationTool/AutomationUtils/ProcessUtils.cs b/Engine/Source/Programs/AutomationTool/AutomationUtils/ProcessUtils.cs index 035ca496d85a..8565cb0cdb8b 100644 --- a/Engine/Source/Programs/AutomationTool/AutomationUtils/ProcessUtils.cs +++ b/Engine/Source/Programs/AutomationTool/AutomationUtils/ProcessUtils.cs @@ -961,7 +961,7 @@ namespace AutomationTool if (Result.ExitCode > MaxSuccessCode || Result.ExitCode < 0) { throw new CommandFailedException((ExitCode)Result.ExitCode, String.Format("Command failed (Result:{3}): {0} {1}. See logfile for details: '{2}' ", - App, CommandLine, Path.GetFileName(Logfile), Result.ExitCode)); + App, CommandLine, Path.GetFileName(Logfile), Result.ExitCode)){ OutputFormat = AutomationExceptionOutputFormat.Minimal }; } if (!String.IsNullOrEmpty(Result.Output)) { @@ -1050,7 +1050,7 @@ namespace AutomationTool IProcessResult Result = Run(CmdEnv.UATExe, CommandLine, null, ERunOptions.Default, EnvironmentVars, Identifier: Identifier); if (Result.ExitCode != 0) { - throw new CommandFailedException(String.Format("Recursive UAT command failed (exit code {0})", Result.ExitCode)); + throw new CommandFailedException(String.Format("Recursive UAT command failed (exit code {0})", Result.ExitCode)){ OutputFormat = AutomationExceptionOutputFormat.Silent }; } } diff --git a/Engine/Source/Programs/AutomationTool/AutomationUtils/ProjectUtils.cs b/Engine/Source/Programs/AutomationTool/AutomationUtils/ProjectUtils.cs index db6cc406675e..48b5af8e8a31 100644 --- a/Engine/Source/Programs/AutomationTool/AutomationUtils/ProjectUtils.cs +++ b/Engine/Source/Programs/AutomationTool/AutomationUtils/ProjectUtils.cs @@ -176,7 +176,7 @@ namespace AutomationTool foreach (UnrealTargetPlatform ClientPlatform in ClientTargetPlatforms) { EncryptionAndSigning.CryptoSettings Settings = EncryptionAndSigning.ParseCryptoSettings(RawProjectPath.Directory, ClientPlatform); - if (Settings.IsAnyEncryptionEnabled() || Settings.bEnablePakSigning) + if (Settings.IsAnyEncryptionEnabled() || Settings.IsPakSigningEnabled()) { Reason = "encryption/signing is enabled"; return true; diff --git a/Engine/Source/Programs/AutomationTool/AutomationUtils/UBTUtils.cs b/Engine/Source/Programs/AutomationTool/AutomationUtils/UBTUtils.cs index 38cacfed5671..0aa999d5e299 100644 --- a/Engine/Source/Programs/AutomationTool/AutomationUtils/UBTUtils.cs +++ b/Engine/Source/Programs/AutomationTool/AutomationUtils/UBTUtils.cs @@ -47,7 +47,7 @@ namespace AutomationTool IProcessResult Result = Run(UBTExecutable, CommandLine, Options: ERunOptions.AllowSpew | ERunOptions.NoStdOutCapture); if(Result.ExitCode != 0) { - throw new AutomationException((ExitCode)Result.ExitCode, "UnrealBuildTool failed. See log for more details. ({0})", CommandUtils.CombinePaths(Env.FinalLogFolder, LogName)); + throw new AutomationException((ExitCode)Result.ExitCode, "UnrealBuildTool failed. See log for more details. ({0})", CommandUtils.CombinePaths(Env.FinalLogFolder, LogName)) { OutputFormat = AutomationExceptionOutputFormat.Minimal }; } } diff --git a/Engine/Source/Programs/AutomationTool/AutomationUtils/UE4Build.cs b/Engine/Source/Programs/AutomationTool/AutomationUtils/UE4Build.cs index 2789422583b3..2e7e01269041 100644 --- a/Engine/Source/Programs/AutomationTool/AutomationUtils/UE4Build.cs +++ b/Engine/Source/Programs/AutomationTool/AutomationUtils/UE4Build.cs @@ -12,6 +12,20 @@ using System.Linq; namespace AutomationTool { + class UE4BuildException : AutomationException + { + public UE4BuildException(string Message) + : base("BUILD FAILED: " + Message) + { + OutputFormat = AutomationExceptionOutputFormat.Minimal; + } + + public UE4BuildException(string Format, params object[] Args) + : this(String.Format(Format, Args)) + { + } + } + [Help("ForceMonolithic", "Toggle to combined the result into one executable")] [Help("ForceDebugInfo", "Forces debug info even in development builds")] [Help("NoXGE", "Toggle to disable the distributed build process")] @@ -40,7 +54,7 @@ namespace AutomationTool string File = CommandUtils.CombinePaths(InFile); if (!CommandUtils.FileExists(File) && !CommandUtils.DirectoryExists(File)) { - throw new AutomationException("BUILD FAILED specified file to AddBuildProduct {0} does not exist.", File); + throw new UE4BuildException("Specified file to AddBuildProduct {0} does not exist.", File); } if (!HasBuildProduct(InFile)) { @@ -60,7 +74,7 @@ namespace AutomationTool { if (!FileReference.Exists(ManifestFile)) { - throw new AutomationException("BUILD FAILED UBT Manifest {0} does not exist.", ManifestFile); + throw new UE4BuildException("UBT Manifest {0} does not exist.", ManifestFile); } BuildManifest Manifest = CommandUtils.ReadManifest(ManifestFile); @@ -68,7 +82,7 @@ namespace AutomationTool { if (!CommandUtils.FileExists_NoExceptions(Item) && !CommandUtils.DirectoryExists_NoExceptions(Item)) { - throw new AutomationException("BUILD FAILED {0} was in manifest but was not produced.", Item); + throw new UE4BuildException("{0} was in manifest but was not produced.", Item); } AddBuildProduct(Item); } @@ -79,7 +93,7 @@ namespace AutomationTool { if (CommandUtils.FileExists(UBTExecutable) == false) { - throw new AutomationException("UBT does not exist in {0}.", UBTExecutable); + throw new UE4BuildException("UBT does not exist in {0}.", UBTExecutable); } } @@ -127,7 +141,7 @@ namespace AutomationTool { if (!CommandUtils.FileExists_NoExceptions(XGEFile)) { - throw new AutomationException("BUILD FAILED: Couldn't find file: {0}", XGEFile); + throw new UE4BuildException("Couldn't find file: {0}", XGEFile); } int FileNum = 0; string OutFile; @@ -160,7 +174,7 @@ namespace AutomationTool { if (!CommandUtils.FileExists_NoExceptions(ManifestItem)) { - throw new AutomationException("BUILD FAILED {0} was in manifest but was not produced.", ManifestItem); + throw new UE4BuildException("{0} was in manifest but was not produced.", ManifestItem); } AddBuildProduct(ManifestItem); } @@ -555,7 +569,7 @@ namespace AutomationTool } if (!CommandUtils.FileExists(TaskFilePath)) { - throw new AutomationException("Unable to find xge xml: " + TaskFilePath); + throw new UE4BuildException("Unable to find xge xml: " + TaskFilePath); } CombineXGEStopwatch.Finish(); @@ -602,7 +616,7 @@ namespace AutomationTool } else if (SuccesCode != 0) { - throw new AutomationException("Command failed (Result:{3}): {0} {1}. See logfile for details: '{2}' ", + throw new UE4BuildException("Command failed (Result:{3}): {0} {1}. See logfile for details: '{2}' ", XGETool, Args, Path.GetFileName(LogFile), SuccesCode); } CommandUtils.LogVerbose("{0} {1} Done *******", XGETool, Args); @@ -645,7 +659,7 @@ namespace AutomationTool { if (!CommandUtils.FileExists_NoExceptions(XGEFile)) { - throw new AutomationException("BUILD FAILED: Couldn't find file: {0}", XGEFile); + throw new UE4BuildException("Couldn't find file: {0}", XGEFile); } var TargetFile = TaskFilePath + "." + Path.GetFileName(XGEFile); CommandUtils.CopyFile(XGEFile, TargetFile); @@ -889,7 +903,7 @@ namespace AutomationTool string ActionJob = ThisAction + string.Format("_{0}", Job); if (!ActionToAction.ContainsKey(ActionJob)) { - throw new AutomationException("Action not found '{0}' in {1}->{2}", ActionJob, XGEFile, TargetFile); + throw new UE4BuildException("Action not found '{0}' in {1}->{2}", ActionJob, XGEFile, TargetFile); // the XGE schedule is not topologically sorted. Hmmm. Well, we make a scary assumption here that this } NewDepends += ActionToAction[ActionJob]; @@ -964,7 +978,7 @@ namespace AutomationTool { if (!CommandUtils.CmdEnv.HasCapabilityToCompile) { - throw new AutomationException("You are attempting to compile on a machine that does not have a supported compiler!"); + throw new UE4BuildException("You are attempting to compile on a machine that does not have a supported compiler!"); } bool DeleteBuildProducts = InDeleteBuildProducts.HasValue ? InDeleteBuildProducts.Value : ParseParam("Clean"); if (InUpdateVersionFiles) @@ -1116,7 +1130,7 @@ namespace AutomationTool CommandUtils.LogSetProgress(InShowProgress, "Building..."); if (!ProcessXGEItems(XGEItems, XGETool, Args, TaskFilePath, InShowProgress)) { - throw new AutomationException("BUILD FAILED: {0} failed, retries not enabled:", XGETool); + throw new UE4BuildException("{0} failed, retries not enabled:", XGETool); } } @@ -1139,7 +1153,7 @@ namespace AutomationTool { if (!CommandUtils.FileExists(Product) && !CommandUtils.DirectoryExists(Product)) { - throw new AutomationException("BUILD FAILED {0} was a build product but no longer exists", Product); + throw new UE4BuildException("{0} was a build product but no longer exists", Product); } CommandUtils.LogLog(Product); } @@ -1161,7 +1175,7 @@ namespace AutomationTool CommandUtils.P4.Sync("-f -k " + CommandUtils.MakePathSafeToUseWithCommandLine(File) + "#head"); // sync the file without overwriting local one if (!CommandUtils.FileExists(File)) { - throw new AutomationException("BUILD FAILED {0} was a build product but no longer exists", File); + throw new UE4BuildException("{0} was a build product but no longer exists", File); } CommandUtils.P4.ReconcileNoDeletes(WorkingCL, CommandUtils.MakePathSafeToUseWithCommandLine(File)); @@ -1225,7 +1239,7 @@ namespace AutomationTool var UBTProduct = CommandUtils.CombinePaths(UBTLocation, UBTFile); if (!CommandUtils.FileExists_NoExceptions(UBTProduct)) { - throw new AutomationException("Cannot add UBT to the build products because {0} does not exist.", UBTProduct); + throw new UE4BuildException("Cannot add UBT to the build products because {0} does not exist.", UBTProduct); } AddBuildProduct(UBTProduct); } @@ -1249,7 +1263,7 @@ namespace AutomationTool var OutputFile = CommandUtils.CombinePaths(DotNetOutputLocation, UATFile); if (!CommandUtils.FileExists_NoExceptions(OutputFile)) { - throw new AutomationException("Cannot add UAT to the build products because {0} does not exist.", OutputFile); + throw new UE4BuildException("Cannot add UAT to the build products because {0} does not exist.", OutputFile); } AddBuildProduct(OutputFile); } @@ -1267,7 +1281,7 @@ namespace AutomationTool var UATScripts = Directory.GetFiles(UATScriptsLocation, "*" + ScriptsPostfix, SearchOption.AllDirectories); if (CommandUtils.IsNullOrEmpty(UATScripts)) { - throw new AutomationException("No automation scripts found in {0}. Cannot add UAT files to the build products.", UATScriptsLocation); + throw new UE4BuildException("No automation scripts found in {0}. Cannot add UAT files to the build products.", UATScriptsLocation); } var UATFiles = new List(new string[] @@ -1286,7 +1300,7 @@ namespace AutomationTool var OutputFile = CommandUtils.CombinePaths(DotNetOutputLocation, UATFile); if (!CommandUtils.FileExists_NoExceptions(OutputFile)) { - throw new AutomationException("Cannot add UAT to the build products because {0} does not exist.", OutputFile); + throw new UE4BuildException("Cannot add UAT to the build products because {0} does not exist.", OutputFile); } AddBuildProduct(OutputFile); } @@ -1296,7 +1310,7 @@ namespace AutomationTool { if (!CommandUtils.FileExists_NoExceptions(UATScriptFilePath)) { - throw new AutomationException("Cannot add UAT to the build products because {0} does not exist.", UATScriptFilePath); + throw new UE4BuildException("Cannot add UAT to the build products because {0} does not exist.", UATScriptFilePath); } AddBuildProduct(UATScriptFilePath); diff --git a/Engine/Source/Programs/AutomationTool/Gauntlet/Framework/Gauntlet.Utils.cs b/Engine/Source/Programs/AutomationTool/Gauntlet/Framework/Gauntlet.Utils.cs index 13284153a254..4e30efee0889 100644 --- a/Engine/Source/Programs/AutomationTool/Gauntlet/Framework/Gauntlet.Utils.cs +++ b/Engine/Source/Programs/AutomationTool/Gauntlet/Framework/Gauntlet.Utils.cs @@ -768,24 +768,31 @@ namespace Gauntlet { DestDir = Directory.CreateDirectory(DestDir.FullName); } + + bool IsMirroring = (Options & CopyOptions.Mirror) == CopyOptions.Mirror; System.IO.FileInfo[] SourceFiles = SourceDir.GetFiles("*", SearchOption.AllDirectories); - System.IO.FileInfo[] DestFiles = DestDir.GetFiles("*", SearchOption.AllDirectories); + System.IO.FileInfo[] DestFiles = null; // Convert dest into a map of relative paths to absolute Dictionary DestStructure = new Dictionary(); - foreach (FileInfo Info in DestFiles) + if (IsMirroring) { - string RelativePath = Info.FullName.Replace(DestDir.FullName, ""); + DestFiles = DestDir.GetFiles("*", SearchOption.AllDirectories); - // remove leading seperator - if (RelativePath.First() == Path.DirectorySeparatorChar) + foreach (FileInfo Info in DestFiles) { - RelativePath = RelativePath.Substring(1); - } + string RelativePath = Info.FullName.Replace(DestDir.FullName, ""); - DestStructure[RelativePath] = Info; + // remove leading seperator + if (RelativePath.First() == Path.DirectorySeparatorChar) + { + RelativePath = RelativePath.Substring(1); + } + + DestStructure[RelativePath] = Info; + } } // List of relative-path files to copy to dest @@ -833,7 +840,7 @@ namespace Gauntlet } // If set to mirror, delete all the files that were not in source - if ((Options & CopyOptions.Mirror) == CopyOptions.Mirror) + if (IsMirroring) { // Now go through the remaining map items and delete them foreach (var Pair in DestStructure) diff --git a/Engine/Source/Programs/AutomationTool/Gauntlet/Unreal/Base/Gauntlet.UnrealTestNode.cs b/Engine/Source/Programs/AutomationTool/Gauntlet/Unreal/Base/Gauntlet.UnrealTestNode.cs index f0765242fe7a..46b0c352c339 100644 --- a/Engine/Source/Programs/AutomationTool/Gauntlet/Unreal/Base/Gauntlet.UnrealTestNode.cs +++ b/Engine/Source/Programs/AutomationTool/Gauntlet/Unreal/Base/Gauntlet.UnrealTestNode.cs @@ -84,6 +84,11 @@ namespace Gauntlet static protected DateTime SessionStartTime = DateTime.MinValue; + /// + /// Path to the directory that logs and other artifacts are copied to after the test run. + /// + protected string ArtifactPath { get; private set; } + /// /// Our test result. May be set directly, or by overriding GetUnrealTestResult() /// @@ -91,8 +96,6 @@ namespace Gauntlet protected TConfigClass CachedConfig = null; - private string CachedArtifactPath = null; - /// /// If our test should exit suddenly, this is the process that caused it /// @@ -115,6 +118,7 @@ namespace Gauntlet LastLogCount = 0; CurrentPass = 0; NumPasses = 0; + ArtifactPath = string.Empty; } ~UnrealTestNode() @@ -411,7 +415,33 @@ namespace Gauntlet TConfigClass Config = GetCachedConfiguration(); CurrentPass = Pass; - NumPasses = InNumPasses; + NumPasses = InNumPasses; + + string TestFolder = ToString(); + TestFolder = TestFolder.Replace(" ", "_"); + TestFolder = TestFolder.Replace(",", ""); + + ArtifactPath = Path.Combine(Context.Options.LogDir, TestFolder); + + // if doing multiple passes, put each in a subdir + if (NumPasses > 1) + { + ArtifactPath = Path.Combine(ArtifactPath, string.Format("Pass_{0}_of_{1}", CurrentPass, NumPasses)); + } + + // Basic pre-existing directory check. + if (CommandUtils.IsBuildMachine && Directory.Exists(ArtifactPath)) + { + string NewOutputPath = ArtifactPath; + int i = 0; + while (Directory.Exists(NewOutputPath)) + { + i++; + NewOutputPath = string.Format("{0}_{1}", ArtifactPath, i); + } + Log.Info("Directory already exists at {0}", ArtifactPath); + ArtifactPath = NewOutputPath; + } // Launch the test TestInstance = UnrealApp.LaunchSession(); @@ -526,42 +556,16 @@ namespace Gauntlet // access to these objects and their resources! Final cleanup is done in CleanupTest() TestInstance.Shutdown(); - //string TestFolder = string.Format("{0}-{1:yyyy.MM.dd-HH.mm}", Name, SessionStartTime); - string TestFolder = ToString(); - TestFolder = TestFolder.Replace(" ", "_"); - TestFolder = TestFolder.Replace(",", ""); - - string OutputPath = Path.Combine(Context.Options.LogDir, TestFolder); - - // if doing multiple passes, put each in a subdir - if (NumPasses > 1) - { - OutputPath = Path.Combine(OutputPath, string.Format("Pass_{0}_of_{1}", CurrentPass, NumPasses)); - } - try { - // Basic pre-existing directory check. - if (CommandUtils.IsBuildMachine && Directory.Exists(OutputPath)) - { - string NewOutputPath = OutputPath; - int i = 0; - while (Directory.Exists(NewOutputPath)) - { - i++; - NewOutputPath = string.Format("{0}_{1}", OutputPath, i); - } - Log.Info("Directory already exists at {0}", OutputPath); - OutputPath = NewOutputPath; - } - Log.Info("Saving artifacts to {0}", OutputPath); - Directory.CreateDirectory(OutputPath); - Utils.SystemHelpers.MarkDirectoryForCleanup(OutputPath); + Log.Info("Saving artifacts to {0}", ArtifactPath); + Directory.CreateDirectory(ArtifactPath); + Utils.SystemHelpers.MarkDirectoryForCleanup(ArtifactPath); - SessionArtifacts = SaveRoleArtifacts(OutputPath); + SessionArtifacts = SaveRoleArtifacts(ArtifactPath); // call legacy version - SaveArtifacts_DEPRECATED(OutputPath); + SaveArtifacts_DEPRECATED(ArtifactPath); } catch (Exception Ex) { @@ -580,7 +584,7 @@ namespace Gauntlet try { - CreateReport(GetTestResult(), Context, Context.BuildInfo, SessionArtifacts, OutputPath); + CreateReport(GetTestResult(), Context, Context.BuildInfo, SessionArtifacts, ArtifactPath); } catch (Exception Ex) { @@ -589,7 +593,7 @@ namespace Gauntlet try { - SubmitToDashboard(GetTestResult(), Context, Context.BuildInfo, SessionArtifacts, OutputPath); + SubmitToDashboard(GetTestResult(), Context, Context.BuildInfo, SessionArtifacts, ArtifactPath); } catch (Exception Ex) { @@ -640,8 +644,7 @@ namespace Gauntlet /// public virtual IEnumerable SaveRoleArtifacts(string OutputPath) { - CachedArtifactPath = OutputPath; - return UnrealApp.SaveRoleArtifacts(Context, TestInstance, CachedArtifactPath); + return UnrealApp.SaveRoleArtifacts(Context, TestInstance, ArtifactPath); } /// diff --git a/Engine/Source/Programs/AutomationTool/HTML5/HTML5Platform.Automation.cs b/Engine/Source/Programs/AutomationTool/HTML5/HTML5Platform.Automation.cs index 4a61c5b2de9f..1de3e9556764 100644 --- a/Engine/Source/Programs/AutomationTool/HTML5/HTML5Platform.Automation.cs +++ b/Engine/Source/Programs/AutomationTool/HTML5/HTML5Platform.Automation.cs @@ -587,19 +587,20 @@ public class HTML5Platform : Platform string WorkingDirectory = Path.GetDirectoryName(ClientApp); string url = Path.GetFileName(ClientApp) +".html"; - // WARNING: splitting the following situation - // if cookonthefly is used: tell the browser to use the PROXY at DEFAULT_HTTP_FILE_SERVING_PORT - // leave the normal HTML5LaunchHelper port (ServerPort) alone -- otherwise it will collide with the PROXY port +// UE-64628: seems proxy port is no longer used anymore -- leaving details here for future reference... +// // WARNING: splitting the following situation +// // if cookonthefly is used: tell the browser to use the PROXY at DEFAULT_HTTP_FILE_SERVING_PORT +// // leave the normal HTML5LaunchHelper port (ServerPort) alone -- otherwise it will collide with the PROXY port if (ClientCmdLine.Contains("filehostip")) { url += "?cookonthefly=true"; - Int32 ProxyPort = 41898; // DEFAULT_HTTP_FILE_SERVING_PORT - url = String.Format("http://localhost:{0}/{1}", ProxyPort, url); +// Int32 ProxyPort = 41898; // DEFAULT_HTTP_FILE_SERVING_PORT +// url = String.Format("http://localhost:{0}/{1}", ProxyPort, url); } - else - { +// else +// { url = String.Format("http://localhost:{0}/{1}", ServerPort, url); - } +// } // Check HTML5LaunchHelper source for command line args diff --git a/Engine/Source/Programs/AutomationTool/Program.cs b/Engine/Source/Programs/AutomationTool/Program.cs index 864adf742773..32b78c4a3dd1 100644 --- a/Engine/Source/Programs/AutomationTool/Program.cs +++ b/Engine/Source/Programs/AutomationTool/Program.cs @@ -89,8 +89,22 @@ namespace AutomationTool } catch (AutomationException Ex) { + // Output the message in the desired format + if(Ex.OutputFormat == AutomationExceptionOutputFormat.Silent) + { + Log.TraceLog("{0}", ExceptionUtils.FormatExceptionDetails(Ex)); + } + else if(Ex.OutputFormat == AutomationExceptionOutputFormat.Minimal) + { + Log.TraceInformation("{0}", Ex.ToString().Replace("\n", "\n ")); + Log.TraceLog("{0}", ExceptionUtils.FormatExceptionDetails(Ex)); + } + else + { + Log.WriteException(Ex, LogUtils.FinalLogFileName); + } + // Take the exit code from the exception - Log.WriteException(Ex, LogUtils.FinalLogFileName); ReturnCode = Ex.ErrorCode; } catch (Exception Ex) diff --git a/Engine/Source/Programs/AutomationTool/Scripts/BuildProjectCommand.Automation.cs b/Engine/Source/Programs/AutomationTool/Scripts/BuildProjectCommand.Automation.cs index 6cf8c3871ff3..39f748b778d4 100644 --- a/Engine/Source/Programs/AutomationTool/Scripts/BuildProjectCommand.Automation.cs +++ b/Engine/Source/Programs/AutomationTool/Scripts/BuildProjectCommand.Automation.cs @@ -124,7 +124,10 @@ public partial class Project : CommandUtils if (string.IsNullOrEmpty(Params.UbtArgs) == false) { - AdditionalArgs += " " + Params.UbtArgs; + string Arg = Params.UbtArgs; + Arg = Arg.TrimStart(new char[] { '\"' }); + Arg = Arg.TrimEnd(new char[] { '\"' }); + AdditionalArgs += " " + Arg; } if (Params.MapFile) @@ -137,6 +140,11 @@ public partial class Project : CommandUtils AdditionalArgs += " -skipdeploy"; // skip deploy step in UBT if we going to do it later anyway } + if (Params.Distribution) + { + AdditionalArgs += " -distribution"; + } + // Config overrides (-ini) foreach (string ConfigOverrideParam in Params.ConfigOverrideParams) { diff --git a/Engine/Source/Programs/AutomationTool/Scripts/CopyBuildToStagingDirectory.Automation.cs b/Engine/Source/Programs/AutomationTool/Scripts/CopyBuildToStagingDirectory.Automation.cs index 11e5f0ac375f..5e2c495b6ff0 100644 --- a/Engine/Source/Programs/AutomationTool/Scripts/CopyBuildToStagingDirectory.Automation.cs +++ b/Engine/Source/Programs/AutomationTool/Scripts/CopyBuildToStagingDirectory.Automation.cs @@ -891,7 +891,21 @@ public partial class Project : CommandUtils } // Remap all the non-ufs files if not using a PAK file - SC.FilesToStage.NonUFSFiles = SC.FilesToStage.NonUFSFiles.ToDictionary(x => SC.StageTargetPlatform.Remap(x.Key), x => x.Value); + if (Params.HasIterateSharedCookedBuild) + { + // Shared NonUFS files are staged in their remapped location, and may be duplicated in the to-stage list. + Dictionary NonUFSToStage = new Dictionary(); + foreach (KeyValuePair StagedFilePair in SC.FilesToStage.NonUFSFiles) + { + NonUFSToStage[SC.StageTargetPlatform.Remap(StagedFilePair.Key)] = StagedFilePair.Value; + } + SC.FilesToStage.NonUFSFiles = NonUFSToStage; + } + else + { + SC.FilesToStage.NonUFSFiles = SC.FilesToStage.NonUFSFiles.ToDictionary(x => SC.StageTargetPlatform.Remap(x.Key), x => x.Value); + } + if (!Params.UsePak(SC.StageTargetPlatform)) { SC.FilesToStage.UFSFiles = SC.FilesToStage.UFSFiles.ToDictionary(x => SC.StageTargetPlatform.Remap(x.Key), x => x.Value); @@ -1351,6 +1365,8 @@ public partial class Project : CommandUtils List RulesList = new List(); foreach (string SectionName in PakRulesConfig.SectionNames) { + //LogInformation("Building PakFileRules for Section {0}", SectionName); + string PlatformString, TargetString, PakString; if (PakRulesConfig.TryGetValue(SectionName, "Platforms", out PlatformString)) @@ -1361,12 +1377,12 @@ public partial class Project : CommandUtils // Check platform string foreach (string Platform in PlatformStrings) { - if (SC.StageTargetPlatform.PlatformType.ToString() == Platform) + if (SC.StageTargetPlatform.PlatformType.ToString().Equals(Platform, StringComparison.OrdinalIgnoreCase)) { bMatches = true; break; } - else if (SC.StageTargetPlatform.IniPlatformType.ToString() == Platform) + else if (SC.StageTargetPlatform.IniPlatformType.ToString().Equals(Platform, StringComparison.OrdinalIgnoreCase)) { bMatches = true; break; @@ -1375,6 +1391,7 @@ public partial class Project : CommandUtils if (!bMatches) { + //LogInformation("No matching platform for PakFileRules for Section {0} : {1}, {2}", SectionName, SC.StageTargetPlatform.PlatformType.ToString(), SC.StageTargetPlatform.IniPlatformType.ToString()); continue; } } @@ -1441,6 +1458,7 @@ public partial class Project : CommandUtils PakRules.Filter = new FileFilter(); foreach (string FileFilter in FilesEnumberable) { + //LogInformation("Adding to PakFileRules for Section {0} : {1}", SectionName, FileFilter); PakRules.Filter.AddRule(FileFilter); } @@ -1927,9 +1945,16 @@ public partial class Project : CommandUtils } } } - } + } - Commands.Add(GetUnrealPakArguments(PakParams.UnrealPakResponseFile, OutputLocation, PrimaryOrderFile, SC.StageTargetPlatform.GetPlatformPakCommandLine(Params, SC) + BulkOption + CompressionFormats + " " + Params.AdditionalPakOptions, PakParams.bCompressed, CryptoSettings, CryptoKeysCacheFilename, PatchSourceContentPath, PakParams.EncryptionKeyGuid, SecondaryOrderFile)); + string PatchSeekOptMaxGapSizeOption = String.Empty; + string PatchSeekOptMaxGapSize = String.Empty; + if (PlatformGameConfig.GetString("/Script/UnrealEd.ProjectPackagingSettings", "PatchSeekOptMaxGapSize", out PatchSeekOptMaxGapSize)) + { + PatchSeekOptMaxGapSizeOption = String.Format(" -patchSeekOptMaxGapSize={0}", PatchSeekOptMaxGapSize); + } + + Commands.Add(GetUnrealPakArguments(PakParams.UnrealPakResponseFile, OutputLocation, PrimaryOrderFile, SC.StageTargetPlatform.GetPlatformPakCommandLine(Params, SC) + PatchSeekOptMaxGapSizeOption + BulkOption + CompressionFormats + " " + Params.AdditionalPakOptions, PakParams.bCompressed, CryptoSettings, CryptoKeysCacheFilename, PatchSourceContentPath, PakParams.EncryptionKeyGuid, SecondaryOrderFile)); LogNames.Add(OutputLocation.GetFileNameWithoutExtension()); } } diff --git a/Engine/Source/Programs/AutomationTool/Scripts/SharedCookedBuild.cs b/Engine/Source/Programs/AutomationTool/Scripts/SharedCookedBuild.cs index 4dd3283b50d0..0edbef386c47 100644 --- a/Engine/Source/Programs/AutomationTool/Scripts/SharedCookedBuild.cs +++ b/Engine/Source/Programs/AutomationTool/Scripts/SharedCookedBuild.cs @@ -1,4 +1,4 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; @@ -15,20 +15,32 @@ public class SharedCookedBuild { private static Task CopySharedCookedBuildTask = null; - private static bool FindBestSharedCookedBuild(ref string FinalCookedBuildPath, string ProjectFullPath, UnrealTargetPlatform TargetPlatform, string CookPlatform, string SharedCookedBuildCL ) + public enum SharedCookType + { + Exact, + RecentContentOnly, + Recent + } + + public static bool FindBestSharedCookedBuild(ref string FinalCookedBuildPath, string ProjectFullPath, UnrealTargetPlatform TargetPlatform, string CookPlatform, SharedCookType BuildType) { string BuildRoot = CommandUtils.P4Enabled ? CommandUtils.P4Env.Branch.Replace("/", "+") : ""; int CurrentCLInt = CommandUtils.P4Enabled ? CommandUtils.P4Env.Changelist : 0; + int CurrentCodeCLInt = CommandUtils.P4Enabled ? CommandUtils.P4Env.CodeChangelist : 0; + BuildVersion Version; if (BuildVersion.TryRead(BuildVersion.GetDefaultFileName(), out Version)) { CurrentCLInt = Version.Changelist; + CurrentCodeCLInt = Version.EffectiveCompatibleChangelist; BuildRoot = Version.BranchName; } System.GC.Collect(); string CurrentCL = CurrentCLInt.ToString(); + CommandUtils.LogInformation("Search for best shared cooked build..."); + CommandUtils.LogInformation("CurrentCL: {0}, Type: {1}", CurrentCodeCLInt, BuildType); FileReference ProjectFileRef = new FileReference(ProjectFullPath); // get network location @@ -43,7 +55,7 @@ public class SharedCookedBuild const string MetaDataFilename = "\\Metadata\\DevelopmentAssetRegistry.bin"; - if (SharedCookedBuildCL == "usesyncedbuild") + if (BuildType == SharedCookType.Exact) { foreach (string CookedBuildPath in CookedBuildPaths) { @@ -74,13 +86,10 @@ public class SharedCookedBuild } } } - else if (SharedCookedBuildCL == "userecentbuild") + else if (BuildType == SharedCookType.Recent || BuildType == SharedCookType.RecentContentOnly) { - // build our CookedBUildPath into a regex which we can execute on the directories and extract info from - - string BestBuild = null; int BestCLNumber = 0; @@ -141,15 +150,23 @@ public class SharedCookedBuild } } } - } if ( string.IsNullOrEmpty(BestBuild) ) { + CommandUtils.LogError("Cannot find a recent shared cooked build"); + return false; + } + + if (BuildType == SharedCookType.RecentContentOnly && BestCLNumber < CurrentCodeCLInt) + { + CommandUtils.LogError("Cannot find a recent shared cooked build without code changes"); + CommandUtils.LogError("Your CL: {0}, Last code CL: {1}, Best fit: {2}", CurrentCLInt, CurrentCodeCLInt, BestCLNumber); return false; } FinalCookedBuildPath = BestBuild; + CommandUtils.LogInformation("Selected build: {0}", BestCLNumber); return true; } @@ -241,22 +258,32 @@ public class SharedCookedBuild return true; } - public static void CopySharedCookedBuildForTarget(string ProjectFullPath, UnrealTargetPlatform TargetPlatform, string CookPlatform, string BuildCl, bool bOnlyCopyAssetRegistry = false) + public static void CopySharedCookedBuildForTarget(string ProjectFullPath, UnrealTargetPlatform TargetPlatform, string CookPlatform, string BuildCL, bool bOnlyCopyAssetRegistry = false) { var LocalPath = CommandUtils.CombinePaths(Path.GetDirectoryName(ProjectFullPath), "Saved", "SharedIterativeBuild", CookPlatform); - + + SharedCookType SharedBuildType = SharedCookType.Exact; + if (BuildCL.Equals("userecentbuild")) + { + SharedBuildType = SharedCookType.Recent; + } + else if (BuildCL.Equals("userecentcontentonly")) + { + SharedBuildType = SharedCookType.RecentContentOnly; + } + string CookedBuildPath = null; - if ( FindBestSharedCookedBuild(ref CookedBuildPath, ProjectFullPath, TargetPlatform, CookPlatform, BuildCl) ) + if ( FindBestSharedCookedBuild(ref CookedBuildPath, ProjectFullPath, TargetPlatform, CookPlatform, SharedBuildType) ) { CopySharedCookedBuildForTargetInternal(CookedBuildPath, CookPlatform, LocalPath, bOnlyCopyAssetRegistry); } - else if( FindBestSharedCookedBuild(ref CookedBuildPath, ProjectFullPath, TargetPlatform, TargetPlatform.ToString(), BuildCl) ) + else if( FindBestSharedCookedBuild(ref CookedBuildPath, ProjectFullPath, TargetPlatform, TargetPlatform.ToString(), SharedBuildType) ) { CopySharedCookedBuildForTargetInternal(CookedBuildPath, TargetPlatform.ToString(), LocalPath, bOnlyCopyAssetRegistry); } else { - throw new AutomationException("Can't find cooked build for {0} {1} {2}", BuildCl, CookPlatform, TargetPlatform.ToString()); + throw new AutomationException("Can't find cooked build for {0} {1} {2}", SharedBuildType, CookPlatform, TargetPlatform.ToString()); } return; diff --git a/Engine/Source/Programs/IOS/iPhonePackager/MobileProvisionUtilities.cs b/Engine/Source/Programs/IOS/iPhonePackager/MobileProvisionUtilities.cs index b675932727cb..6f68993b5e82 100644 --- a/Engine/Source/Programs/IOS/iPhonePackager/MobileProvisionUtilities.cs +++ b/Engine/Source/Programs/IOS/iPhonePackager/MobileProvisionUtilities.cs @@ -341,37 +341,47 @@ namespace iPhonePackager // For distribution builds, the entitlements from mobileprovisioning have a modified syntax if (Config.bForDistribution) { + // remove the wildcards from the ubiquity-kvstore-identifier string if (XCentPList.HasKey("com.apple.developer.ubiquity-kvstore-identifier")) { string UbiquityKvstoreString; XCentPList.GetString("com.apple.developer.ubiquity-kvstore-identifier", out UbiquityKvstoreString); - if (UbiquityKvstoreString.Contains("*")) + int DotPosition = UbiquityKvstoreString.LastIndexOf("*"); + if (DotPosition >= 0) { - string NewUbiquityKvstoreIdentifier = String.Format("{0}.{1}", TeamIdentifier, CFBundleIdentifier); + string TeamPrefix = DotPosition > 1 ? UbiquityKvstoreString.Substring(0, DotPosition - 1) : TeamIdentifier; + string NewUbiquityKvstoreIdentifier = String.Format("{0}.{1}", TeamPrefix, CFBundleIdentifier); XCentPList.SetValueForKey("com.apple.developer.ubiquity-kvstore-identifier", NewUbiquityKvstoreIdentifier); } } + + // remove the wildcards from the ubiquity-container-identifiers array if (XCentPList.HasKey("com.apple.developer.ubiquity-container-identifiers")) { List UbiquityContainerIdentifiersGroups = XCentPList.GetArray("com.apple.developer.ubiquity-container-identifiers", "string"); - if (UbiquityContainerIdentifiersGroups.Count == 0 || UbiquityContainerIdentifiersGroups[0].Contains("*")) + for (int i = 0; i < UbiquityContainerIdentifiersGroups.Count; i++) { - UbiquityContainerIdentifiersGroups.Clear(); - - string UbiquityContainerString; - XCentPList.GetString("com.apple.developer.ubiquity-container-identifiers", out UbiquityContainerString); - - if (UbiquityContainerString.Contains("*")) + int DotPosition = UbiquityContainerIdentifiersGroups[i].LastIndexOf("*"); + if (DotPosition >= 0) { - string NewUbiquityContainerIdentifier = String.Format("{0}.{1}", TeamIdentifier, CFBundleIdentifier); - UbiquityContainerIdentifiersGroups.Add(NewUbiquityContainerIdentifier); + string TeamPrefix = DotPosition > 1 ? UbiquityContainerIdentifiersGroups[i].Substring(0, DotPosition - 1) : TeamIdentifier; + string NewUbiquityContainerIdentifier = String.Format("{0}.{1}", TeamPrefix, CFBundleIdentifier); + UbiquityContainerIdentifiersGroups[i] = NewUbiquityContainerIdentifier; } } + if (UbiquityContainerIdentifiersGroups.Count == 0) + { + string NewUbiquityKvstoreIdentifier = String.Format("{0}.{1}", TeamIdentifier, CFBundleIdentifier); + UbiquityContainerIdentifiersGroups.Add(NewUbiquityKvstoreIdentifier); + } + XCentPList.SetValueForKey("com.apple.developer.ubiquity-container-identifiers", UbiquityContainerIdentifiersGroups); } + + // remove the wildcards from the developer.associated-domains array or string if (XCentPList.HasKey("com.apple.developer.associated-domains")) { List AssociatedDomainsGroup = XCentPList.GetArray("com.apple.developer.associated-domains", "string"); @@ -390,16 +400,17 @@ namespace iPhonePackager // remove development keys - generated when the cloudkit container is in development mode XCentPList.RemoveKeyValue("com.apple.developer.icloud-container-development-container-identifiers"); - if (XCentPList.HasKey("com.apple.developer.icloud-container-environment")) + } + + // set the icloud-container-environment accordign to project settings + if (XCentPList.HasKey("com.apple.developer.icloud-container-environment")) + { + List ContainerEnvironmentGroup = XCentPList.GetArray("com.apple.developer.icloud-container-environment", "string"); + + if (ContainerEnvironmentGroup.Count != 0) { - List ContainerEnvironmentGroup = XCentPList.GetArray("com.apple.developer.icloud-container-environment", "string"); - - if (ContainerEnvironmentGroup.Count == 0 || ContainerEnvironmentGroup[0].Equals("Development")) - { - ContainerEnvironmentGroup.Clear(); - ContainerEnvironmentGroup.Add("Production"); - } - + ContainerEnvironmentGroup.Clear(); + ContainerEnvironmentGroup.Add(Config.bForDistribution ? "Production" : "Development"); XCentPList.SetValueForKey("com.apple.developer.icloud-container-environment", ContainerEnvironmentGroup); } } diff --git a/Engine/Source/Programs/IOS/iPhonePackager/iPhonePackager.cs b/Engine/Source/Programs/IOS/iPhonePackager/iPhonePackager.cs index b2074bff614b..0a575e9fb69f 100644 --- a/Engine/Source/Programs/IOS/iPhonePackager/iPhonePackager.cs +++ b/Engine/Source/Programs/IOS/iPhonePackager/iPhonePackager.cs @@ -682,11 +682,19 @@ namespace iPhonePackager } bVisualsStarted = true; + // make the form look good on modern displays! + if (Environment.OSVersion.Version.Major >= 6) + { + SetProcessDPIAware(); + } + Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); ProgressDialog = new SlowProgressDialog(); } + [System.Runtime.InteropServices.DllImport("user32.dll")] + private static extern bool SetProcessDPIAware(); static void RunInVisualMode(CreateFormDelegate Work) { diff --git a/Engine/Source/Programs/PixelStreaming/WebServers/SignallingWebServer/scripts/app.js b/Engine/Source/Programs/PixelStreaming/WebServers/SignallingWebServer/scripts/app.js index a9f7ea938ce2..81aac08061fc 100644 --- a/Engine/Source/Programs/PixelStreaming/WebServers/SignallingWebServer/scripts/app.js +++ b/Engine/Source/Programs/PixelStreaming/WebServers/SignallingWebServer/scripts/app.js @@ -14,6 +14,7 @@ var matchViewportResolution; var lastTimeResized = new Date().getTime(); var resizeTimeout; +var onDataChannelConnected; var responseEventListeners = new Map(); var t0 = Date.now(); @@ -276,6 +277,9 @@ function setupWebRtcPlayer(htmlElement, clientConfig){ webRtcPlayerObj.onDataChannelConnected = function(){ showTextOverlay('WebRTC connected, waiting for video'); + if (onDataChannelConnected) { + onDataChannelConnected(); + } } webRtcPlayerObj.onDataChannelMessage = function (data) { diff --git a/Engine/Source/Programs/UnrealBuildTool/Configuration/TargetRules.cs b/Engine/Source/Programs/UnrealBuildTool/Configuration/TargetRules.cs index fb794678cd16..0e5b0a1dd324 100644 --- a/Engine/Source/Programs/UnrealBuildTool/Configuration/TargetRules.cs +++ b/Engine/Source/Programs/UnrealBuildTool/Configuration/TargetRules.cs @@ -668,7 +668,7 @@ namespace UnrealBuildTool /// Disables force-included PCHs for files that are in the adaptive non-unity working set. /// [XmlConfigFile(Category = "BuildConfiguration")] - public bool bAdaptiveUnityDisablesPCH = true; + public bool bAdaptiveUnityDisablesPCH = false; /// /// Backing storage for bAdaptiveUnityDisablesProjectPCH. diff --git a/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildBinary.cs b/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildBinary.cs index 0b6749c2bf6b..38fe69431611 100644 --- a/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildBinary.cs +++ b/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildBinary.cs @@ -201,6 +201,12 @@ namespace UnrealBuildTool FileItem[] Executables = ToolChain.LinkAllFiles(BinaryLinkEnvironment, false, Makefile.Actions); OutputFiles.AddRange(Executables); + // Save all the output items for this binary. This is used for hot-reload, and excludes any items added in PostBuild (such as additional files copied into the app). + if(Target.LinkType == TargetLinkType.Modular) + { + Makefile.ModuleNameToOutputItems[PrimaryModule.Name] = OutputFiles.ToArray(); + } + // Produce additional console app if requested if (bBuildAdditionalConsoleApp) { diff --git a/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildTarget.cs b/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildTarget.cs index d3de6f3870d7..7099895367f3 100644 --- a/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildTarget.cs +++ b/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildTarget.cs @@ -1314,10 +1314,6 @@ namespace UnrealBuildTool foreach (UEBuildBinary Binary in Binaries) { List BinaryOutputItems = Binary.Build(Rules, TargetToolChain, GlobalCompileEnvironment, GlobalLinkEnvironment, WorkingSet, ExeDir, Makefile); - if(!bCompileMonolithic) - { - Makefile.ModuleNameToOutputItems[Binary.PrimaryModule.Name] = BinaryOutputItems.ToArray(); - } Makefile.OutputItems.AddRange(BinaryOutputItems); } } @@ -1604,7 +1600,7 @@ namespace UnrealBuildTool } else { - CopyAction.CommandArguments = String.Format("-c 'cp -f {0} {1}'", Utils.EscapeShellArgument(SourceFile.FullName), Utils.EscapeShellArgument(TargetFile.FullName)); + CopyAction.CommandArguments = String.Format("-c 'cp -f \"{0}\" \"{1}\"'", SourceFile.FullName, TargetFile.FullName); } CopyAction.WorkingDirectory = UnrealBuildTool.EngineSourceDirectory; CopyAction.PrerequisiteItems.Add(SourceFileItem); @@ -2088,14 +2084,17 @@ namespace UnrealBuildTool private HashSet GetHotReloadModuleNames() { HashSet HotReloadModuleNames = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (UEBuildBinary Binary in Binaries) + if (!ShouldCompileMonolithic()) { - List GameModules = Binary.FindGameModules(); - if (GameModules != null && GameModules.Count > 0) + foreach (UEBuildBinary Binary in Binaries) { - if(!UnrealBuildTool.IsProjectInstalled() || EnabledPlugins.Where(x => x.Type == PluginType.Mod).Any(x => Binary.OutputFilePaths[0].IsUnderDirectory(x.Directory))) + List GameModules = Binary.FindGameModules(); + if (GameModules != null && GameModules.Count > 0) { - HotReloadModuleNames.UnionWith(GameModules.OfType().Select(x => x.Name)); + if (!UnrealBuildTool.IsProjectInstalled() || EnabledPlugins.Where(x => x.Type == PluginType.Mod).Any(x => Binary.OutputFilePaths[0].IsUnderDirectory(x.Directory))) + { + HotReloadModuleNames.UnionWith(GameModules.OfType().Select(x => x.Name)); + } } } } @@ -2623,6 +2622,7 @@ namespace UnrealBuildTool ReferencedNames.Add(Plugin.Name); PluginReferenceDescriptor PluginReference = new PluginReferenceDescriptor(Plugin.Name, null, true); + PluginReference.SupportedTargetPlatforms = Plugin.Descriptor.SupportedTargetPlatforms; PluginReference.bOptional = true; AddPlugin(PluginReference, "default plugins", ExcludeFolders, NameToInstance, NameToInfo); @@ -2727,8 +2727,7 @@ namespace UnrealBuildTool // Disable any plugin which does not support the target platform. The editor should update such references in the .uproject file on load. if (!Rules.bIncludePluginsForTargetPlatforms && !Info.Descriptor.SupportsTargetPlatform(Platform)) { - Log.TraceLog("Ignoring plugin '{0}' (referenced via {1}) due to target platform not supported by descriptor.", Reference.Name, ReferenceChain); - return null; + throw new BuildException("{0} is referenced via {1} with a mismatched 'SupportedTargetPlatforms' field. This will cause problems in packaged builds, because the .uplugin file will not be staged. Launch the editor to update references from your project file, or update references from other plugins manually.", Info.File.GetFileName(), ReferenceChain); } // Disable any plugin that requires the build platform diff --git a/Engine/Source/Programs/UnrealBuildTool/Modes/GenerateProjectFilesMode.cs b/Engine/Source/Programs/UnrealBuildTool/Modes/GenerateProjectFilesMode.cs index 4c9f2d6fdfe5..29d3774fb566 100644 --- a/Engine/Source/Programs/UnrealBuildTool/Modes/GenerateProjectFilesMode.cs +++ b/Engine/Source/Programs/UnrealBuildTool/Modes/GenerateProjectFilesMode.cs @@ -143,7 +143,7 @@ namespace UnrealBuildTool Generator = new VCProjectFileGenerator(ProjectFile, VCProjectFileFormat.VisualStudio2019, Arguments); break; case ProjectFileFormat.XCode: - Generator = new XcodeProjectFileGenerator(ProjectFile); + Generator = new XcodeProjectFileGenerator(ProjectFile, Arguments); break; case ProjectFileFormat.Eddie: Generator = new EddieProjectFileGenerator(ProjectFile); diff --git a/Engine/Source/Programs/UnrealBuildTool/Platform/Android/UEDeployAndroid.cs b/Engine/Source/Programs/UnrealBuildTool/Platform/Android/UEDeployAndroid.cs index 84587c330f1c..c89667c295dd 100644 --- a/Engine/Source/Programs/UnrealBuildTool/Platform/Android/UEDeployAndroid.cs +++ b/Engine/Source/Programs/UnrealBuildTool/Platform/Android/UEDeployAndroid.cs @@ -860,8 +860,9 @@ namespace UnrealBuildTool bool bUseChangeListAsStoreVersion = false; Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bUseChangeListAsStoreVersion", out bUseChangeListAsStoreVersion); + bool IsBuildMachine = Environment.GetEnvironmentVariable("IsBuildMachine") == "1"; // override store version with changelist if enabled and is build machine - if (bUseChangeListAsStoreVersion && Environment.GetEnvironmentVariable("IsBuildMachine") == "1") + if (bUseChangeListAsStoreVersion && IsBuildMachine) { // make sure changelist is cached string EngineVersion = ReadEngineVersion(); @@ -876,6 +877,8 @@ namespace UnrealBuildTool } } + Log.TraceInformation("GotStoreVersion found v{0}. (bUseChangeListAsStoreVersion={1} IsBuildMachine={2} EngineChangeList={3})", StoreVersion, bUseChangeListAsStoreVersion, IsBuildMachine, EngineChangelist); + CachedStoreVersion = StoreVersion; } @@ -3702,6 +3705,10 @@ namespace UnrealBuildTool if (bSkipGradleBuild) { FinalSOName = OutputPath; + if (!File.Exists(FinalSOName)) + { + Log.TraceWarning("Did not find compiled .so [{0}]", FinalSOName); + } } else { @@ -3995,7 +4002,7 @@ namespace UnrealBuildTool bool bSaveSymbols = false; Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildWithHiddenSymbolVisibility", out bBuildWithHiddenSymbolVisibility); Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bSaveSymbols", out bSaveSymbols); -bSaveSymbols = true; + bSaveSymbols = true; if (bSaveSymbols || (Configuration == UnrealTargetConfiguration.Shipping && bBuildWithHiddenSymbolVisibility)) { // Copy .so with symbols to @@ -4003,6 +4010,7 @@ bSaveSymbols = true; string SymbolSODirectory = Path.Combine(DestApkDirectory, ProjectName + "_Symbols_v" + StoreVersion + "/" + ProjectName + Arch + GPUArchitecture); string SymbolifiedSOPath = Path.Combine(SymbolSODirectory, Path.GetFileName(FinalSOName)); MakeDirectoryIfRequired(SymbolifiedSOPath); + Log.TraceInformation("Writing symbols to {0}", SymbolifiedSOPath); File.Copy(FinalSOName, SymbolifiedSOPath, true); } @@ -4071,14 +4079,19 @@ bSaveSymbols = true; // get the receipt SetAndroidPluginData(ToolChain.GetAllArchitectures(), CollectPluginDataPaths(Receipt)); + bool bShouldCompileAsDll = Receipt.HasValueForAdditionalProperty("CompileAsDll", "true"); + // Get the output paths - List OutputPaths = Receipt.BuildProducts.Where(x => x.Type == BuildProductType.Executable).Select(x => x.Path).ToList(); + BuildProductType ProductType = bShouldCompileAsDll ? BuildProductType.DynamicLibrary : BuildProductType.Executable; + List OutputPaths = Receipt.BuildProducts.Where(x => x.Type == ProductType).Select(x => x.Path).ToList(); + if (OutputPaths.Count < 1) + { + throw new BuildException("Target file does not contain either executable or dynamic library .so"); + } // we need to strip architecture from any of the output paths string BaseSoName = ToolChain.RemoveArchName(OutputPaths[0].FullName); - bool bShouldCompileAsDll = Receipt.HasValueForAdditionalProperty("CompileAsDll", "true"); - // make an apk at the end of compiling, so that we can run without packaging (debugger, cook on the fly, etc) string RelativeEnginePath = UnrealBuildTool.EngineDirectory.MakeRelativeTo(DirectoryReference.GetCurrentDirectory()); diff --git a/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/IOSPostBuildSyncMode.cs b/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/IOSPostBuildSyncMode.cs index 86122e2cbb01..bba170ed1c46 100644 --- a/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/IOSPostBuildSyncMode.cs +++ b/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/IOSPostBuildSyncMode.cs @@ -28,6 +28,7 @@ namespace UnrealBuildTool public string ImportCertificate; public string ImportCertificatePassword; public Dictionary FrameworkNameToSourceDir; + public bool bForDistribution = false; public IOSPostBuildSyncTarget(ReadOnlyTargetRules Target, FileReference OutputPath, DirectoryReference ProjectIntermediateDirectory, List UPLScripts, VersionNumber SdkVersion, Dictionary FrameworkNameToSourceDir) { @@ -47,6 +48,7 @@ namespace UnrealBuildTool this.ImportCertificate = Target.IOSPlatform.ImportCertificate; this.ImportCertificatePassword = Target.IOSPlatform.ImportCertificatePassword; this.FrameworkNameToSourceDir = FrameworkNameToSourceDir; + this.bForDistribution = Target.IOSPlatform.bForDistribution; } } diff --git a/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/IOSToolChain.cs b/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/IOSToolChain.cs index 0870484da0ac..e775bc207aa3 100644 --- a/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/IOSToolChain.cs +++ b/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/IOSToolChain.cs @@ -9,6 +9,7 @@ using System.IO; using System.Diagnostics; using System.Security.AccessControl; using System.Xml; +using System.Xml.Linq; using System.Text; using Ionic.Zip; using Ionic.Zlib; @@ -1240,25 +1241,21 @@ namespace UnrealBuildTool } // copy the icons from the game directory if it has any string[][] Images = { - new string []{ "Icon_Large_Back-1280x768.png", "AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset" }, - new string []{ "Icon_Large_Front-1280x768.png", "AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset" }, - new string []{ "Icon_Large_Middle-1280x768.png", "AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset" }, - new string []{ "Icon_Small_Back-400x240.png", "AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset" }, - new string []{ "Icon_Small_Back-800x480.png", "AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset" }, - new string []{ "Icon_Small_Front-400x240.png", "AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset" }, - new string []{ "Icon_Small_Front-800x480.png", "AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset" }, - new string []{ "Icon_Small_Middle-400x240.png", "AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset" }, - new string []{ "Icon_Small_Middle-800x480.png", "AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset" }, - //new string []{ "TopShelfMarketWide.png", "AppIcon.brandassets/Top Shelf Image Wide.imageset" }, - //new string []{ "TopShelfMarketWide@2x.png", "AppIcon.brandassets/Top Shelf Image Wide.imageset" }, - new string []{ "TopShelfWide-1920x720@2x.png", "AppIcon.brandassets/Top Shelf Image Wide.imageset" }, - new string []{ "TopShelfWide-1920x720.png", "AppIcon.brandassets/Top Shelf Image Wide.imageset" }, - //new string []{ "TopShelfMarket.png", "AppIcon.brandassets/Top Shelf Image.imageset" }, - //new string []{ "TopShelfMarket@2x.png", "AppIcon.brandassets/Top Shelf Image.imageset" }, - new string []{ "TopShelf.png", "AppIcon.brandassets/Top Shelf Image.imageset" }, - new string []{ "TopShelf@2x.png", "AppIcon.brandassets/Top Shelf Image.imageset" }, - new string []{ "Launch.png", "LaunchImage.launchimage" }, - new string []{ "Launch@2x.png", "LaunchImage.launchimage" }, + new string []{ "Icon_Large_Back-1280x768.png", "App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset" }, + new string []{ "Icon_Large_Front-1280x768.png", "App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset" }, + new string []{ "Icon_Large_Middle-1280x768.png", "App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset" }, + new string []{ "Icon_Small_Back-400x240.png", "App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset" }, + new string []{ "Icon_Small_Back-800x480.png", "App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset" }, + new string []{ "Icon_Small_Front-400x240.png", "App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset" }, + new string []{ "Icon_Small_Front-800x480.png", "App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset" }, + new string []{ "Icon_Small_Middle-400x240.png", "App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset" }, + new string []{ "Icon_Small_Middle-800x480.png", "App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset" }, + new string []{ "TopShelfWide-1920x720@2x.png", "App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset" }, + new string []{ "TopShelfWide-1920x720.png", "App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset" }, + new string []{ "TopShelf.png", "App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset" }, + new string []{ "TopShelf@2x.png", "App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset" }, + new string []{ "Launch.png", "Launch Image.launchimage" }, + new string []{ "Launch@2x.png", "Launch Image.launchimage" }, }; Dir = Path.Combine(IntermediateDir, "Resources", "Assets.xcassets"); @@ -1354,9 +1351,9 @@ namespace UnrealBuildTool List OutputFiles = new List(base.PostBuild(Executable, BinaryLinkEnvironment, Actions)); if (BinaryLinkEnvironment.bIsBuildingLibrary) - { - return OutputFiles; - } + { + return OutputFiles; + } // For IOS/tvOS, generate the dSYM file if the config file is set to do so if (ProjectSettings.bGeneratedSYMFile == true || ProjectSettings.bGeneratedSYMBundle == true || BinaryLinkEnvironment.bUsePDBFiles == true) @@ -1373,10 +1370,13 @@ namespace UnrealBuildTool { FileItem StripCompleteFile = FileItem.GetItemByFileReference(FileReference.Combine(BinaryLinkEnvironment.IntermediateDirectory, Executable.Location.GetFileName() + ".stripped")); + // If building a framework we can only strip local symbols, need to leave global in place + string StripArguments = BinaryLinkEnvironment.bIsBuildingDLL ? "-x" : ""; + Action StripAction = new Action(ActionType.CreateAppBundle); StripAction.WorkingDirectory = GetMacDevSrcRoot(); StripAction.CommandPath = BuildHostPlatform.Current.Shell; - StripAction.CommandArguments = String.Format("-c '\"{0}strip\" \"{1}\" && touch \"{2}\"'", Settings.Value.ToolchainDir, Executable.Location, StripCompleteFile); + StripAction.CommandArguments = String.Format("-c '\"{0}strip\" {1} \"{2}\" && touch \"{3}\"'", Settings.Value.ToolchainDir, StripArguments, Executable.Location, StripCompleteFile); StripAction.PrerequisiteItems.Add(Executable); StripAction.PrerequisiteItems.AddRange(OutputFiles); StripAction.ProducedItems.Add(StripCompleteFile); @@ -1509,6 +1509,14 @@ namespace UnrealBuildTool } } } + + public void OutputReceivedDataEventLogger(Object Sender, DataReceivedEventArgs Line) + { + if ((Line != null) && (Line.Data != null)) + { + Log.TraceInformation(Line.Data); + } + } } private static void GenerateCrashlyticsData(string ExecutableDirectory, string ExecutableName, string ProjectDir, string ProjectName) @@ -1549,7 +1557,7 @@ namespace UnrealBuildTool PlatformProjectGenerators.RegisterPlatformProjectGenerator(UnrealTargetPlatform.IOS, new IOSProjectGenerator(CmdLine)); PlatformProjectGenerators.RegisterPlatformProjectGenerator(UnrealTargetPlatform.TVOS, new TVOSProjectGenerator(CmdLine)); - XcodeProjectFileGenerator Generator = new XcodeProjectFileGenerator(ProjectFile); + XcodeProjectFileGenerator Generator = new XcodeProjectFileGenerator(ProjectFile, CmdLine); return Generator.GenerateProjectFiles(PlatformProjectGenerators, Arguments); } finally @@ -1563,6 +1571,152 @@ namespace UnrealBuildTool return FileReference.Combine(Executable.Directory, "Payload", TargetName + ".app", TargetName); } + + private static void WriteEntitlements(IOSPostBuildSyncTarget Target) + { + // get some info from the mobileprovisioning file + // the iCloud identifier and the bundle id may differ + string AppName = Target.TargetName; + FileReference MobileProvisionFile; + IOSProjectSettings ProjectSettings = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(Target.Platform)).ReadProjectSettings(Target.ProjectFile); + + if (Target.ImportProvision == null) + { + IOSProvisioningData ProvisioningData = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(Target.Platform)).ReadProvisioningData(ProjectSettings, Target.bForDistribution); + MobileProvisionFile = ProvisioningData.MobileProvisionFile; + } + else + { + MobileProvisionFile = new FileReference(Target.ImportProvision); + } + + string iCloudContainerIdentifiersXML = ""; + string iCloudContainerIdentifier = ""; + string UbiquityContainerIdentifiersXML = ""; + if (MobileProvisionFile!= null && File.Exists(MobileProvisionFile.FullName)) + { + MobileProvisionContents MobileProvisionContent = MobileProvisionContents.Read(MobileProvisionFile); + + iCloudContainerIdentifier = MobileProvisionContent.GetNodeValueByName("com.apple.developer.icloud-container-identifiers"); + iCloudContainerIdentifiersXML = MobileProvisionContent.GetNodeXMLValueByName("com.apple.developer.icloud-container-identifiers"); + UbiquityContainerIdentifiersXML = MobileProvisionContent.GetNodeXMLValueByName("com.apple.developer.ubiquity-container-identifiers"); + } + // create the entitlements file + string IntermediateDir = (((Target.ProjectFile != null) ? Target.ProjectFile.Directory.ToString() : + UnrealBuildTool.EngineDirectory.ToString())) + "/Intermediate/" + (Target.Platform == UnrealTargetPlatform.IOS ? "IOS" : "TVOS"); + WriteEntitlementsFile(Path.Combine(IntermediateDir, AppName + ".entitlements"), Target.ProjectFile, Target.bForDistribution, iCloudContainerIdentifiersXML, UbiquityContainerIdentifiersXML); + + // create a pList key named ICloudContainerIdentifier + // to be used at run-time when intializing the CloudKit services + if (iCloudContainerIdentifier != "") + { + string PListFile = IntermediateDir + "/" + AppName + "-Info.plist"; + if (File.Exists(PListFile)) + { + string OldPListData = File.ReadAllText(PListFile); + XDocument XDoc; + try + { + XDoc = XDocument.Parse(OldPListData); + if (XDoc.DocumentType != null) + { + XDoc.DocumentType.InternalSubset = null; + } + + XElement dictElement = XDoc.Root.Element("dict"); + if (dictElement != null) + { + dictElement.Add(new XElement("key", "ICloudContainerIdentifier")); + dictElement.Add(new XElement("string", iCloudContainerIdentifier)); + + XDoc.Save(PListFile); + } + } + catch (Exception e) + { + throw new BuildException("plist is invalid {0}\n{1}", e, OldPListData); + } + + } + } + } + + private static void WriteEntitlementsFile(string OutputFilename, FileReference ProjectFile, bool bForDistribution, string iCloudContainerIdentifiersXML, string UbiquityContainerIdentifiersXML) + { + // get the settings from the ini file + ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(ProjectFile), UnrealTargetPlatform.IOS); + bool bCloudKitSupported = false; + Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableCloudKitSupport", out bCloudKitSupported); + Directory.CreateDirectory(Path.GetDirectoryName(OutputFilename)); + // we need to have something so Xcode will compile, so we just set the get-task-allow, since we know the value, + // which is based on distribution or not (true means debuggable) + StringBuilder Text = new StringBuilder(); + Text.AppendLine(""); + Text.AppendLine(""); + Text.AppendLine(""); + Text.AppendLine(""); + Text.AppendLine("\tget-task-allow"); + Text.AppendLine(string.Format("\t<{0}/>", bForDistribution ? "false" : "true")); + if (bCloudKitSupported) + { + Text.AppendLine("\tcom.apple.developer.icloud-container-identifiers"); + if (iCloudContainerIdentifiersXML == "") + { + Text.AppendLine("\t"); + Text.AppendLine("\t\tiCloud.$(CFBundleIdentifier)"); + Text.AppendLine("\t"); + } + else + { + Text.AppendLine(iCloudContainerIdentifiersXML); + } + Text.AppendLine("\tcom.apple.developer.icloud-services"); + Text.AppendLine("\t"); + Text.AppendLine("\t\tCloudKit"); + Text.AppendLine("\t\tCloudDocuments"); + Text.AppendLine("\t"); + Text.AppendLine("\tcom.apple.developer.ubiquity-container-identifiers"); + if (UbiquityContainerIdentifiersXML == "") + { + Text.AppendLine("\t"); + Text.AppendLine("\t\tiCloud.$(CFBundleIdentifier)"); + Text.AppendLine("\t"); + } + else + { + Text.AppendLine(UbiquityContainerIdentifiersXML); + } + Text.AppendLine("\tcom.apple.developer.ubiquity-kvstore-identifier"); + Text.AppendLine("\t$(TeamIdentifierPrefix)$(CFBundleIdentifier)"); + } + + bool bRemoteNotificationsSupported = false; + Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableRemoteNotificationsSupport", out bRemoteNotificationsSupported); + if (bRemoteNotificationsSupported) + { + Text.AppendLine("\taps-environment"); + Text.AppendLine(string.Format("\t{0}", bForDistribution ? "production" : "development")); + } + Text.AppendLine(""); + Text.AppendLine(""); + + if (File.Exists(OutputFilename)) + { + // read existing file + string ExisitingFileContents = File.ReadAllText(OutputFilename); + bool bFileChanged = !ExisitingFileContents.Equals(Text.ToString(), StringComparison.Ordinal); + // overwrite file if there are content changes + if (bFileChanged) + { + File.WriteAllText(OutputFilename, Text.ToString()); + } + } + else + { + File.WriteAllText(OutputFilename, Text.ToString()); + } + } + public static void PostBuildSync(IOSPostBuildSyncTarget Target) { IOSProjectSettings ProjectSettings = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(Target.Platform)).ReadProjectSettings(Target.ProjectFile); @@ -1598,6 +1752,7 @@ namespace UnrealBuildTool // ensure the plist, entitlements, and provision files are properly copied UEDeployIOS DeployHandler = (Target.Platform == UnrealTargetPlatform.IOS ? new UEDeployIOS() : new UEDeployTVOS()); + DeployHandler.ForDistribution = Target.bForDistribution; DeployHandler.PrepTargetForDeployment(Target.ProjectFile, Target.TargetName, Target.Platform, Target.Configuration, Target.UPLScripts, Target.SdkVersion, Target.bCreateStubIPA); // copy the executable @@ -1615,12 +1770,12 @@ namespace UnrealBuildTool DirectoryReference XcodeWorkspaceDir; if (AppName == "UE4Game" || AppName == "UE4Client" || Target.ProjectFile == null || Target.ProjectFile.IsUnderDirectory(UnrealBuildTool.EngineDirectory)) { - GenerateProjectFiles(Target.ProjectFile, new string[] { "-platforms=" + (Target.Platform == UnrealTargetPlatform.IOS ? "IOS" : "TVOS"), "-NoIntellIsense", (Target.Platform == UnrealTargetPlatform.IOS ? "-iosdeployonly" : "-tvosdeployonly"), "-ignorejunk" }); + GenerateProjectFiles(Target.ProjectFile, new string[] { "-platforms=" + (Target.Platform == UnrealTargetPlatform.IOS ? "IOS" : "TVOS"), "-NoIntellIsense", (Target.Platform == UnrealTargetPlatform.IOS ? "-iosdeployonly" : "-tvosdeployonly"), "-ignorejunk", (Target.bForDistribution ? "-distribution" : "-development") }); XcodeWorkspaceDir = DirectoryReference.Combine(UnrealBuildTool.RootDirectory, String.Format("UE4_{0}.xcworkspace", (Target.Platform == UnrealTargetPlatform.IOS ? "IOS" : "TVOS"))); } else { - GenerateProjectFiles(Target.ProjectFile, new string[] { "-platforms=" + (Target.Platform == UnrealTargetPlatform.IOS ? "IOS" : "TVOS"), "-NoIntellIsense", (Target.Platform == UnrealTargetPlatform.IOS ? "-iosdeployonly" : "-tvosdeployonly"), "-ignorejunk", String.Format("-project={0}", Target.ProjectFile), "-game" }); + GenerateProjectFiles(Target.ProjectFile, new string[] { "-platforms=" + (Target.Platform == UnrealTargetPlatform.IOS ? "IOS" : "TVOS"), "-NoIntellIsense", (Target.Platform == UnrealTargetPlatform.IOS ? "-iosdeployonly" : "-tvosdeployonly"), "-ignorejunk", (Target.bForDistribution ? "-distribution" : "-development"), String.Format("-project={0}", Target.ProjectFile), "-game" }); XcodeWorkspaceDir = DirectoryReference.Combine(Target.ProjectDirectory, String.Format("{0}_{1}.xcworkspace", Target.ProjectFile.GetFileNameWithoutExtension(), (Target.Platform == UnrealTargetPlatform.IOS ? "IOS" : "TVOS"))); } @@ -1632,8 +1787,12 @@ namespace UnrealBuildTool // ensure the plist, entitlements, and provision files are properly copied DeployHandler = (Target.Platform == UnrealTargetPlatform.IOS ? new UEDeployIOS() : new UEDeployTVOS()); + DeployHandler.ForDistribution = Target.bForDistribution; DeployHandler.PrepTargetForDeployment(Target.ProjectFile, Target.TargetName, Target.Platform, Target.Configuration, Target.UPLScripts, Target.SdkVersion, true); + // Path to the temporary keychain. When -ImportCertificate is specified, we will temporarily add this to the list of keychains to search, and remove it later. + FileReference TempKeychain = FileReference.Combine(Target.ProjectIntermediateDirectory, "TempKeychain.keychain"); + FileReference SignProjectScript = FileReference.Combine(Target.ProjectIntermediateDirectory, "SignProject.sh"); using(StreamWriter Writer = new StreamWriter(SignProjectScript.FullName)) { @@ -1648,15 +1807,12 @@ namespace UnrealBuildTool Writer.WriteLine("cp -f {0} ~/Library/MobileDevice/Provisioning\\ Profiles/", Utils.EscapeShellArgument(Target.ImportProvision)); } - // Path to the temporary keychain. When -ImportCertificate is specified, we will temporarily add this to the list of keychains to search, and remove it later. - FileReference TempKeychain = null; - // Get the signing certificate to use string SigningCertificate; if(Target.ImportCertificate == null) { // Take it from the standard settings - IOSProvisioningData ProvisioningData = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(Target.Platform)).ReadProvisioningData(Target.ProjectFile); + IOSProvisioningData ProvisioningData = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(Target.Platform)).ReadProvisioningData(Target.ProjectFile, Target.bForDistribution); SigningCertificate = ProvisioningData.SigningCertificate; // Set the identity on the command line @@ -1679,9 +1835,6 @@ namespace UnrealBuildTool } SigningCertificate = Certificate.GetNameInfo(X509NameType.SimpleName, false); - // Set the path to the temporary keychain - TempKeychain = FileReference.Combine(Target.ProjectIntermediateDirectory, "TempKeychain.keychain");//(DirectoryReference.GetSpecialFolder(Environment.SpecialFolder.UserProfile), "Library", "Keychains/UE4TempKeychain.keychain"; - // Install a certificate given on the command line to a temporary keychain Writer.WriteLine("security delete-keychain \"{0}\" || true", TempKeychain); Writer.WriteLine("security create-keychain -p \"A\" \"{0}\"", TempKeychain); @@ -1703,7 +1856,7 @@ namespace UnrealBuildTool string TeamUUID; if(Target.ImportProvision == null) { - IOSProvisioningData ProvisioningData = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(Target.Platform)).ReadProvisioningData(ProjectSettings); + IOSProvisioningData ProvisioningData = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(Target.Platform)).ReadProvisioningData(ProjectSettings, Target.bForDistribution); MobileProvisionFile = ProvisioningData.MobileProvisionFile; MobileProvisionUUID = ProvisioningData.MobileProvisionUUID; TeamUUID = ProvisioningData.TeamUUID; @@ -1749,15 +1902,13 @@ namespace UnrealBuildTool } Writer.WriteLine("/usr/bin/xcrun {0}", CmdLine); - // Remove the temporary keychain from the search list - if(TempKeychain != null) - { - Writer.WriteLine("security delete-keychain \"{0}\" || true", TempKeychain); - } } Log.TraceInformation("Executing {0}", SignProjectScript); + // write the entitlements file (building remotely) + WriteEntitlements(Target); + Process SignProcess = new Process(); SignProcess.StartInfo.WorkingDirectory = RemoteShadowDirectoryMac; SignProcess.StartInfo.FileName = "/bin/sh"; @@ -1767,11 +1918,37 @@ namespace UnrealBuildTool SignProcess.OutputDataReceived += new DataReceivedEventHandler(Output.OutputReceivedDataEventHandler); SignProcess.ErrorDataReceived += new DataReceivedEventHandler(Output.OutputReceivedDataEventHandler); - + Output.OutputReceivedDataEventHandlerEncounteredError = false; Output.OutputReceivedDataEventHandlerEncounteredErrorMessage = ""; Utils.RunLocalProcess(SignProcess); + // cleanup + if (Target.ImportCertificate != null) + { + FileReference CleanProjectScript = FileReference.Combine(Target.ProjectIntermediateDirectory, "CleanProject.sh"); + using (StreamWriter CleanWriter = new StreamWriter(CleanProjectScript.FullName)) + { + // Remove the temporary keychain from the search list + CleanWriter.WriteLine("security delete-keychain \"{0}\" || true", TempKeychain); + CleanWriter.WriteLine("security list-keychain -s login.keychain"); + } + + Log.TraceInformation("Executing {0}", CleanProjectScript); + + Process CleanProcess = new Process(); + CleanProcess.StartInfo.WorkingDirectory = RemoteShadowDirectoryMac; + CleanProcess.StartInfo.FileName = "/bin/sh"; + CleanProcess.StartInfo.Arguments = CleanProjectScript.FullName; + + ProcessOutput CleanOutput = new ProcessOutput(); + + SignProcess.OutputDataReceived += new DataReceivedEventHandler(CleanOutput.OutputReceivedDataEventLogger); + SignProcess.ErrorDataReceived += new DataReceivedEventHandler(CleanOutput.OutputReceivedDataEventLogger); + + Utils.RunLocalProcess(CleanProcess); + } + // delete the temp project DirectoryReference.Delete(XcodeWorkspaceDir, true); @@ -1783,6 +1960,12 @@ namespace UnrealBuildTool // Package the stub PackageStub(RemoteShadowDirectoryMac, AppName, Target.OutputPath.GetFileNameWithoutExtension()); } + else + { + // write the entitlements file (building on Mac) + WriteEntitlements(Target); + } + { // Copy bundled assets from additional frameworks to the intermediate assets directory (so they can get picked up during staging) diff --git a/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/MobileProvisionContents.cs b/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/MobileProvisionContents.cs index f8e42bd73b19..b92f786a2de2 100644 --- a/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/MobileProvisionContents.cs +++ b/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/MobileProvisionContents.cs @@ -144,5 +144,53 @@ namespace UnrealBuildTool throw new BuildException("No PKCS7-Data section found in {0}", Location); } } + + // return the outerXML of the node's value + public string GetNodeXMLValueByName(string InValue) + { + XmlNodeList elemList = this.Document.GetElementsByTagName("key"); + for (int i = 0; i < elemList.Count; i++) + { + if (elemList[i].InnerXml.Equals(InValue)) + { + XmlNode valueNode = elemList[i].NextSibling; + + if (valueNode != null) + { + return valueNode.OuterXml; + } + } + } + return ""; + } + + // return the innerXML of the node's value + public string GetNodeValueByName(string InValue) + { + XmlNodeList elemList = this.Document.GetElementsByTagName("key"); + for (int i = 0; i < elemList.Count; i++) + { + if (elemList[i].InnerXml.Equals(InValue)) + { + XmlNode valueNode = elemList[i].NextSibling; + if (valueNode != null) + { + if (valueNode.Name.Equals("array")) + { + XmlNode firstChildNode = valueNode.FirstChild; + if (firstChildNode != null) + { + return firstChildNode.InnerXml; + } + } + else + { + return valueNode.InnerXml; + } + } + } + } + return ""; + } } } diff --git a/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/UEBuildIOS.cs b/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/UEBuildIOS.cs index c00e6b6bc9d4..5a6fd7f2d76d 100644 --- a/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/UEBuildIOS.cs +++ b/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/UEBuildIOS.cs @@ -35,6 +35,12 @@ namespace UnrealBuildTool [CommandLine("-skipcrashlytics")] public bool bSkipCrashlytics = false; + /// + /// Mark the build for distribution + /// + [CommandLine("-distribution")] + public bool bForDistribution = false; + /// /// Manual override for the provision to use. Should be a full path. /// @@ -100,6 +106,11 @@ namespace UnrealBuildTool get { return Inner.bSkipCrashlytics; } } + public bool bForDistribution + { + get { return Inner.bForDistribution; } + } + public string ImportProvision { get { return Inner.ImportProvision; } @@ -289,6 +300,12 @@ namespace UnrealBuildTool [ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableAdvertisingIdentifier")] public readonly bool bEnableAdvertisingIdentifier = false; + /// + /// true when building for distribution + /// + [ConfigFile(ConfigHierarchyType.Game, "/Script/UnrealEd.ProjectPackagingSettings", "ForDistribution")] + public readonly bool bForDistribution = false; + /// /// Returns a list of all the non-shipping architectures which are supported /// diff --git a/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/UEDeployIOS.cs b/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/UEDeployIOS.cs index b99509d1b021..107b971f474d 100644 --- a/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/UEDeployIOS.cs +++ b/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/UEDeployIOS.cs @@ -22,6 +22,13 @@ namespace UnrealBuildTool protected UnrealPluginLanguage UPL = null; protected delegate bool FilenameFilter(string InFilename); + public bool ForDistribution + { + get { return bForDistribution; } + set { bForDistribution = value; } + } + bool bForDistribution = false; + protected class VersionUtilities { public static string BuildDirectory @@ -29,22 +36,24 @@ namespace UnrealBuildTool get; set; } - public static string GameName + public static string GameName { get; - set; + set; } - static string RunningVersionFilename + + + static string RunningVersionFilename { get { return Path.Combine(BuildDirectory, GameName + ".PackageVersionCounter"); } } - /// - /// Reads the GameName.PackageVersionCounter from disk and bumps the minor version number in it - /// - /// - public static string ReadRunningVersion() + /// + /// Reads the GameName.PackageVersionCounter from disk and bumps the minor version number in it + /// + /// + public static string ReadRunningVersion() { string CurrentVersion = "0.0"; if (File.Exists(RunningVersionFilename)) @@ -946,7 +955,6 @@ namespace UnrealBuildTool Directory.CreateDirectory(BuildDirectory); // create the entitlements file - WriteEntitlementsFile(Path.Combine(IntermediateDirectory, GameName + ".entitlements"), ProjectFile, bForDistribution); // delete some old files if they exist if (Directory.Exists(AppDirectory + "/_CodeSignature")) @@ -1154,7 +1162,7 @@ namespace UnrealBuildTool if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac && Environment.GetEnvironmentVariable("UBT_NO_POST_DEPLOY") != "true") { - return PrepForUATPackageOrDeploy(Configuration, ProjectFile, GameName, ProjectDirectory, BuildPath + "/" + DecoratedGameName, "../../Engine", false, "", false, bCreateStubIPA, UPLScripts, SdkVersion); + return PrepForUATPackageOrDeploy(Configuration, ProjectFile, GameName, ProjectDirectory, BuildPath + "/" + DecoratedGameName, "../../Engine", bForDistribution, "", false, bCreateStubIPA, UPLScripts, SdkVersion); } else { @@ -1190,76 +1198,6 @@ namespace UnrealBuildTool return PluginExtras; } - private void WriteEntitlementsFile(string OutputFilename, FileReference ProjectFile, bool bForDistribution) - { - // get the settings from the ini file - // plist replacements - // @todo tvos: Separate TVOS version? - ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(ProjectFile), UnrealTargetPlatform.IOS); - bool bCloudKitSupported = false; - Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableCloudKitSupport", out bCloudKitSupported); - - Directory.CreateDirectory(Path.GetDirectoryName(OutputFilename)); - // we need to have something so Xcode will compile, so we just set the get-task-allow, since we know the value, - // which is based on distribution or not (true means debuggable) - StringBuilder Text = new StringBuilder(); - Text.AppendLine(""); - Text.AppendLine(""); - Text.AppendLine(""); - Text.AppendLine(""); - Text.AppendLine("\tget-task-allow"); - Text.AppendLine(string.Format("\t<{0}/>", bForDistribution ? "false" : "true")); - - if (bCloudKitSupported) - { - Text.AppendLine("\tcom.apple.developer.icloud-container-identifiers"); - Text.AppendLine("\t"); - Text.AppendLine("\t\tiCloud.$(CFBundleIdentifier)"); - Text.AppendLine("\t"); - Text.AppendLine("\tcom.apple.developer.icloud-services"); - Text.AppendLine("\t"); - Text.AppendLine("\t\tCloudKit"); - Text.AppendLine("\t\tCloudDocuments"); - Text.AppendLine("\t"); - Text.AppendLine("\tcom.apple.developer.ubiquity-container-identifiers"); - Text.AppendLine("\t"); - Text.AppendLine("\t\tiCloud.$(CFBundleIdentifier)"); - Text.AppendLine("\t"); - Text.AppendLine("\tcom.apple.developer.ubiquity-kvstore-identifier"); - Text.AppendLine("\t$(TeamIdentifierPrefix)$(CFBundleIdentifier)"); - } - - bool bRemoteNotificationsSupported = false; - Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableRemoteNotificationsSupport", out bRemoteNotificationsSupported); - - if (bRemoteNotificationsSupported) - { - Text.AppendLine("\taps-environment"); - Text.AppendLine(string.Format("\t{0}", bForDistribution ? "production" : "development")); - } - - Text.AppendLine(""); - Text.AppendLine(""); - - if (File.Exists(OutputFilename)) - { - // read existing file - string ExisitingFileContents = File.ReadAllText(OutputFilename); - - bool bFileChanged = !ExisitingFileContents.Equals(Text.ToString(), StringComparison.Ordinal); - - // overwrite file if there are content changes - if (bFileChanged) - { - File.WriteAllText(OutputFilename, Text.ToString()); - } - } - else - { - File.WriteAllText(OutputFilename, Text.ToString()); - } - } - public static void SafeFileCopy(FileInfo SourceFile, string DestinationPath, bool bOverwrite) { FileInfo DI = new FileInfo(DestinationPath); diff --git a/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/ProjectFileGenerator.cs b/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/ProjectFileGenerator.cs index 06b97309c7fa..75016a682810 100644 --- a/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/ProjectFileGenerator.cs +++ b/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/ProjectFileGenerator.cs @@ -500,16 +500,7 @@ namespace UnrealBuildTool /// True if a preferred IDE was set, false otherwise public static bool GetPreferredSourceCodeAccessor(FileReference ProjectFile, out ProjectFileFormat Format) { - DirectoryReference EngineSavedDir = DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "Saved"); - if(UnrealBuildTool.IsEngineInstalled()) - { - BuildVersion Version; - if(BuildVersion.TryRead(BuildVersion.GetDefaultFileName(), out Version)) - { - EngineSavedDir = DirectoryReference.Combine(Utils.GetUserSettingDirectory(), "UnrealEngine", String.Format("{0}.{1}", Version.MajorVersion, Version.MinorVersion), "Saved"); - } - } - ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.EditorSettings, DirectoryReference.FromFile(ProjectFile), BuildHostPlatform.Current.Platform, EngineSavedDir); + ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.EditorSettings, DirectoryReference.FromFile(ProjectFile), BuildHostPlatform.Current.Platform); string PreferredAccessor; if (Ini.GetString("/Script/SourceCodeAccess.SourceCodeAccessSettings", "PreferredAccessor", out PreferredAccessor)) diff --git a/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/VisualStudio/VCProject.cs b/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/VisualStudio/VCProject.cs index 3a17c72d4e93..c509a3a7cb70 100644 --- a/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/VisualStudio/VCProject.cs +++ b/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/VisualStudio/VCProject.cs @@ -378,7 +378,7 @@ namespace UnrealBuildTool return new MSBuildProjectContext(StubProjectConfigurationName, StubProjectPlatformName); } - // Have to match every solution configuration combination to a project configuration (or use the invalid one) + // Have to match every solution configuration combination to a project configuration (or use the invalid one) string ProjectConfigurationName = "Invalid"; // Get the default platform. If there were not valid platforms for this project, just use one that will always be available in VS. @@ -944,7 +944,7 @@ namespace UnrealBuildTool if (!String.IsNullOrWhiteSpace(AliasedFile.ProjectPath)) { VCFiltersFileContent.AppendLine(" <{0} Include=\"{1}\">", VCFileType, EscapeFileName(AliasedFile.FileSystemPath)); - VCFiltersFileContent.AppendLine(" {0}", Utils.CleanDirectorySeparators(AliasedFile.ProjectPath)); + VCFiltersFileContent.AppendLine(" {0}", Utils.CleanDirectorySeparators(EscapeFileName(AliasedFile.ProjectPath))); VCFiltersFileContent.AppendLine(" ", VCFileType); FiltersFileIsNeeded = true; @@ -1148,7 +1148,7 @@ namespace UnrealBuildTool // matches identically with the pre-existing file string FilterGUID = Guid.NewGuid().ToString("B").ToUpperInvariant(); - VCFiltersFileContent.AppendLine(" ", LeadingDirectory); + VCFiltersFileContent.AppendLine(" ", EscapeFileName(LeadingDirectory)); VCFiltersFileContent.AppendLine(" {0}", FilterGUID); VCFiltersFileContent.AppendLine(" "); @@ -1517,7 +1517,7 @@ namespace UnrealBuildTool throw new BuildException("Unexpected root element '{0}' in project file", Document.DocumentElement.Name); } - // Parse all the configurations and platforms + // Parse all the configurations and platforms // Parse the basic structure of the document, updating properties and recursing into other referenced projects as we go foreach (XmlElement Element in Document.DocumentElement.ChildNodes.OfType()) { @@ -1624,7 +1624,7 @@ namespace UnrealBuildTool { bBuildByDefault = true; } - + // Create the context return new MSBuildProjectContext(ProjectConfigurationName, ProjectPlatformName){ bBuildByDefault = bBuildByDefault }; } diff --git a/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/Xcode/XcodeProject.cs b/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/Xcode/XcodeProject.cs index 111868b8accd..2c5269fefb01 100644 --- a/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/Xcode/XcodeProject.cs +++ b/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/Xcode/XcodeProject.cs @@ -133,10 +133,12 @@ namespace UnrealBuildTool /// /// The path to the project file on disk /// - public XcodeProjectFile(FileReference InitFilePath, FileReference InOnlyGameProject) + /// True for distribution builds + public XcodeProjectFile(FileReference InitFilePath, FileReference InOnlyGameProject, bool IsForDistribution) : base(InitFilePath) { OnlyGameProject = InOnlyGameProject; + bForDistribution = IsForDistribution; } public override string ToString() @@ -144,6 +146,11 @@ namespace UnrealBuildTool return ProjectFilePath.GetFileNameWithoutExtension(); } + /// + /// Used to mark the project for distribution (some platforms require this) + /// + bool bForDistribution = false; + /// /// Gets Xcode file category based on its extension /// @@ -828,7 +835,7 @@ namespace UnrealBuildTool { TVOSPlatform TVOSPlatform = ((TVOSPlatform)UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.TVOS)); TVOSProjectSettings ProjectSettings = TVOSPlatform.ReadProjectSettings(ProjectFile); - TVOSProvisioningData ProvisioningData = TVOSPlatform.ReadProvisioningData(ProjectSettings); + TVOSProvisioningData ProvisioningData = TVOSPlatform.ReadProvisioningData(ProjectSettings, bForDistribution); bAutomaticSigning = ProjectSettings.bAutomaticSigning; } @@ -980,7 +987,7 @@ namespace UnrealBuildTool { IOSPlatform IOSPlatform = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.IOS)); IOSProjectSettings ProjectSettings = IOSPlatform.ReadProjectSettings(ProjectFile); - IOSProvisioningData ProvisioningData = IOSPlatform.ReadProvisioningData(ProjectSettings); + IOSProvisioningData ProvisioningData = IOSPlatform.ReadProvisioningData(ProjectSettings, bForDistribution); IOSRunTimeVersion = ProjectSettings.RuntimeVersion; IOSRunTimeDevices = ProjectSettings.RuntimeDevices; ValidArchs += " arm64 armv7 armv7s"; @@ -999,7 +1006,7 @@ namespace UnrealBuildTool { TVOSPlatform TVOSPlatform = ((TVOSPlatform)UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.TVOS)); TVOSProjectSettings ProjectSettings = TVOSPlatform.ReadProjectSettings(ProjectFile); - TVOSProvisioningData ProvisioningData = TVOSPlatform.ReadProvisioningData(ProjectSettings); + TVOSProvisioningData ProvisioningData = TVOSPlatform.ReadProvisioningData(ProjectSettings, bForDistribution); TVOSRunTimeVersion = ProjectSettings.RuntimeVersion; TVOSRunTimeDevices = ProjectSettings.RuntimeDevices; if (!ValidArchs.Contains("arm64")) diff --git a/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/Xcode/XcodeProjectFileGenerator.cs b/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/Xcode/XcodeProjectFileGenerator.cs index 7f1dc744b19f..20621d327084 100644 --- a/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/Xcode/XcodeProjectFileGenerator.cs +++ b/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/Xcode/XcodeProjectFileGenerator.cs @@ -27,9 +27,18 @@ namespace UnrealBuildTool // always seed the random number the same, so multiple runs of the generator will generate the same project static Random Rand = new Random(0); - public XcodeProjectFileGenerator(FileReference InOnlyGameProject) + /// + /// Mark for distribution builds + /// + bool bForDistribution = false; + + public XcodeProjectFileGenerator(FileReference InOnlyGameProject, CommandLineArguments CommandLine) : base(InOnlyGameProject) { + if (CommandLine.HasOption("-distribution")) + { + bForDistribution = true; + } } /// @@ -90,7 +99,7 @@ namespace UnrealBuildTool /// The newly allocated project file object protected override ProjectFile AllocateProjectFile(FileReference InitFilePath) { - return new XcodeProjectFile(InitFilePath, OnlyGameProject); + return new XcodeProjectFile(InitFilePath, OnlyGameProject, bForDistribution); } /// ProjectFileGenerator interface diff --git a/Engine/Source/Programs/UnrealBuildTool/System/ConfigCache.cs b/Engine/Source/Programs/UnrealBuildTool/System/ConfigCache.cs index 5b076dc11482..eada6d53188d 100644 --- a/Engine/Source/Programs/UnrealBuildTool/System/ConfigCache.cs +++ b/Engine/Source/Programs/UnrealBuildTool/System/ConfigCache.cs @@ -1,4 +1,4 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; @@ -156,9 +156,8 @@ namespace UnrealBuildTool /// The type of hierarchy to read /// The project directory to read the hierarchy for /// Which platform to read platform-specific config files for - /// Base directory for generated configs /// The requested config hierarchy - public static ConfigHierarchy ReadHierarchy(ConfigHierarchyType Type, DirectoryReference ProjectDir, UnrealTargetPlatform Platform, DirectoryReference GeneratedConfigDir = null) + public static ConfigHierarchy ReadHierarchy(ConfigHierarchyType Type, DirectoryReference ProjectDir, UnrealTargetPlatform Platform) { // Get the key to use for the cache. It cannot be null, so we use the engine directory if a project directory is not given. ConfigHierarchyKey Key = new ConfigHierarchyKey(Type, ProjectDir, Platform); @@ -169,6 +168,7 @@ namespace UnrealBuildTool { if (!HierarchyKeyToHierarchy.TryGetValue(Key, out Hierarchy)) { + // Find all the input files List Files = new List(); foreach (FileReference IniFileName in ConfigHierarchy.EnumerateConfigFileLocations(Type, ProjectDir, Platform)) { @@ -179,27 +179,6 @@ namespace UnrealBuildTool } } - // If we haven't been given a generated project dir, but we do have a project then the generated configs - // should go into ProjectDir/Saved - if (GeneratedConfigDir == null && ProjectDir != null) - { - GeneratedConfigDir = DirectoryReference.Combine(ProjectDir, "Saved"); - } - - if (GeneratedConfigDir != null) - { - // We know where the generated version of this config file lives, so we can read it back in - // and include any user settings from there in our hierarchy - string BaseIniName = Enum.GetName(typeof(ConfigHierarchyType), Type); - string PlatformName = ConfigHierarchy.GetIniPlatformName(Platform); - FileReference DestinationIniFilename = FileReference.Combine(GeneratedConfigDir, "Config", PlatformName, BaseIniName + ".ini"); - ConfigFile File; - if (TryReadFile(DestinationIniFilename, out File)) - { - Files.Add(File); - } - } - // Handle command line overrides string[] CmdLine = Environment.GetCommandLineArgs(); string IniConfigArgPrefix = "-ini:" + Enum.GetName(typeof(ConfigHierarchyType), Type) + ":"; @@ -212,6 +191,7 @@ namespace UnrealBuildTool } } + // Create the hierarchy Hierarchy = new ConfigHierarchy(Files); HierarchyKeyToHierarchy.Add(Key, Hierarchy); } diff --git a/Engine/Source/Programs/UnrealBuildTool/System/ConfigHierarchy.cs b/Engine/Source/Programs/UnrealBuildTool/System/ConfigHierarchy.cs index 5176064cfd60..8f60c49e4930 100644 --- a/Engine/Source/Programs/UnrealBuildTool/System/ConfigHierarchy.cs +++ b/Engine/Source/Programs/UnrealBuildTool/System/ConfigHierarchy.cs @@ -850,6 +850,48 @@ namespace UnrealBuildTool { yield return FileReference.Combine(ProjectDir, "Config", "User" + BaseIniName + ".ini"); } + + // Get the generated config file too. EditorSettings overrides this from + if(Type == ConfigHierarchyType.EditorSettings) + { + yield return FileReference.Combine(GetGameAgnosticSavedDir(), "Config", PlatformName, BaseIniName + ".ini"); + } + else + { + yield return FileReference.Combine(GetGeneratedConfigDir(ProjectDir), PlatformName, BaseIniName + ".ini"); + } + } + + /// + /// Determines the path to the generated config directory (same as FPaths::GeneratedConfigDir()) + /// + /// + public static DirectoryReference GetGeneratedConfigDir(DirectoryReference ProjectDir) + { + if(ProjectDir == null) + { + return DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "Saved", "Config"); + } + else + { + return DirectoryReference.Combine(ProjectDir, "Saved", "Config"); + } + } + + /// + /// Determes the path to the game-agnostic saved directory (same as FPaths::GameAgnosticSavedDir()) + /// + /// + public static DirectoryReference GetGameAgnosticSavedDir() + { + if(UnrealBuildTool.IsEngineInstalled()) + { + return DirectoryReference.Combine(Utils.GetUserSettingDirectory(), "UnrealEngine", String.Format("{0}.{1}", ReadOnlyBuildVersion.Current.MajorVersion, ReadOnlyBuildVersion.Current.MinorVersion), "Saved"); + } + else + { + return DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "Saved"); + } } /// diff --git a/Engine/Source/Programs/UnrealBuildTool/System/EncryptionAndSigning.cs b/Engine/Source/Programs/UnrealBuildTool/System/EncryptionAndSigning.cs index bcebc334a66a..507a1952eb5e 100644 --- a/Engine/Source/Programs/UnrealBuildTool/System/EncryptionAndSigning.cs +++ b/Engine/Source/Programs/UnrealBuildTool/System/EncryptionAndSigning.cs @@ -61,6 +61,18 @@ namespace UnrealBuildTool { return PublicKey != null && PrivateKey != null && PublicKey.IsValid() && PrivateKey.IsValid(); } + + /// + /// Returns TRUE if this is a short key from the old 256-bit system + /// + public bool IsUnsecureLegacyKey() + { + int LongestKey = PublicKey.Exponent.Length; + LongestKey = Math.Max(LongestKey, PublicKey.Modulus.Length); + LongestKey = Math.Max(LongestKey, PrivateKey.Exponent.Length); + LongestKey = Math.Max(LongestKey, PrivateKey.Modulus.Length); + return LongestKey <= 64; + } } /// @@ -135,6 +147,16 @@ namespace UnrealBuildTool /// public bool bDataCryptoRequired = false; + /// + /// Config setting to enable pak signing + /// + public bool PakEncryptionRequired = true; + + /// + /// Config setting to enable pak encryption + /// + public bool PakSigningRequired = true; + /// /// A set of named encryption keys that can be used to encrypt different sets of data with a different key that is delivered dynamically (i.e. not embedded within the game executable) /// @@ -205,10 +227,12 @@ namespace UnrealBuildTool public static CryptoSettings ParseCryptoSettings(DirectoryReference InProjectDirectory, UnrealTargetPlatform InTargetPlatform) { CryptoSettings Settings = new CryptoSettings(); - + ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, InProjectDirectory, InTargetPlatform); Ini.GetBool("PlatformCrypto", "PlatformRequiresDataCrypto", out Settings.bDataCryptoRequired); - + Ini.GetBool("PlatformCrypto", "PakSigningRequired", out Settings.PakSigningRequired); + Ini.GetBool("PlatformCrypto", "PakEncryptionRequired", out Settings.PakEncryptionRequired); + { // Start by parsing the legacy encryption.ini settings Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Encryption, InProjectDirectory, InTargetPlatform); @@ -394,6 +418,27 @@ namespace UnrealBuildTool NewSettings.SecondaryEncryptionKeys = Settings.SecondaryEncryptionKeys; Settings = NewSettings; } + else + { + if (!Settings.PakSigningRequired) + { + Settings.bEnablePakSigning = false; + Settings.SigningKey = null; + } + + if (!Settings.PakEncryptionRequired) + { + Settings.bEnablePakFullAssetEncryption = false; + Settings.bEnablePakIndexEncryption = false; + Settings.bEnablePakIniEncryption = false; + Settings.EncryptionKey = null; + Settings.SigningKey = null; + } + } + + { + //Log.TraceWarningOnce("Project signing keys found in '{0}' are of the old insecure short format. Please regenerate them using the project crypto settings panel in the editor!", InProjectDirectory); + } // Validate the settings we have read if (Settings.bDataCryptoRequired && Settings.bEnablePakSigning && (Settings.SigningKey == null || !Settings.SigningKey.IsValid())) diff --git a/Engine/Source/Programs/UnrealBuildTool/System/HotReload.cs b/Engine/Source/Programs/UnrealBuildTool/System/HotReload.cs index 7bfa438d16f6..43e83bfe47f4 100644 --- a/Engine/Source/Programs/UnrealBuildTool/System/HotReload.cs +++ b/Engine/Source/Programs/UnrealBuildTool/System/HotReload.cs @@ -141,7 +141,7 @@ namespace UnrealBuildTool // Check if we're using LiveCode instead ConfigHierarchy EditorPerProjectHierarchy = ConfigCache.ReadHierarchy(ConfigHierarchyType.EditorPerProjectUserSettings, DirectoryReference.FromFile(TargetDesc.ProjectFile), TargetDesc.Platform); bool bEnableLiveCode; - if(EditorPerProjectHierarchy.GetBool("LiveCode", "Enabled", out bEnableLiveCode) && bEnableLiveCode) + if(EditorPerProjectHierarchy.GetBool("/Script/LiveCoding.LiveCodingSettings", "bEnabled", out bEnableLiveCode) && bEnableLiveCode) { return false; } @@ -527,9 +527,6 @@ namespace UnrealBuildTool } } - // Go ahead and replace all occurrences of our file name in the command-line (ignoring extensions) - Action.CommandArguments = ReplaceBaseFileName(Action.CommandArguments, OriginalFileNameWithoutExtension, NewFileNameWithoutExtension); - // Update this action's list of produced items too for (int ItemIndex = 0; ItemIndex < Action.ProducedItems.Count; ++ItemIndex) { @@ -581,6 +578,18 @@ namespace UnrealBuildTool if (OriginalFileNameAndNewFileNameList_NoExtensions.Count > 0) { + // Update all the paths in link actions + foreach (Action Action in Actions.Where((Action) => Action.ActionType == ActionType.Link)) + { + foreach (KeyValuePair FileNameTuple in OriginalFileNameAndNewFileNameList_NoExtensions) + { + string OriginalFileNameWithoutExtension = FileNameTuple.Key; + string NewFileNameWithoutExtension = FileNameTuple.Value; + + Action.CommandArguments = ReplaceBaseFileName(Action.CommandArguments, OriginalFileNameWithoutExtension, NewFileNameWithoutExtension); + } + } + foreach (string ResponseFilePath in ResponseFilePaths) { // Load the file up @@ -660,7 +669,8 @@ namespace UnrealBuildTool WriteMetadataTargetInfo TargetInfo = BinaryFormatterUtils.Load(TargetInfoFile); foreach (KeyValuePair FileNameToVersionManifest in TargetInfo.FileToManifest) { - foreach (KeyValuePair Manifest in FileNameToVersionManifest.Value.ModuleNameToFileName) + KeyValuePair[] ManifestEntries = FileNameToVersionManifest.Value.ModuleNameToFileName.ToArray(); + foreach (KeyValuePair Manifest in ManifestEntries) { FileReference OriginalFile = FileReference.Combine(FileNameToVersionManifest.Key.Directory, Manifest.Value); @@ -668,7 +678,6 @@ namespace UnrealBuildTool if(OriginalFileToHotReloadFile.TryGetValue(OriginalFile, out HotReloadFile)) { FileNameToVersionManifest.Value.ModuleNameToFileName[Manifest.Key] = HotReloadFile.GetFileName(); - break; } } } diff --git a/Engine/Source/Programs/UnrealBuildTool/ToolChain/AppleToolChain.cs b/Engine/Source/Programs/UnrealBuildTool/ToolChain/AppleToolChain.cs index 022c3e785071..870897f3adc2 100644 --- a/Engine/Source/Programs/UnrealBuildTool/ToolChain/AppleToolChain.cs +++ b/Engine/Source/Programs/UnrealBuildTool/ToolChain/AppleToolChain.cs @@ -165,13 +165,27 @@ namespace UnrealBuildTool { FileReference DsymutilLocation = new FileReference("/usr/bin/dsymutil"); - DirectoryReference AutoSdkDir; - if (UEBuildPlatformSDK.TryGetHostPlatformAutoSDKDir(out AutoSdkDir)) + string DsymutilVersionString = Utils.RunLocalProcessAndReturnStdOut(DsymutilLocation.FullName, "-version"); + + // dsymutil 10.0.0 has a bug that causes issues, it's fixed in autosdks but not everyone has those set up so for the timebeing we have + // a version in P4... + if (DsymutilVersionString.StartsWith("Apple LLVM version 10.0.0", StringComparison.Ordinal)) { - FileReference AutoSdkDsymutilLocation = FileReference.Combine(AutoSdkDir, "Mac", "LLVM", "bin", "dsymutil"); - if (FileReference.Exists(AutoSdkDsymutilLocation)) + FileReference PatchedDsymutilLocation = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Binaries/Mac/NotForLicensees/LLVM/bin/dsymutil"); + + if (File.Exists(PatchedDsymutilLocation.FullName)) { - DsymutilLocation = AutoSdkDsymutilLocation; + DsymutilLocation = PatchedDsymutilLocation; + } + + DirectoryReference AutoSdkDir; + if (UEBuildPlatformSDK.TryGetHostPlatformAutoSDKDir(out AutoSdkDir)) + { + FileReference AutoSdkDsymutilLocation = FileReference.Combine(AutoSdkDir, "Mac", "LLVM", "bin", "dsymutil"); + if (FileReference.Exists(AutoSdkDsymutilLocation)) + { + DsymutilLocation = AutoSdkDsymutilLocation; + } } } diff --git a/Engine/Source/Programs/UnrealBuildTool/ToolChain/RemoteMac.cs b/Engine/Source/Programs/UnrealBuildTool/ToolChain/RemoteMac.cs index 50d2ff54f898..47d5c5158960 100644 --- a/Engine/Source/Programs/UnrealBuildTool/ToolChain/RemoteMac.cs +++ b/Engine/Source/Programs/UnrealBuildTool/ToolChain/RemoteMac.cs @@ -400,7 +400,7 @@ namespace UnrealBuildTool RulesAssembly RulesAssembly = RulesCompiler.CreateTargetRulesAssembly(TargetDesc.ProjectFile, TargetDesc.Name, false, false, TargetDesc.ForeignPlugin); // Create the target rules - TargetRules Rules = RulesAssembly.CreateTargetRules(TargetDesc.Name, TargetDesc.Platform, TargetDesc.Configuration, TargetDesc.Architecture, TargetDesc.ProjectFile, null); + TargetRules Rules = RulesAssembly.CreateTargetRules(TargetDesc.Name, TargetDesc.Platform, TargetDesc.Configuration, TargetDesc.Architecture, TargetDesc.ProjectFile, TargetDesc.AdditionalArguments); // Check if we need to enable a nativized plugin, and compile the assembly for that if we do FileReference NativizedPluginFile = Rules.GetNativizedPlugin(); @@ -430,7 +430,7 @@ namespace UnrealBuildTool RemoteArguments.Add("-NoUBTMakefiles"); // Get the provisioning data for this project - IOSProvisioningData ProvisioningData = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(TargetDesc.Platform)).ReadProvisioningData(TargetDesc.ProjectFile); + IOSProvisioningData ProvisioningData = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(TargetDesc.Platform)).ReadProvisioningData(TargetDesc.ProjectFile, TargetDesc.AdditionalArguments.HasOption("-distribution")); if(ProvisioningData == null || ProvisioningData.MobileProvisionFile == null) { throw new BuildException("Unable to find mobile provision for {0}. See log for more information.", TargetDesc.Name); @@ -585,7 +585,7 @@ namespace UnrealBuildTool RemoteArguments.Add(String.Format("-Project={0}", GetRemotePath(TargetDesc.ProjectFile))); } - foreach(string LocalArgument in TargetDesc.AdditionalArguments) + foreach (string LocalArgument in TargetDesc.AdditionalArguments) { int EqualsIdx = LocalArgument.IndexOf('='); if(EqualsIdx == -1) diff --git a/Engine/Source/Programs/UnrealPak/UnrealPak.Build.cs b/Engine/Source/Programs/UnrealPak/UnrealPak.Build.cs index 0940b57b5067..e2d0c94482df 100644 --- a/Engine/Source/Programs/UnrealPak/UnrealPak.Build.cs +++ b/Engine/Source/Programs/UnrealPak/UnrealPak.Build.cs @@ -8,7 +8,7 @@ public class UnrealPak : ModuleRules { PublicIncludePaths.Add("Runtime/Launch/Public"); - PrivateDependencyModuleNames.AddRange(new string[] { "Core", "PakFile", "Json", "Projects", "PakFileUtilities" }); + PrivateDependencyModuleNames.AddRange(new string[] { "Core", "PakFile", "Json", "Projects", "PakFileUtilities", "RSA" }); PrivateIncludePaths.Add("Runtime/Launch/Private"); // For LaunchEngineLoop.cpp include diff --git a/Engine/Source/Runtime/Android/AndroidRuntimeSettings/Classes/AndroidRuntimeSettings.h b/Engine/Source/Runtime/Android/AndroidRuntimeSettings/Classes/AndroidRuntimeSettings.h index de3e41405d23..a9a8d92c2e82 100644 --- a/Engine/Source/Runtime/Android/AndroidRuntimeSettings/Classes/AndroidRuntimeSettings.h +++ b/Engine/Source/Runtime/Android/AndroidRuntimeSettings/Classes/AndroidRuntimeSettings.h @@ -393,7 +393,7 @@ public: TArray LeaderboardMap; // Enabling this requests snapshots support for saved games during Google Play login. - UPROPERTY(GlobalConfig, EditAnywhere, Category = GooglePlayServices, meta = (DisplayName = "Enable Snapshots on Google Play login")) + UPROPERTY(GlobalConfig, EditAnywhere, Category = GooglePlayServices, meta = (DisplayName = "Enable Snapshots on Google Play login [Experimental]")) bool bEnableSnapshots; // Enabling this includes the AdMob SDK and will be detected by Google Play Console on upload of APK. Disable if you do not need ads to remove this warning. diff --git a/Engine/Source/Runtime/AnimGraphRuntime/Private/AnimNodes/AnimNode_PoseDriver.cpp b/Engine/Source/Runtime/AnimGraphRuntime/Private/AnimNodes/AnimNode_PoseDriver.cpp index 306d09801e28..fbc3b5d9290f 100644 --- a/Engine/Source/Runtime/AnimGraphRuntime/Private/AnimNodes/AnimNode_PoseDriver.cpp +++ b/Engine/Source/Runtime/AnimGraphRuntime/Private/AnimNodes/AnimNode_PoseDriver.cpp @@ -309,6 +309,12 @@ void FAnimNode_PoseDriver::Evaluate_AnyThread(FPoseContext& Output) { FPoseContext CurrentPose(Output); + // clear the value before setting it. + for (int32 PoseIndex = 0; PoseIndex < PoseExtractContext.PoseCurves.Num(); ++PoseIndex) + { + PoseExtractContext.PoseCurves[PoseIndex].Value = 0.f; + } + // Then fill in weight for any driven poses for (const FRBFOutputWeight& Weight : OutputWeights) { diff --git a/Engine/Source/Runtime/AnimGraphRuntime/Private/BoneControllers/AnimNode_RigidBody.cpp b/Engine/Source/Runtime/AnimGraphRuntime/Private/BoneControllers/AnimNode_RigidBody.cpp index 6e5d6ab2f221..608418a41188 100644 --- a/Engine/Source/Runtime/AnimGraphRuntime/Private/BoneControllers/AnimNode_RigidBody.cpp +++ b/Engine/Source/Runtime/AnimGraphRuntime/Private/BoneControllers/AnimNode_RigidBody.cpp @@ -220,9 +220,14 @@ void FAnimNode_RigidBody::EvaluateSkeletalControl_AnyThread(FComponentSpacePoseC //SCOPED_NAMED_EVENT_TEXT("FAnimNode_Ragdoll::EvaluateSkeletalControl_AnyThread", FColor::Magenta); // Update our eval counter, and decide whether we need to reset simulated bodies, if our anim instance hasn't updated in a while. - if(EvalCounter.HasEverBeenUpdated() && !EvalCounter.WasSynchronizedLastFrame(Output.AnimInstanceProxy->GetEvaluationCounter())) + if(EvalCounter.HasEverBeenUpdated()) { - ResetSimulatedTeleportType = ETeleportType::ResetPhysics; + // Always propagate skip rate as it can go up and down between updates + EvalCounter.SetMaxSkippedFrames(Output.AnimInstanceProxy->GetEvaluationCounter().GetMaxSkippedFrames()); + if(!EvalCounter.WasSynchronizedLastFrame(Output.AnimInstanceProxy->GetEvaluationCounter())) + { + ResetSimulatedTeleportType = ETeleportType::ResetPhysics; + } } EvalCounter.SynchronizeWith(Output.AnimInstanceProxy->GetEvaluationCounter()); diff --git a/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalCommandQueue.cpp b/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalCommandQueue.cpp index 4b225d28aa27..4ea52237396a 100644 --- a/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalCommandQueue.cpp +++ b/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalCommandQueue.cpp @@ -90,10 +90,6 @@ FMetalCommandQueue::FMetalCommandQueue(mtlpp::Device InDevice, uint32 const MaxN GMetalFColorVertexFormat = mtlpp::VertexFormat::UChar4Normalized_BGRA; } - if (MaxShaderVersion >= 3) - { - Features |= EMetalFeaturesLinearTextureUAVs; - } if (Vers.majorVersion >= 12) { Features |= EMetalFeaturesMaxThreadsPerThreadgroup; @@ -157,12 +153,6 @@ FMetalCommandQueue::FMetalCommandQueue(mtlpp::Device InDevice, uint32 const MaxN Features |= EMetalFeaturesPresentMinDuration | EMetalFeaturesGPUCaptureManager | EMetalFeaturesBufferSubAllocation | EMetalFeaturesParallelRenderEncoders | EMetalFeaturesPipelineBufferMutability; - // Turn on Linear Texture UAVs! Avoids the need to have function-constants which reduces initial runtime shader compile time - if (MaxShaderVersion >= 3) - { - Features |= EMetalFeaturesLinearTextureUAVs; - } - // Turn on Texture Buffers! These are faster on the GPU as we don't need to do out-of-bounds tests but require Metal 2.1 and macOS 10.14 if (Vers.majorVersion >= 12) { diff --git a/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalPipeline.cpp b/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalPipeline.cpp index af64a84694da..cbb9434dc76f 100644 --- a/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalPipeline.cpp +++ b/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalPipeline.cpp @@ -855,10 +855,10 @@ static FMetalShaderPipeline* CreateMTLRenderPipeline(bool const bSync, FMetalGra } #endif #if METAL_DEBUG_OPTIONS - if (GetMetalDeviceContext().GetCommandQueue().GetRuntimeDebuggingLevel() >= EMetalDebugLevelFastValidation METAL_STATISTIC(|| GetMetalDeviceContext().GetCommandQueue().GetStatistics())) + if (GetMetalDeviceContext().GetCommandQueue().GetRuntimeDebuggingLevel() >= EMetalDebugLevelFastValidation METAL_STATISTICS_ONLY(|| GetMetalDeviceContext().GetCommandQueue().GetStatistics())) { mtlpp::AutoReleasedComputePipelineReflection Reflection; - ComputeOption = mtlpp::PipelineOption::ArgumentInfo|mtlpp::PipelineOption::BufferTypeInfo METAL_STATISTIC(|NSUInteger(EMTLPipelineStats)); + ComputeOption = mtlpp::PipelineOption::ArgumentInfo|mtlpp::PipelineOption::BufferTypeInfo METAL_STATISTICS_ONLY(|NSUInteger(EMTLPipelineStats)); Pipeline->ComputePipelineState = Device.NewComputePipelineState(ComputePipelineDesc, (mtlpp::PipelineOption)ComputeOption, &Reflection, &AutoError); Pipeline->ComputePipelineReflection = Reflection; } @@ -982,9 +982,9 @@ static FMetalShaderPipeline* CreateMTLRenderPipeline(bool const bSync, FMetalGra #if METAL_DEBUG_OPTIONS mtlpp::AutoReleasedRenderPipelineReflection OutReflection; Reflection = &OutReflection; - if (GetMetalDeviceContext().GetCommandQueue().GetRuntimeDebuggingLevel() >= EMetalDebugLevelFastValidation METAL_STATISTIC(|| GetMetalDeviceContext().GetCommandQueue().GetStatistics())) + if (GetMetalDeviceContext().GetCommandQueue().GetRuntimeDebuggingLevel() >= EMetalDebugLevelFastValidation METAL_STATISTICS_ONLY(|| GetMetalDeviceContext().GetCommandQueue().GetStatistics())) { - RenderOption = mtlpp::PipelineOption::ArgumentInfo|mtlpp::PipelineOption::BufferTypeInfo METAL_STATISTIC(|NSUInteger(EMTLPipelineStats)); + RenderOption = mtlpp::PipelineOption::ArgumentInfo|mtlpp::PipelineOption::BufferTypeInfo METAL_STATISTICS_ONLY(|NSUInteger(EMTLPipelineStats)); } #endif diff --git a/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalRHIPrivate.h b/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalRHIPrivate.h index fb75ebafdf7f..32e898244a8a 100644 --- a/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalRHIPrivate.h +++ b/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalRHIPrivate.h @@ -124,9 +124,11 @@ extern bool GMetalSupportsTileShaders; #endif #if METAL_STATISTICS -#define METAL_STATISTIC(Code) Code +#define METAL_STATISTIC(Code) if (GIsMetalInitialized) { Code; } +#define METAL_STATISTICS_ONLY(Code) Code #else #define METAL_STATISTIC(Code) +#define METAL_STATISTICS_ONLY(Code) #endif #define UNREAL_TO_METAL_BUFFER_INDEX(Index) ((MaxMetalStreams - 1) - Index) diff --git a/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalShaders.cpp b/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalShaders.cpp index 86c6c93633de..5e9c13003dfa 100644 --- a/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalShaders.cpp +++ b/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalShaders.cpp @@ -924,12 +924,12 @@ FMetalShaderPipeline* FMetalComputeShader::GetPipeline() METAL_GPUPROFILE(FScopedMetalCPUStats CPUStat(FString::Printf(TEXT("NewComputePipeline: %d_%d"), SourceLen, SourceCRC))); #if METAL_DEBUG_OPTIONS - if (GetMetalDeviceContext().GetCommandQueue().GetRuntimeDebuggingLevel() >= EMetalDebugLevelFastValidation METAL_STATISTIC(|| GetMetalDeviceContext().GetCommandQueue().GetStatistics())) + if (GetMetalDeviceContext().GetCommandQueue().GetRuntimeDebuggingLevel() >= EMetalDebugLevelFastValidation METAL_STATISTICS_ONLY(|| GetMetalDeviceContext().GetCommandQueue().GetStatistics())) { ns::AutoReleasedError ComputeError; mtlpp::AutoReleasedComputePipelineReflection ComputeReflection; - NSUInteger ComputeOption = mtlpp::PipelineOption::ArgumentInfo|mtlpp::PipelineOption::BufferTypeInfo METAL_STATISTIC(|NSUInteger(EMTLPipelineStats)); + NSUInteger ComputeOption = mtlpp::PipelineOption::ArgumentInfo|mtlpp::PipelineOption::BufferTypeInfo METAL_STATISTICS_ONLY(|NSUInteger(EMTLPipelineStats)); Kernel = GetMetalDeviceContext().GetDevice().NewComputePipelineState(Descriptor, mtlpp::PipelineOption(ComputeOption), &ComputeReflection, &ComputeError); Error = ComputeError; Reflection = ComputeReflection; diff --git a/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalStateCache.cpp b/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalStateCache.cpp index ec519c48f5b5..886e54705005 100644 --- a/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalStateCache.cpp +++ b/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalStateCache.cpp @@ -533,29 +533,44 @@ bool FMetalStateCache::SetRenderTargetsInfo(FRHISetRenderTargetsInfo const& InRe ERenderTargetStoreAction HighLevelStoreAction = RenderTargetView.StoreAction; ERenderTargetLoadAction HighLevelLoadAction = RenderTargetView.LoadAction; + + // on iOS with memory-less MSAA textures we can't load them + // in case high level code wants to load and render to MSAA target, set attachment to a resolved texture + bool bUseResolvedTexture = false; +#if PLATFORM_IOS + bUseResolvedTexture = ( + Surface.MSAATexture && + Surface.MSAATexture.GetStorageMode() == mtlpp::StorageMode::Memoryless && + HighLevelLoadAction == ERenderTargetLoadAction::ELoad); +#endif - if (Surface.MSAATexture) + if (Surface.MSAATexture && !bUseResolvedTexture) { - // set up an MSAA attachment - ColorAttachment.SetTexture(Surface.MSAATexture); - NewColorStore[RenderTargetIndex] = GetMetalRTStoreAction(ERenderTargetStoreAction::EMultisampleResolve); - ColorAttachment.SetStoreAction(bSupportsDeferredStore && GRHIDeviceId > 2 ? mtlpp::StoreAction::Unknown : NewColorStore[RenderTargetIndex]); - ColorAttachment.SetResolveTexture(Surface.MSAAResolveTexture ? Surface.MSAAResolveTexture : Surface.Texture); - SampleCount = Surface.MSAATexture.GetSampleCount(); + bool bMemoryless = false; #if PLATFORM_IOS if (Surface.MSAATexture.GetStorageMode() == mtlpp::StorageMode::Memoryless) { + bMemoryless = true; HighLevelLoadAction = ERenderTargetLoadAction::EClear; } #endif + // set up an MSAA attachment + ColorAttachment.SetTexture(Surface.MSAATexture); + NewColorStore[RenderTargetIndex] = GetMetalRTStoreAction(ERenderTargetStoreAction::EMultisampleResolve); + + ColorAttachment.SetStoreAction(bSupportsDeferredStore && !bMemoryless && GRHIDeviceId > 2 ? mtlpp::StoreAction::Unknown : NewColorStore[RenderTargetIndex]); + ColorAttachment.SetResolveTexture(Surface.MSAAResolveTexture ? Surface.MSAAResolveTexture : Surface.Texture); + SampleCount = Surface.MSAATexture.GetSampleCount(); // only allow one MRT with msaa checkf(RenderTargetsInfo.NumColorRenderTargets == 1, TEXT("Only expected one MRT when using MSAA")); } else { + bool bMemoryless = false; #if PLATFORM_IOS if (Surface.Texture.GetStorageMode() == mtlpp::StorageMode::Memoryless) { + bMemoryless = true; HighLevelStoreAction = ERenderTargetStoreAction::ENoAction; HighLevelLoadAction = ERenderTargetLoadAction::EClear; } @@ -563,7 +578,7 @@ bool FMetalStateCache::SetRenderTargetsInfo(FRHISetRenderTargetsInfo const& InRe // set up non-MSAA attachment ColorAttachment.SetTexture(Surface.Texture); NewColorStore[RenderTargetIndex] = GetMetalRTStoreAction(HighLevelStoreAction); - ColorAttachment.SetStoreAction(bSupportsDeferredStore ? mtlpp::StoreAction::Unknown : NewColorStore[RenderTargetIndex]); + ColorAttachment.SetStoreAction(bSupportsDeferredStore && !bMemoryless ? mtlpp::StoreAction::Unknown : NewColorStore[RenderTargetIndex]); SampleCount = 1; } @@ -806,7 +821,7 @@ bool FMetalStateCache::SetRenderTargetsInfo(FRHISetRenderTargetsInfo const& InRe //needed to quiet the metal validation that runs when you end renderpass. (it requires some kind of 'resolve' for an msaa target) //But with deferredstore we don't set the real one until submit time. NewDepthStore = !Surface.MSAATexture || bSupportsMSAADepthResolve ? GetMetalRTStoreAction(HighLevelStoreAction) : mtlpp::StoreAction::DontCare; - DepthAttachment.SetStoreAction(bSupportsDeferredStore && Surface.MSAATexture && GRHIDeviceId > 2 ? mtlpp::StoreAction::Unknown : NewDepthStore); + DepthAttachment.SetStoreAction(bSupportsDeferredStore && !bDepthTextureMemoryless && Surface.MSAATexture && GRHIDeviceId > 2 ? mtlpp::StoreAction::Unknown : NewDepthStore); DepthAttachment.SetClearDepth(DepthClearValue); check(SampleCount > 0); @@ -862,9 +877,11 @@ bool FMetalStateCache::SetRenderTargetsInfo(FRHISetRenderTargetsInfo const& InRe HighLevelStoreAction = ERenderTargetStoreAction::EStore; } + bool bStencilMemoryless = false; #if PLATFORM_IOS if (StencilTexture.GetStorageMode() == mtlpp::StorageMode::Memoryless) { + bStencilMemoryless = true; HighLevelStoreAction = ERenderTargetStoreAction::ENoAction; StencilAttachment.SetLoadAction(mtlpp::LoadAction::Clear); } @@ -873,7 +890,7 @@ bool FMetalStateCache::SetRenderTargetsInfo(FRHISetRenderTargetsInfo const& InRe // For the case where Depth+Stencil is MSAA we can't Resolve depth and Store stencil - we can only Resolve + DontCare or StoreResolve + Store (on newer H/W and iOS). // We only allow use of StoreResolve in the Desktop renderers as the mobile renderer does not and should not assume hardware support for it. NewStencilStore = (StencilTexture.GetSampleCount() == 1 || GetMetalRTStoreAction(ERenderTargetStoreAction::EMultisampleResolve) == mtlpp::StoreAction::StoreAndMultisampleResolve) ? GetMetalRTStoreAction(HighLevelStoreAction) : mtlpp::StoreAction::DontCare; - StencilAttachment.SetStoreAction(bSupportsDeferredStore && StencilTexture.GetSampleCount() > 1 && GRHIDeviceId > 2 ? mtlpp::StoreAction::Unknown : NewStencilStore); + StencilAttachment.SetStoreAction(bSupportsDeferredStore && !bStencilMemoryless && StencilTexture.GetSampleCount() > 1 && GRHIDeviceId > 2 ? mtlpp::StoreAction::Unknown : NewStencilStore); StencilAttachment.SetClearStencil(StencilClearValue); if (SampleCount == 0) diff --git a/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalTexture.cpp b/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalTexture.cpp index 3c60b9b1e52a..1d2f5c0ebebb 100644 --- a/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalTexture.cpp +++ b/Engine/Source/Runtime/Apple/MetalRHI/Private/MetalTexture.cpp @@ -876,19 +876,9 @@ FMetalSurface::FMetalSurface(ERHIResourceType ResourceType, EPixelFormat Format, else if (Flags & (TexCreate_RenderTargetable|TexCreate_DepthStencilTargetable|TexCreate_ResolveTargetable|TexCreate_DepthStencilResolveTarget)) { check(!(Flags & TexCreate_CPUReadback)); -#if PLATFORM_IOS - if (FMetalCommandQueue::SupportsFeature(EMetalFeaturesMemoryLessResources) && !(Flags & (TexCreate_ShaderResource|TexCreate_UAV)) && (GMaxRHIFeatureLevel < ERHIFeatureLevel::SM5)) - { - Desc.SetStorageMode(mtlpp::StorageMode::Memoryless); - Desc.SetResourceOptions(mtlpp::ResourceOptions::StorageModeMemoryless); - } - else -#endif - { - Desc.SetCpuCacheMode(mtlpp::CpuCacheMode::DefaultCache); - Desc.SetStorageMode(mtlpp::StorageMode::Private); - Desc.SetResourceOptions((mtlpp::ResourceOptions)(mtlpp::ResourceOptions::CpuCacheModeDefaultCache|mtlpp::ResourceOptions::StorageModePrivate)); - } + Desc.SetCpuCacheMode(mtlpp::CpuCacheMode::DefaultCache); + Desc.SetStorageMode(mtlpp::StorageMode::Private); + Desc.SetResourceOptions((mtlpp::ResourceOptions)(mtlpp::ResourceOptions::CpuCacheModeDefaultCache|mtlpp::ResourceOptions::StorageModePrivate)); } else { diff --git a/Engine/Source/Runtime/Apple/MetalRHI/Public/ue4_stdlib.h b/Engine/Source/Runtime/Apple/MetalRHI/Public/ue4_stdlib.h index 0f37ec7ec207..18ea67f799fb 100644 --- a/Engine/Source/Runtime/Apple/MetalRHI/Public/ue4_stdlib.h +++ b/Engine/Source/Runtime/Apple/MetalRHI/Public/ue4_stdlib.h @@ -1080,7 +1080,123 @@ unsigned char ue4_stdlib_metal[] = { 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, + 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, + 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, + 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x72, 0x63, + 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x32, 0x28, 0x69, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, + 0x20, 0x69, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x29, 0x29, + 0x2e, 0x78, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, + 0x68, 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, + 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x72, + 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x79, + 0x20, 0x3d, 0x20, 0x69, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x28, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x28, + 0x30, 0x29, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x61, 0x64, + 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x20, 0x25, + 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x79, 0x29, 0x29, 0x2e, + 0x78, 0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x28, 0x79, 0x20, 0x3c, 0x20, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, + 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x20, 0x41, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, + 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, + 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, + 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, + 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, + 0x65, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x20, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, + 0x3d, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x73, 0x72, + 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, + 0x29, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x6d, + 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x28, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x2e, 0x78, 0x29, 0x20, 0x2a, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, + 0x79, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x28, 0x76, 0x65, 0x63, 0x3c, 0x46, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x34, 0x3e, 0x28, 0x76, + 0x29, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x2c, 0x20, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x29, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x09, + 0x09, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, + 0x54, 0x55, 0x52, 0x45, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, + 0x5f, 0x29, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, + 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, + 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, + 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, + 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, + 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x46, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, + 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, + 0x61, 0x64, 0x28, 0x69, 0x29, 0x2e, 0x78, 0x3b, 0x0a, 0x09, 0x09, 0x7d, + 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, + 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, + 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, + 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, + 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, + 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x46, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, + 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x69, 0x2c, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x76, + 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x73, 0x72, 0x63, + 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x76, 0x65, 0x63, 0x3c, 0x46, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x34, + 0x3e, 0x28, 0x76, 0x29, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x09, 0x09, + 0x7d, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x09, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, + 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, + 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, + 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, + 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, @@ -1089,99 +1205,229 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x69, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, - 0x74, 0x68, 0x29, 0x29, 0x2e, 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, + 0x74, 0x68, 0x29, 0x29, 0x2e, 0x78, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, + 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, + 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, + 0x68, 0x6f, 0x72, 0x74, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, + 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x20, 0x79, 0x20, 0x3d, 0x20, 0x69, 0x20, 0x2f, 0x20, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x53, 0x72, 0x63, 0x54, + 0x79, 0x70, 0x65, 0x28, 0x30, 0x29, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x2e, + 0x72, 0x65, 0x61, 0x64, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, + 0x28, 0x69, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, + 0x79, 0x29, 0x29, 0x2e, 0x78, 0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x28, + 0x79, 0x20, 0x3c, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x29, 0x29, + 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x7d, + 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, + 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, + 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, + 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, + 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, + 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x53, 0x72, + 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, + 0x0a, 0x09, 0x09, 0x09, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x20, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x20, 0x3d, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x73, + 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x28, 0x29, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, 0x29, 0x3b, 0x0a, 0x09, + 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x28, 0x75, + 0x69, 0x6e, 0x74, 0x28, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x29, 0x20, 0x2a, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x28, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x2e, 0x79, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, + 0x09, 0x73, 0x72, 0x63, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x76, + 0x65, 0x63, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x2c, 0x20, 0x34, 0x3e, 0x28, 0x76, 0x29, 0x2c, 0x20, 0x75, 0x73, + 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, + 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x2e, 0x78, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, + 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x2e, 0x78, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, + 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, + 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, + 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x3e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, + 0x3c, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x46, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x32, 0x3e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, + 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, + 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, + 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, + 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, + 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x75, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x2c, 0x20, 0x69, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x29, 0x29, 0x2e, 0x78, 0x79, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, + 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, + 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, + 0x68, 0x6f, 0x72, 0x74, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, + 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x20, 0x79, 0x20, 0x3d, 0x20, 0x69, 0x20, 0x2f, 0x20, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x53, 0x72, 0x63, 0x54, + 0x79, 0x70, 0x65, 0x28, 0x30, 0x29, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x2e, + 0x72, 0x65, 0x61, 0x64, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, + 0x28, 0x69, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, + 0x79, 0x29, 0x29, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, + 0x28, 0x79, 0x20, 0x3c, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x29, + 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, + 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, + 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x53, + 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x32, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x32, 0x28, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x2e, + 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x28, + 0x75, 0x69, 0x6e, 0x74, 0x28, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x29, 0x20, 0x2a, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x28, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x2e, 0x79, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x72, 0x63, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x76, 0x65, 0x63, + 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, + 0x20, 0x34, 0x3e, 0x28, 0x76, 0x2e, 0x78, 0x79, 0x78, 0x79, 0x29, 0x2c, + 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x2c, 0x20, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x29, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x09, 0x09, 0x0a, + 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, + 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, + 0x52, 0x45, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x5f, 0x29, + 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, + 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, + 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, + 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, + 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, + 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x46, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, + 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x61, 0x64, + 0x28, 0x69, 0x29, 0x2e, 0x78, 0x79, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, + 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, + 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, - 0x65, 0x32, 0x64, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x53, 0x72, 0x63, - 0x54, 0x79, 0x70, 0x65, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, - 0x32, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x20, 0x3d, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, - 0x28, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, - 0x74, 0x68, 0x28, 0x29, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, - 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, 0x29, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, - 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x28, 0x75, 0x69, - 0x6e, 0x74, 0x28, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x2e, 0x78, 0x29, 0x20, 0x2a, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x28, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x2e, 0x79, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x72, 0x63, - 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x76, 0x65, 0x63, 0x3c, 0x46, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x34, - 0x3e, 0x28, 0x76, 0x29, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, - 0x32, 0x28, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x25, 0x20, 0x77, 0x69, - 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, - 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x2f, 0x20, 0x77, 0x69, - 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, - 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, - 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, - 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x42, 0x55, 0x46, 0x46, - 0x45, 0x52, 0x5f, 0x5f, 0x29, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, - 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, - 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x29, 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6c, - 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, - 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, - 0x72, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x20, 0x69, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, - 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x72, 0x63, - 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x69, 0x29, 0x2e, 0x78, 0x3b, 0x0a, - 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, - 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, - 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, - 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, - 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, - 0x65, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, - 0x73, 0x72, 0x63, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x76, 0x65, - 0x63, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x2c, 0x20, 0x34, 0x3e, 0x28, 0x76, 0x29, 0x2c, 0x20, 0x69, 0x29, 0x3b, - 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, - 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, - 0x79, 0x70, 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, - 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, - 0x64, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x20, 0x69, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, - 0x09, 0x09, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, 0x64, + 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x46, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, + 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x2c, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x76, 0x29, + 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x73, 0x72, 0x63, 0x2e, + 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x76, 0x65, 0x63, 0x3c, 0x46, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x34, 0x3e, + 0x28, 0x76, 0x2e, 0x78, 0x79, 0x78, 0x79, 0x29, 0x2c, 0x20, 0x69, 0x29, + 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, + 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, 0x63, + 0x54, 0x79, 0x70, 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, + 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, + 0x32, 0x64, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, + 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, + 0x5f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, - 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x09, 0x09, - 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x72, 0x63, 0x2e, - 0x72, 0x65, 0x61, 0x64, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, - 0x28, 0x69, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, - 0x69, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x29, 0x29, 0x2e, - 0x78, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, + 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x61, + 0x64, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x20, + 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x69, 0x20, 0x2f, + 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x29, 0x29, 0x2e, 0x78, 0x79, 0x3b, + 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x72, + 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, + 0x65, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x79, 0x20, 0x3d, 0x20, + 0x69, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x28, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x28, 0x30, 0x29, 0x2c, + 0x20, 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x75, 0x73, + 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x20, 0x25, 0x20, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x2c, 0x20, 0x79, 0x29, 0x29, 0x2e, 0x78, 0x79, 0x2c, + 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x28, 0x79, 0x20, 0x3c, 0x20, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, + 0x69, 0x66, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, @@ -1208,290 +1454,177 @@ unsigned char ue4_stdlib_metal[] = { 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x73, 0x72, 0x63, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x76, 0x65, 0x63, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x34, 0x3e, 0x28, - 0x76, 0x29, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, - 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x2c, 0x20, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, - 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x29, 0x29, - 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x53, - 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, - 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, - 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x3c, 0x53, 0x72, 0x63, 0x54, 0x79, - 0x70, 0x65, 0x2c, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x2c, 0x20, 0x32, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, - 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, - 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, - 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, - 0x65, 0x32, 0x64, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, - 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x72, - 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, - 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x72, - 0x63, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, - 0x74, 0x32, 0x28, 0x69, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, - 0x2c, 0x20, 0x69, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x29, - 0x29, 0x2e, 0x78, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, - 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, - 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, - 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, - 0x64, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, - 0x70, 0x65, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x20, - 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x20, 0x3d, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x73, - 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, - 0x28, 0x29, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, 0x29, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, - 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, - 0x28, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x2e, 0x78, 0x29, 0x20, 0x2a, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, - 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x2e, 0x79, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x77, - 0x72, 0x69, 0x74, 0x65, 0x28, 0x76, 0x65, 0x63, 0x3c, 0x46, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x34, 0x3e, 0x28, 0x76, 0x2e, 0x78, 0x79, 0x78, 0x79, 0x29, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x2e, 0x78, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x23, 0x69, 0x66, 0x20, - 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, - 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x42, - 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x5f, 0x29, 0x0a, 0x09, 0x09, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, - 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, - 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, - 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, - 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, - 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x29, 0x0a, 0x09, 0x09, - 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, - 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x69, 0x29, 0x2e, - 0x78, 0x79, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, - 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, - 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, - 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, - 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x53, 0x72, - 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, - 0x0a, 0x09, 0x09, 0x09, 0x73, 0x72, 0x63, 0x2e, 0x77, 0x72, 0x69, 0x74, - 0x65, 0x28, 0x76, 0x65, 0x63, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x34, 0x3e, 0x28, 0x76, 0x2e, 0x78, - 0x79, 0x78, 0x79, 0x29, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x09, 0x09, - 0x7d, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x09, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, - 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, - 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, - 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, - 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x69, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x75, - 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, - 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, - 0x64, 0x74, 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, - 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x61, - 0x64, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x20, - 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x69, 0x20, 0x2f, - 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x29, 0x29, 0x2e, 0x78, 0x79, 0x3b, - 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, - 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, - 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, - 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, - 0x2c, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x76, 0x29, - 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x75, 0x73, 0x68, 0x6f, - 0x72, 0x74, 0x32, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, - 0x74, 0x32, 0x28, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, - 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x2e, - 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, - 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, - 0x2c, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x77, 0x69, 0x64, 0x74, - 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x29, 0x20, - 0x2a, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x77, 0x69, 0x64, 0x74, 0x68, - 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x79, 0x29, 0x29, 0x29, - 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x73, 0x72, 0x63, 0x2e, 0x77, 0x72, 0x69, - 0x74, 0x65, 0x28, 0x76, 0x65, 0x63, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x34, 0x3e, 0x28, 0x76, 0x2e, - 0x78, 0x79, 0x78, 0x79, 0x29, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, - 0x74, 0x32, 0x28, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x25, 0x20, 0x77, - 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, - 0x78, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x2f, 0x20, 0x77, - 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, - 0x78, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x23, 0x65, 0x6e, - 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, - 0x65, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x74, - 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x3c, 0x53, 0x72, - 0x63, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x33, 0x3e, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, - 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, - 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, - 0x52, 0x5f, 0x5f, 0x29, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, - 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, - 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, - 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6c, 0x6f, - 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, - 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, - 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, - 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x69, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, - 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x72, 0x63, 0x2e, - 0x72, 0x65, 0x61, 0x64, 0x28, 0x69, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x3b, - 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, - 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, - 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, - 0x72, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, - 0x70, 0x65, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, - 0x09, 0x73, 0x72, 0x63, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x76, - 0x65, 0x63, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x2c, 0x20, 0x34, 0x3e, 0x28, 0x76, 0x2e, 0x78, 0x79, 0x7a, 0x78, - 0x29, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x23, - 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, - 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, - 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, - 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6c, 0x6f, - 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, - 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, - 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, - 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, - 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x75, 0x73, 0x68, 0x6f, - 0x72, 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, - 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, - 0x28, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x75, - 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x20, 0x25, 0x20, 0x77, - 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x69, 0x20, 0x2f, 0x20, 0x77, 0x69, - 0x64, 0x74, 0x68, 0x29, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x3b, 0x0a, 0x09, - 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, - 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, - 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, - 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, - 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, - 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x76, 0x29, 0x0a, 0x09, - 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, - 0x32, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x20, 0x3d, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, - 0x28, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, - 0x74, 0x68, 0x28, 0x29, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, - 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, 0x29, 0x3b, - 0x0a, 0x09, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, - 0x28, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x29, 0x20, 0x2a, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x28, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x79, 0x29, 0x29, 0x29, 0x3b, 0x0a, - 0x09, 0x09, 0x09, 0x73, 0x72, 0x63, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, - 0x28, 0x76, 0x65, 0x63, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x2c, 0x20, 0x34, 0x3e, 0x28, 0x76, 0x2e, 0x78, 0x79, - 0x7a, 0x78, 0x29, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, - 0x28, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, - 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x2c, - 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, - 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x29, - 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, + 0x74, 0x2e, 0x78, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x23, + 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2c, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x3e, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x3c, + 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x46, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x33, 0x3e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, + 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x42, 0x55, 0x46, + 0x46, 0x45, 0x52, 0x5f, 0x5f, 0x29, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, + 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x72, + 0x63, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x69, 0x29, 0x2e, 0x78, 0x79, + 0x7a, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, - 0x70, 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, - 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, - 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, - 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x69, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, - 0x09, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, + 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x53, 0x72, 0x63, + 0x54, 0x79, 0x70, 0x65, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, + 0x09, 0x09, 0x09, 0x73, 0x72, 0x63, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x28, 0x76, 0x65, 0x63, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x2c, 0x20, 0x34, 0x3e, 0x28, 0x76, 0x2e, 0x78, 0x79, + 0x7a, 0x78, 0x29, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, + 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, + 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, + 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, + 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, + 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x75, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x2c, 0x20, 0x69, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x29, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x3b, 0x0a, 0x23, 0x65, 0x6c, + 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, + 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x20, 0x79, 0x20, 0x3d, 0x20, 0x69, 0x20, 0x2f, 0x20, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x53, 0x72, 0x63, + 0x54, 0x79, 0x70, 0x65, 0x28, 0x30, 0x29, 0x2c, 0x20, 0x73, 0x72, 0x63, + 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x32, 0x28, 0x69, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, + 0x20, 0x79, 0x29, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2c, 0x20, 0x62, 0x6f, + 0x6f, 0x6c, 0x28, 0x79, 0x20, 0x3c, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, + 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, + 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, + 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x76, 0x29, 0x0a, + 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x75, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x32, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x32, 0x28, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x28, 0x29, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, + 0x65, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, 0x29, + 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, + 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x29, 0x20, 0x2a, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x79, 0x29, 0x29, 0x29, 0x3b, + 0x0a, 0x09, 0x09, 0x09, 0x73, 0x72, 0x63, 0x2e, 0x77, 0x72, 0x69, 0x74, + 0x65, 0x28, 0x76, 0x65, 0x63, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x34, 0x3e, 0x28, 0x76, 0x2e, 0x78, + 0x79, 0x7a, 0x78, 0x29, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x32, 0x28, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x25, 0x20, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, + 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x2f, 0x20, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, + 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, + 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, + 0x79, 0x70, 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, + 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, + 0x64, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x69, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, + 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, - 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, - 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x72, - 0x65, 0x61, 0x64, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, - 0x69, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x69, - 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x29, 0x29, 0x2e, 0x78, - 0x79, 0x7a, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x61, 0x64, + 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x20, 0x25, + 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x69, 0x20, 0x2f, 0x20, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x29, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x3b, + 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x72, + 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, + 0x65, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x79, 0x20, 0x3d, 0x20, + 0x69, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x28, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x28, 0x30, 0x29, 0x2c, + 0x20, 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x75, 0x73, + 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x20, 0x25, 0x20, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x2c, 0x20, 0x79, 0x29, 0x29, 0x2e, 0x78, 0x79, 0x7a, + 0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x28, 0x79, 0x20, 0x3c, 0x20, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, + 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, @@ -1580,205 +1713,193 @@ unsigned char ue4_stdlib_metal[] = { 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, - 0x09, 0x09, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, 0x64, - 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, - 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x09, 0x09, - 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x72, 0x63, 0x2e, - 0x72, 0x65, 0x61, 0x64, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, - 0x28, 0x69, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, - 0x69, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x29, 0x29, 0x3b, - 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, - 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, - 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, - 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, - 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, - 0x2c, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x76, 0x29, - 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x75, 0x73, 0x68, 0x6f, - 0x72, 0x74, 0x32, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, - 0x74, 0x32, 0x28, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, - 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x2e, - 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, - 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, - 0x2c, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x77, 0x69, 0x64, 0x74, - 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x29, 0x20, - 0x2a, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x77, 0x69, 0x64, 0x74, 0x68, - 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x79, 0x29, 0x29, 0x29, - 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x73, 0x72, 0x63, 0x2e, 0x77, 0x72, 0x69, - 0x74, 0x65, 0x28, 0x76, 0x65, 0x63, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x34, 0x3e, 0x28, 0x76, 0x29, - 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x2c, 0x20, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x20, 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x29, 0x29, 0x3b, 0x0a, - 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, - 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, - 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, - 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, 0x6f, - 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, - 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, - 0x69, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x75, 0x73, - 0x68, 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x3d, - 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, - 0x74, 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, + 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, + 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x20, 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x69, 0x20, 0x2f, 0x20, - 0x77, 0x69, 0x64, 0x74, 0x68, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, - 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x20, 0x3c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, - 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, - 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, - 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, - 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, - 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, - 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x53, 0x72, - 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, - 0x0a, 0x09, 0x09, 0x09, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x20, - 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x20, 0x3d, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x73, - 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, - 0x28, 0x29, 0x2c, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, 0x29, 0x3b, 0x0a, 0x09, - 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, - 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x28, 0x75, - 0x69, 0x6e, 0x74, 0x28, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, 0x29, 0x20, 0x2a, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x28, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x2e, 0x79, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, - 0x09, 0x73, 0x72, 0x63, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x76, - 0x65, 0x63, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x2c, 0x20, 0x34, 0x3e, 0x28, 0x76, 0x29, 0x2c, 0x20, 0x75, 0x73, - 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, - 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x2e, 0x78, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, - 0x2f, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x2e, 0x78, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, - 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, - 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x3e, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, - 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, + 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, + 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x20, 0x79, 0x20, 0x3d, 0x20, 0x69, 0x20, 0x2f, 0x20, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x53, 0x72, 0x63, + 0x54, 0x79, 0x70, 0x65, 0x28, 0x30, 0x29, 0x2c, 0x20, 0x53, 0x72, 0x63, + 0x54, 0x79, 0x70, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x61, + 0x64, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x20, + 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x79, 0x29, 0x29, + 0x29, 0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x28, 0x79, 0x20, 0x3c, 0x20, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, + 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, + 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, 0x63, - 0x54, 0x79, 0x70, 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, - 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x53, - 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, - 0x69, 0x2c, 0x20, 0x28, 0x6c, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, - 0x6f, 0x66, 0x28, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x29, 0x29, 0x20, 0x2d, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, - 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, - 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x28, 0x30, 0x29, 0x2c, 0x20, - 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x28, 0x28, 0x28, 0x63, 0x6f, - 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x46, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2a, 0x29, 0x73, - 0x72, 0x63, 0x29, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x29, 0x2c, - 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x28, 0x69, 0x20, 0x3c, 0x20, 0x28, 0x6c, - 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x46, 0x6f, - 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x29, 0x29, 0x29, 0x29, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, - 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, - 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x53, 0x72, 0x63, 0x54, - 0x79, 0x70, 0x65, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, + 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, + 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, + 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, + 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, - 0x65, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, + 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x20, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x75, + 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x73, 0x72, 0x63, 0x2e, 0x67, + 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x2c, 0x20, + 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x28, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x6d, + 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x28, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x2e, 0x78, 0x29, 0x20, 0x2a, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, + 0x79, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x73, 0x72, 0x63, + 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x76, 0x65, 0x63, 0x3c, 0x46, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x34, + 0x3e, 0x28, 0x76, 0x29, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x32, 0x28, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x25, 0x20, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, + 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x2f, 0x20, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, + 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, + 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, + 0x79, 0x70, 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, + 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, + 0x64, 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x69, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, + 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x61, 0x64, + 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x20, 0x25, + 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x69, 0x20, 0x2f, 0x20, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, + 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, + 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x20, 0x3d, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x20, 0x79, 0x20, 0x3d, 0x20, 0x69, 0x20, 0x2f, 0x20, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x53, 0x72, 0x63, + 0x54, 0x79, 0x70, 0x65, 0x28, 0x30, 0x29, 0x2c, 0x20, 0x53, 0x72, 0x63, + 0x54, 0x79, 0x70, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2e, 0x72, 0x65, 0x61, + 0x64, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x69, 0x20, + 0x25, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x2c, 0x20, 0x79, 0x29, 0x29, + 0x29, 0x2c, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x28, 0x79, 0x20, 0x3c, 0x20, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, + 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, + 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x3c, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, + 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, + 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, + 0x3c, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, + 0x20, 0x41, 0x3e, 0x26, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, + 0x65, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, + 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x20, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x75, + 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x28, 0x73, 0x72, 0x63, 0x2e, 0x67, + 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x2c, 0x20, + 0x73, 0x72, 0x63, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x28, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x6d, + 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x28, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x2e, 0x78, 0x29, 0x20, 0x2a, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, + 0x79, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x73, 0x72, 0x63, + 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x76, 0x65, 0x63, 0x3c, 0x46, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x34, + 0x3e, 0x28, 0x76, 0x29, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x32, 0x28, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x25, 0x20, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, + 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x2f, 0x20, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x78, + 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, + 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, + 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, + 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, + 0x70, 0x65, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x28, 0x6c, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x46, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x29, 0x29, 0x29, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x28, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x46, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2a, 0x29, 0x73, - 0x72, 0x63, 0x29, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x20, 0x3d, - 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x28, - 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, - 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x74, 0x79, 0x70, - 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x46, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, - 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2a, 0x20, 0x73, - 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x6d, - 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x28, 0x6c, 0x20, 0x2f, 0x20, 0x73, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x29, 0x29, 0x20, + 0x2d, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x53, 0x72, 0x63, 0x54, + 0x79, 0x70, 0x65, 0x28, 0x30, 0x29, 0x2c, 0x20, 0x53, 0x72, 0x63, 0x54, + 0x79, 0x70, 0x65, 0x28, 0x28, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x2a, 0x29, 0x73, 0x72, 0x63, 0x29, 0x5b, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x29, 0x2c, 0x20, 0x62, 0x6f, 0x6f, + 0x6c, 0x28, 0x69, 0x20, 0x3c, 0x20, 0x28, 0x6c, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x29, 0x29, 0x20, 0x2d, 0x20, 0x31, 0x29, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x28, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x28, 0x30, 0x29, 0x2c, 0x20, 0x28, 0x28, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x46, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2a, 0x29, 0x73, 0x72, 0x63, - 0x29, 0x5b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x2c, 0x20, 0x28, 0x69, - 0x20, 0x3c, 0x20, 0x28, 0x6c, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, - 0x6f, 0x66, 0x28, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x29, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, - 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, - 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2a, 0x20, 0x73, 0x72, - 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, - 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x46, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x20, 0x76, 0x29, + 0x54, 0x79, 0x70, 0x65, 0x29, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, + 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2a, + 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, + 0x2c, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, @@ -1788,7 +1909,8 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2a, 0x29, 0x73, 0x72, 0x63, 0x29, 0x5b, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x20, 0x3d, 0x20, 0x76, 0x3b, 0x0a, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x20, 0x3d, 0x20, 0x46, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, @@ -1797,24 +1919,302 @@ unsigned char ue4_stdlib_metal[] = { 0x65, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, + 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x29, 0x29, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x53, 0x72, + 0x63, 0x54, 0x79, 0x70, 0x65, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, + 0x2c, 0x20, 0x28, 0x6c, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, + 0x66, 0x28, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x29, 0x29, 0x20, 0x2d, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x46, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x28, 0x30, 0x29, + 0x2c, 0x20, 0x28, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x2a, 0x29, 0x73, 0x72, 0x63, 0x29, 0x5b, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x5d, 0x2c, 0x20, 0x28, 0x69, 0x20, 0x3c, 0x20, 0x28, + 0x6c, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x46, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x29, 0x29, 0x29, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, + 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x53, 0x72, 0x63, + 0x54, 0x79, 0x70, 0x65, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, + 0x69, 0x2c, 0x20, 0x28, 0x6c, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, + 0x6f, 0x66, 0x28, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x28, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x2a, 0x29, 0x73, 0x72, 0x63, 0x29, 0x5b, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x5d, 0x20, 0x3d, 0x20, 0x76, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2c, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x3e, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, + 0x72, 0x6d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, + 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6c, 0x6f, + 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2a, + 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x53, 0x72, 0x63, 0x54, + 0x79, 0x70, 0x65, 0x2c, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, + 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, + 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, + 0x65, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, + 0x20, 0x6c, 0x2c, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, + 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x3c, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x46, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x3e, 0x3a, 0x3a, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, + 0x6d, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, + 0x72, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, + 0x29, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x20, 0x68, 0x61, 0x6c, 0x66, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, + 0x72, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x68, 0x61, + 0x6c, 0x66, 0x28, 0x32, 0x35, 0x35, 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, + 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, + 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x2a, 0x20, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, + 0x20, 0x68, 0x61, 0x6c, 0x66, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x2c, + 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x2c, 0x20, 0x28, 0x76, 0x20, 0x2a, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, + 0x32, 0x35, 0x35, 0x2e, 0x66, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, + 0x6f, 0x72, 0x6d, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x2c, 0x20, 0x75, + 0x63, 0x68, 0x61, 0x72, 0x32, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, - 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x53, 0x72, 0x63, 0x54, - 0x79, 0x70, 0x65, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, - 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x46, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, - 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x20, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x2a, + 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, + 0x32, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x32, 0x3e, 0x3a, 0x3a, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x32, + 0x35, 0x35, 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, + 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x2a, 0x20, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x68, 0x61, + 0x6c, 0x66, 0x32, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x2c, 0x20, + 0x75, 0x63, 0x68, 0x61, 0x72, 0x32, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x2c, 0x20, 0x28, 0x76, 0x20, 0x2a, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, + 0x32, 0x35, 0x35, 0x2e, 0x66, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, + 0x6f, 0x72, 0x6d, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2c, 0x20, 0x75, + 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, + 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x20, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2a, + 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, + 0x33, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x32, + 0x35, 0x35, 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, + 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x68, 0x61, + 0x6c, 0x66, 0x33, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2c, 0x20, + 0x75, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x2c, 0x20, 0x28, 0x76, 0x20, 0x2a, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, + 0x32, 0x35, 0x35, 0x2e, 0x66, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, + 0x6f, 0x72, 0x6d, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x2c, 0x20, 0x75, + 0x63, 0x68, 0x61, 0x72, 0x34, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, + 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x20, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x2a, + 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x75, 0x6e, 0x70, 0x61, 0x63, + 0x6b, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x34, 0x78, 0x38, 0x5f, 0x74, + 0x6f, 0x5f, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x3c, + 0x68, 0x61, 0x6c, 0x66, 0x34, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x3e, + 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, + 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x2a, 0x20, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x68, 0x61, + 0x6c, 0x66, 0x34, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x3c, 0x68, 0x61, + 0x6c, 0x66, 0x34, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x3e, 0x3a, 0x3a, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x68, 0x61, + 0x6c, 0x66, 0x5f, 0x74, 0x6f, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x34, + 0x78, 0x38, 0x28, 0x76, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, + 0x6d, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, + 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x29, 0x29, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x20, 0x6c, 0x6f, 0x61, 0x64, + 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x2c, 0x20, 0x75, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, + 0x68, 0x61, 0x6c, 0x66, 0x28, 0x36, 0x35, 0x35, 0x33, 0x36, 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, @@ -1822,43 +2222,178 @@ unsigned char ue4_stdlib_metal[] = { 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x53, 0x72, - 0x63, 0x54, 0x79, 0x70, 0x65, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x53, 0x72, 0x63, 0x54, - 0x79, 0x70, 0x65, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x3c, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, - 0x2c, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, - 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, - 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2c, - 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, + 0x6c, 0x66, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x20, 0x76, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, + 0x61, 0x6c, 0x66, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x3e, + 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x28, 0x76, 0x20, 0x2a, 0x20, + 0x68, 0x61, 0x6c, 0x66, 0x28, 0x36, 0x35, 0x35, 0x33, 0x36, 0x2e, 0x66, + 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x68, + 0x61, 0x6c, 0x66, 0x32, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x32, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, + 0x29, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x20, 0x6c, 0x6f, 0x61, 0x64, + 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x2a, 0x20, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x75, 0x6e, + 0x6f, 0x72, 0x6d, 0x32, 0x78, 0x31, 0x36, 0x5f, 0x74, 0x6f, 0x5f, 0x68, + 0x61, 0x6c, 0x66, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x3c, 0x68, 0x61, 0x6c, + 0x66, 0x32, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x3e, 0x3a, 0x3a, 0x6c, + 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, + 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, + 0x61, 0x6c, 0x66, 0x32, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x32, + 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x32, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x6c, 0x66, 0x5f, + 0x74, 0x6f, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x32, 0x78, 0x31, 0x36, + 0x28, 0x76, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, + 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x33, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, + 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x29, 0x29, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x20, 0x6c, 0x6f, 0x61, + 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2a, 0x20, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2c, 0x20, + 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, + 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x29, 0x20, 0x2f, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x36, 0x35, 0x35, + 0x33, 0x36, 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x68, 0x61, 0x6c, 0x66, - 0x33, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, - 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, - 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x66, 0x6f, - 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, - 0x6c, 0x66, 0x33, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, - 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, - 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x68, 0x61, 0x6c, 0x66, - 0x28, 0x32, 0x35, 0x35, 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, + 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x68, 0x61, + 0x6c, 0x66, 0x33, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2c, 0x20, + 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x2c, 0x20, 0x28, 0x76, 0x20, 0x2a, 0x20, 0x68, 0x61, 0x6c, 0x66, + 0x28, 0x36, 0x35, 0x35, 0x33, 0x36, 0x2e, 0x66, 0x29, 0x29, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, + 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x34, + 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x34, 0x3e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x68, 0x61, + 0x6c, 0x66, 0x34, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, + 0x6c, 0x66, 0x34, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, + 0x68, 0x61, 0x6c, 0x66, 0x34, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x34, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x68, + 0x61, 0x6c, 0x66, 0x28, 0x36, 0x35, 0x35, 0x33, 0x36, 0x2e, 0x66, 0x29, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, + 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, + 0x66, 0x34, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x20, 0x76, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, + 0x68, 0x61, 0x6c, 0x66, 0x34, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x34, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x28, 0x76, + 0x20, 0x2a, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x36, 0x35, 0x35, 0x33, + 0x36, 0x2e, 0x66, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, + 0x6d, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x2c, 0x20, 0x75, 0x63, 0x68, + 0x61, 0x72, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, + 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x29, 0x29, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6c, 0x6f, 0x61, + 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x2a, 0x20, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x2c, 0x20, + 0x75, 0x63, 0x68, 0x61, 0x72, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, + 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, + 0x2f, 0x20, 0x32, 0x35, 0x35, 0x2e, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, @@ -1866,43 +2401,507 @@ unsigned char ue4_stdlib_metal[] = { 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2a, 0x20, 0x73, + 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, - 0x68, 0x61, 0x6c, 0x66, 0x33, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x33, - 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, - 0x20, 0x6c, 0x2c, 0x20, 0x28, 0x76, 0x20, 0x2a, 0x20, 0x68, 0x61, 0x6c, - 0x66, 0x28, 0x32, 0x35, 0x35, 0x2e, 0x66, 0x29, 0x29, 0x29, 0x3b, 0x0a, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, + 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x3e, 0x3a, 0x3a, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x2c, 0x20, 0x28, 0x76, 0x20, 0x2a, 0x20, 0x32, 0x35, 0x35, 0x2e, + 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x66, + 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, + 0x32, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, + 0x29, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x6c, 0x6f, 0x61, + 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2a, 0x20, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, + 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x32, 0x3e, 0x3a, 0x3a, 0x6c, + 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x32, 0x35, 0x35, 0x2e, 0x66, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, + 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, + 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, + 0x32, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, + 0x20, 0x6c, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x76, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, + 0x72, 0x32, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x28, 0x76, + 0x20, 0x2a, 0x20, 0x32, 0x35, 0x35, 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, - 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2c, - 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x0a, 0x20, 0x20, + 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, + 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x68, 0x61, 0x6c, - 0x66, 0x33, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, - 0x66, 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, - 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x66, 0x6c, 0x6f, + 0x61, 0x74, 0x33, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2c, 0x20, 0x75, 0x63, 0x68, + 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, + 0x32, 0x35, 0x35, 0x2e, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, + 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2a, 0x20, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x66, + 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, + 0x33, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x28, 0x76, 0x20, 0x2a, 0x20, 0x32, 0x35, + 0x35, 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, + 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x2c, 0x20, 0x75, 0x63, 0x68, + 0x61, 0x72, 0x34, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x6c, + 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x2a, + 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x75, 0x6e, 0x70, 0x61, 0x63, + 0x6b, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x34, 0x78, 0x38, 0x5f, 0x74, + 0x6f, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x28, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, + 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, + 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x2a, 0x20, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, + 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x76, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, + 0x6b, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x70, 0x61, + 0x63, 0x6b, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x5f, 0x74, 0x6f, 0x5f, + 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x34, 0x78, 0x38, 0x28, 0x76, 0x29, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x66, 0x6c, 0x6f, 0x61, + 0x74, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x3e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x36, 0x35, + 0x35, 0x33, 0x36, 0x2e, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, + 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x2a, 0x20, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x2c, 0x20, + 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x2c, 0x20, 0x28, 0x76, 0x20, 0x2a, 0x20, 0x36, 0x35, 0x35, 0x33, 0x36, + 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x32, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x6c, + 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2a, + 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x75, 0x6e, 0x70, 0x61, 0x63, + 0x6b, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x32, 0x78, 0x31, 0x36, 0x5f, + 0x74, 0x6f, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x28, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, + 0x6b, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, + 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, + 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2a, + 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, + 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x76, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, + 0x63, 0x6b, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x70, + 0x61, 0x63, 0x6b, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x5f, 0x74, 0x6f, + 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x32, 0x78, 0x31, 0x36, 0x28, 0x76, + 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x33, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x33, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, + 0x29, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x6c, 0x6f, 0x61, + 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2a, 0x20, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, + 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x36, 0x35, 0x35, 0x33, 0x36, 0x2e, + 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, + 0x33, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x66, + 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2c, 0x20, 0x75, + 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x2c, 0x20, 0x28, 0x76, 0x20, 0x2a, 0x20, 0x36, 0x35, 0x35, 0x33, 0x36, + 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x34, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x6c, + 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x2a, + 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, + 0x74, 0x34, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x34, 0x3e, + 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x36, 0x35, 0x35, 0x33, + 0x36, 0x2e, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, + 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x66, 0x6c, 0x6f, + 0x61, 0x74, 0x34, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x2c, + 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x34, 0x3e, 0x3a, 0x3a, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x2c, 0x20, 0x28, 0x76, 0x20, 0x2a, 0x20, 0x36, 0x35, 0x35, + 0x33, 0x36, 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x09, 0x0a, 0x09, 0x2f, 0x2a, 0x0a, 0x09, 0x20, 0x09, 0x73, 0x6e, + 0x6f, 0x72, 0x6d, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x61, 0x72, + 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x61, 0x73, + 0x20, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x0a, 0x09, 0x20, 0x09, + 0x5b, 0x2d, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x5d, 0x20, + 0x74, 0x6f, 0x20, 0x5b, 0x2d, 0x4d, 0x41, 0x58, 0x2c, 0x20, 0x4d, 0x41, + 0x58, 0x5d, 0x0a, 0x09, 0x20, 0x09, 0x6e, 0x20, 0x3d, 0x20, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x62, 0x69, 0x74, 0x73, + 0x0a, 0x09, 0x20, 0x09, 0x4d, 0x41, 0x58, 0x20, 0x3d, 0x20, 0x32, 0x5e, + 0x28, 0x6e, 0x2d, 0x31, 0x29, 0x20, 0x2d, 0x20, 0x31, 0x0a, 0x09, 0x20, + 0x09, 0x30, 0x2e, 0x30, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x0a, 0x09, 0x20, + 0x0a, 0x09, 0x20, 0x09, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x20, 0x74, 0x6f, + 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x0a, 0x09, 0x20, 0x09, 0x69, + 0x6e, 0x74, 0x20, 0x63, 0x0a, 0x09, 0x20, 0x09, 0x66, 0x6c, 0x6f, 0x61, + 0x74, 0x28, 0x63, 0x29, 0x20, 0x2a, 0x20, 0x31, 0x2e, 0x30, 0x66, 0x20, + 0x2f, 0x20, 0x28, 0x32, 0x5e, 0x28, 0x6e, 0x2d, 0x31, 0x29, 0x20, 0x2d, + 0x20, 0x31, 0x2e, 0x30, 0x66, 0x29, 0x0a, 0x09, 0x20, 0x0a, 0x09, 0x20, + 0x09, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x6e, + 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x09, 0x20, 0x09, 0x66, 0x6c, 0x6f, 0x61, + 0x74, 0x20, 0x63, 0x0a, 0x09, 0x20, 0x09, 0x69, 0x6e, 0x74, 0x28, 0x63, + 0x20, 0x2a, 0x20, 0x28, 0x32, 0x5e, 0x28, 0x6e, 0x2d, 0x31, 0x29, 0x20, + 0x2d, 0x31, 0x2e, 0x30, 0x66, 0x29, 0x29, 0x0a, 0x09, 0x20, 0x2a, 0x2f, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2c, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x3e, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, + 0x72, 0x6d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, + 0x29, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6c, 0x6f, + 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2a, + 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x53, 0x72, 0x63, 0x54, + 0x79, 0x70, 0x65, 0x2c, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, + 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, + 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, + 0x65, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, + 0x20, 0x6c, 0x2c, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, + 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x3c, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x46, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x3e, 0x3a, 0x3a, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, + 0x6d, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, + 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, + 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, + 0x20, 0x68, 0x61, 0x6c, 0x66, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, + 0x68, 0x61, 0x6c, 0x66, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, + 0x68, 0x61, 0x6c, 0x66, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x3e, 0x3a, + 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, + 0x31, 0x32, 0x37, 0x2e, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, + 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x68, 0x61, 0x6c, + 0x66, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x2c, 0x20, 0x63, 0x68, 0x61, + 0x72, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x20, 0x2a, + 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x31, 0x32, 0x37, 0x2e, 0x66, 0x29, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x68, 0x61, 0x6c, + 0x66, 0x32, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x32, 0x3e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x68, 0x61, + 0x6c, 0x66, 0x32, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, + 0x6c, 0x66, 0x32, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, - 0x61, 0x6c, 0x66, 0x33, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, - 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, - 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x68, 0x61, - 0x6c, 0x66, 0x28, 0x36, 0x35, 0x35, 0x33, 0x36, 0x2e, 0x66, 0x29, 0x29, + 0x61, 0x6c, 0x66, 0x32, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x32, 0x3e, + 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x68, 0x61, 0x6c, 0x66, + 0x28, 0x31, 0x32, 0x37, 0x2e, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, + 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x2a, 0x20, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x68, + 0x61, 0x6c, 0x66, 0x32, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x2c, + 0x20, 0x63, 0x68, 0x61, 0x72, 0x32, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x2c, 0x20, 0x76, 0x20, 0x2a, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x31, + 0x32, 0x37, 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, + 0x6d, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2c, 0x20, 0x63, 0x68, 0x61, + 0x72, 0x33, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, + 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x29, 0x29, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x20, 0x6c, 0x6f, 0x61, + 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2a, 0x20, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2c, 0x20, 0x63, + 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, + 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x31, 0x32, 0x37, 0x2e, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, @@ -1918,79 +2917,80 @@ unsigned char ue4_stdlib_metal[] = { 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, - 0x61, 0x6c, 0x66, 0x33, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, - 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, - 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x28, 0x76, 0x20, - 0x2a, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x36, 0x35, 0x35, 0x33, 0x36, - 0x2e, 0x66, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, - 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2c, 0x20, 0x75, 0x63, 0x68, - 0x61, 0x72, 0x33, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x61, 0x6c, 0x66, 0x33, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, + 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x20, 0x2a, 0x20, 0x68, + 0x61, 0x6c, 0x66, 0x28, 0x31, 0x32, 0x37, 0x2e, 0x66, 0x29, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, + 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x34, + 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x34, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x68, 0x61, 0x6c, 0x66, + 0x34, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, + 0x34, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, + 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x75, 0x6e, 0x70, + 0x61, 0x63, 0x6b, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x34, 0x78, 0x38, + 0x5f, 0x74, 0x6f, 0x5f, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, + 0x6b, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, + 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x2a, 0x20, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, + 0x68, 0x61, 0x6c, 0x66, 0x34, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x3c, + 0x68, 0x61, 0x6c, 0x66, 0x34, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x3e, + 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x5f, + 0x68, 0x61, 0x6c, 0x66, 0x5f, 0x74, 0x6f, 0x5f, 0x73, 0x6e, 0x6f, 0x72, + 0x6d, 0x34, 0x78, 0x38, 0x28, 0x76, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, + 0x6f, 0x72, 0x6d, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x2c, 0x20, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x29, 0x29, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x6c, - 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2a, - 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, - 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, - 0x74, 0x33, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, - 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, - 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x32, 0x35, 0x35, 0x2e, 0x66, - 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, - 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, - 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, - 0x61, 0x74, 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, - 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2c, 0x20, 0x75, 0x63, - 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, - 0x28, 0x76, 0x20, 0x2a, 0x20, 0x32, 0x35, 0x35, 0x2e, 0x66, 0x29, 0x29, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, - 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x66, 0x6c, 0x6f, 0x61, - 0x74, 0x33, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, + 0x65, 0x29, 0x29, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x20, 0x6c, 0x6f, 0x61, + 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2c, 0x20, - 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, - 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, - 0x29, 0x20, 0x2f, 0x20, 0x36, 0x35, 0x35, 0x33, 0x36, 0x2e, 0x66, 0x29, + 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x2c, 0x20, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x68, + 0x61, 0x6c, 0x66, 0x28, 0x33, 0x32, 0x37, 0x36, 0x37, 0x2e, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, @@ -1998,110 +2998,89 @@ unsigned char ue4_stdlib_metal[] = { 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, - 0x74, 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, - 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, - 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2c, 0x20, 0x75, 0x73, 0x68, - 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, - 0x28, 0x76, 0x20, 0x2a, 0x20, 0x36, 0x35, 0x35, 0x33, 0x36, 0x2e, 0x66, - 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x09, 0x0a, 0x09, - 0x2f, 0x2a, 0x0a, 0x09, 0x20, 0x09, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x64, 0x65, - 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x6d, 0x61, 0x70, - 0x70, 0x69, 0x6e, 0x67, 0x0a, 0x09, 0x20, 0x09, 0x5b, 0x2d, 0x31, 0x2e, - 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x5d, 0x20, 0x74, 0x6f, 0x20, 0x5b, - 0x2d, 0x4d, 0x41, 0x58, 0x2c, 0x20, 0x4d, 0x41, 0x58, 0x5d, 0x0a, 0x09, - 0x20, 0x09, 0x6e, 0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, - 0x20, 0x6f, 0x66, 0x20, 0x62, 0x69, 0x74, 0x73, 0x0a, 0x09, 0x20, 0x09, - 0x4d, 0x41, 0x58, 0x20, 0x3d, 0x20, 0x32, 0x5e, 0x28, 0x6e, 0x2d, 0x31, - 0x29, 0x20, 0x2d, 0x20, 0x31, 0x0a, 0x09, 0x20, 0x09, 0x30, 0x2e, 0x30, - 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x0a, 0x09, 0x20, 0x0a, 0x09, 0x20, 0x09, - 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x6c, 0x6f, - 0x61, 0x74, 0x3a, 0x0a, 0x09, 0x20, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x63, - 0x0a, 0x09, 0x20, 0x09, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x28, 0x63, 0x29, - 0x20, 0x2a, 0x20, 0x31, 0x2e, 0x30, 0x66, 0x20, 0x2f, 0x20, 0x28, 0x32, - 0x5e, 0x28, 0x6e, 0x2d, 0x31, 0x29, 0x20, 0x2d, 0x20, 0x31, 0x2e, 0x30, - 0x66, 0x29, 0x0a, 0x09, 0x20, 0x0a, 0x09, 0x20, 0x09, 0x66, 0x6c, 0x6f, - 0x61, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, - 0x0a, 0x09, 0x20, 0x09, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x63, 0x0a, - 0x09, 0x20, 0x09, 0x69, 0x6e, 0x74, 0x28, 0x63, 0x20, 0x2a, 0x20, 0x28, - 0x32, 0x5e, 0x28, 0x6e, 0x2d, 0x31, 0x29, 0x20, 0x2d, 0x31, 0x2e, 0x30, - 0x66, 0x29, 0x29, 0x0a, 0x09, 0x20, 0x2a, 0x2f, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, - 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x74, 0x79, 0x70, - 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, - 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x53, 0x72, - 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, - 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, - 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2a, 0x20, 0x73, 0x72, 0x63, - 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, - 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x3c, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2c, - 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x3e, - 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, - 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, + 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, + 0x6c, 0x2c, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x20, 0x76, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, + 0x66, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x3e, 0x3a, 0x3a, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x20, 0x2a, 0x20, 0x68, 0x61, 0x6c, 0x66, + 0x28, 0x33, 0x32, 0x37, 0x36, 0x37, 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, + 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x2c, + 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, - 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x20, 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x2a, 0x20, 0x73, - 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, - 0x53, 0x72, 0x63, 0x54, 0x79, 0x70, 0x65, 0x20, 0x76, 0x29, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x53, 0x72, 0x63, - 0x54, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, - 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, - 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x68, 0x61, - 0x6c, 0x66, 0x33, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x68, 0x61, 0x6c, 0x66, + 0x32, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, + 0x32, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, + 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x75, 0x6e, 0x70, + 0x61, 0x63, 0x6b, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x32, 0x78, 0x31, + 0x36, 0x5f, 0x74, 0x6f, 0x5f, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, + 0x63, 0x6b, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, - 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x68, - 0x61, 0x6c, 0x66, 0x33, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, - 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, - 0x61, 0x6c, 0x66, 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, - 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x33, - 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, - 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x68, 0x61, 0x6c, - 0x66, 0x28, 0x31, 0x32, 0x37, 0x2e, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, + 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x2a, 0x20, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, + 0x20, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, + 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, + 0x5f, 0x68, 0x61, 0x6c, 0x66, 0x5f, 0x74, 0x6f, 0x5f, 0x73, 0x6e, 0x6f, + 0x72, 0x6d, 0x32, 0x78, 0x31, 0x36, 0x28, 0x76, 0x29, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, + 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2c, + 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x68, 0x61, 0x6c, 0x66, + 0x33, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, + 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, + 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, + 0x66, 0x33, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, + 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, + 0x33, 0x32, 0x37, 0x36, 0x37, 0x2e, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, @@ -2117,119 +3096,384 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x33, - 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, - 0x6c, 0x2c, 0x20, 0x76, 0x20, 0x2a, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, - 0x31, 0x32, 0x37, 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, - 0x72, 0x6d, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2c, 0x20, 0x73, 0x68, - 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, - 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x29, 0x29, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x20, 0x6c, - 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2a, 0x20, - 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, - 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2c, - 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, - 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, - 0x29, 0x20, 0x2f, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x33, 0x32, 0x37, - 0x36, 0x37, 0x2e, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, - 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, - 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x68, 0x61, 0x6c, - 0x66, 0x33, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x2c, 0x20, 0x73, - 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, - 0x20, 0x76, 0x20, 0x2a, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x33, 0x32, - 0x37, 0x36, 0x37, 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, - 0x72, 0x6d, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2c, 0x20, 0x63, - 0x68, 0x61, 0x72, 0x33, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, - 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x29, 0x29, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, - 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, - 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, - 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, - 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, - 0x74, 0x33, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, - 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, - 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x31, 0x32, 0x37, 0x2e, 0x66, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, - 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, - 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, - 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, - 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x20, 0x2a, 0x20, 0x68, 0x61, 0x6c, 0x66, + 0x28, 0x33, 0x32, 0x37, 0x36, 0x37, 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, + 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x2c, + 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x34, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x68, 0x61, 0x6c, 0x66, + 0x34, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, + 0x34, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, - 0x20, 0x6c, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x76, - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, - 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, - 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, - 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x20, 0x2a, - 0x20, 0x31, 0x32, 0x37, 0x2e, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, - 0x72, 0x6d, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2c, 0x20, 0x73, - 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, + 0x66, 0x34, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x34, 0x3e, 0x3a, + 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, + 0x33, 0x32, 0x37, 0x36, 0x37, 0x2e, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, + 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x2a, 0x20, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, + 0x68, 0x61, 0x6c, 0x66, 0x34, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x34, + 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x34, 0x3e, 0x3a, 0x3a, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x20, 0x2a, 0x20, 0x68, 0x61, 0x6c, 0x66, + 0x28, 0x33, 0x32, 0x37, 0x36, 0x37, 0x2e, 0x66, 0x29, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, + 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x2c, + 0x20, 0x63, 0x68, 0x61, 0x72, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x2a, + 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, + 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, + 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, + 0x20, 0x2f, 0x20, 0x31, 0x32, 0x37, 0x2e, 0x66, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, + 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x2a, 0x20, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, + 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x2c, 0x20, 0x76, 0x20, 0x2a, 0x20, 0x31, 0x32, 0x37, 0x2e, 0x66, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x66, 0x6c, 0x6f, 0x61, + 0x74, 0x32, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x32, 0x3e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x32, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, + 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2c, 0x20, 0x63, 0x68, 0x61, + 0x72, 0x32, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x31, + 0x32, 0x37, 0x2e, 0x66, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, + 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x66, 0x6c, 0x6f, + 0x61, 0x74, 0x32, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2c, + 0x20, 0x63, 0x68, 0x61, 0x72, 0x32, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x2c, 0x20, 0x76, 0x20, 0x2a, 0x20, 0x31, 0x32, 0x37, 0x2e, 0x66, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x66, 0x6c, 0x6f, 0x61, + 0x74, 0x33, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x33, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, + 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2c, 0x20, 0x63, 0x68, 0x61, + 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x31, + 0x32, 0x37, 0x2e, 0x66, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, + 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x66, 0x6c, 0x6f, + 0x61, 0x74, 0x33, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2c, + 0x20, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x2c, 0x20, 0x76, 0x20, 0x2a, 0x20, 0x31, 0x32, 0x37, 0x2e, 0x66, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x66, 0x6c, 0x6f, 0x61, + 0x74, 0x34, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x34, 0x3e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x34, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, + 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x73, 0x6e, 0x6f, 0x72, + 0x6d, 0x34, 0x78, 0x38, 0x5f, 0x74, 0x6f, 0x5f, 0x66, 0x6c, 0x6f, 0x61, + 0x74, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, + 0x34, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, + 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x34, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, + 0x34, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x3c, 0x66, 0x6c, 0x6f, 0x61, + 0x74, 0x34, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x3e, 0x3a, 0x3a, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x66, 0x6c, 0x6f, + 0x61, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x34, + 0x78, 0x38, 0x28, 0x76, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, + 0x6d, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x2c, 0x20, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, + 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x29, 0x29, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6c, 0x6f, 0x61, + 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x2a, 0x20, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x2c, 0x20, 0x73, + 0x68, 0x6f, 0x72, 0x74, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, + 0x20, 0x33, 0x32, 0x37, 0x36, 0x37, 0x2e, 0x66, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, + 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x2a, 0x20, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, + 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x3e, 0x3a, 0x3a, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x2c, 0x20, 0x76, 0x20, 0x2a, 0x20, 0x33, 0x32, 0x37, 0x36, 0x37, + 0x2e, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x66, + 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x32, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, + 0x29, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x6c, 0x6f, 0x61, + 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2a, 0x20, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x5f, + 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x32, 0x78, 0x31, 0x36, 0x5f, 0x74, 0x6f, + 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x3c, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, + 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2a, 0x20, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, + 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x70, 0x61, 0x63, + 0x6b, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x73, + 0x6e, 0x6f, 0x72, 0x6d, 0x32, 0x78, 0x31, 0x36, 0x28, 0x76, 0x29, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x66, 0x6c, 0x6f, 0x61, + 0x74, 0x33, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, + 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x66, + 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2c, 0x20, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, + 0x20, 0x33, 0x32, 0x37, 0x36, 0x37, 0x2e, 0x66, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, + 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2a, 0x20, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, + 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x76, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, + 0x61, 0x74, 0x33, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, + 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x20, 0x2a, 0x20, 0x33, + 0x32, 0x37, 0x36, 0x37, 0x2e, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, + 0x72, 0x6d, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x2c, 0x20, 0x73, + 0x68, 0x6f, 0x72, 0x74, 0x34, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, + 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, - 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x34, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, - 0x61, 0x74, 0x33, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, + 0x61, 0x74, 0x34, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x34, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x20, 0x2f, 0x20, 0x33, 0x32, 0x37, 0x36, 0x37, 0x2e, 0x66, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, @@ -2240,15 +3484,15 @@ unsigned char ue4_stdlib_metal[] = { 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x66, - 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, - 0x74, 0x33, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x34, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x2c, 0x20, - 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, + 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x2c, 0x20, + 0x73, 0x68, 0x6f, 0x72, 0x74, 0x34, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x20, 0x2a, 0x20, 0x33, 0x32, 0x37, 0x36, 0x37, 0x2e, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, @@ -3111,340 +4355,288 @@ unsigned char ue4_stdlib_metal[] = { 0x35, 0x28, 0x76, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, + 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, - 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x63, - 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x66, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, - 0x29, 0x20, 0x7b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x3e, 0x20, 0x55, 0x6e, 0x6b, 0x6e, - 0x6f, 0x77, 0x6e, 0x20, 0x3f, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x20, 0x3a, 0x20, 0x74, 0x5b, 0x28, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x20, - 0x2a, 0x20, 0x32, 0x29, 0x20, 0x2b, 0x20, 0x31, 0x5d, 0x3b, 0x20, 0x7d, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, - 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x6e, 0x64, 0x65, - 0x78, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x43, 0x6f, 0x6d, 0x70, - 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, - 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, - 0x29, 0x20, 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, - 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x54, - 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, - 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x54, 0x3e, 0x3a, 0x3a, 0x6c, - 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, - 0x6c, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, - 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, - 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x20, 0x76, - 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x20, 0x54, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, - 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, - 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, - 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x54, - 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, - 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, - 0x09, 0x09, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3e, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, - 0x31, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, - 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, - 0x29, 0x20, 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, - 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x54, - 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x3c, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3e, 0x3a, 0x3a, 0x66, 0x6f, - 0x72, 0x6d, 0x61, 0x74, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, - 0x20, 0x74, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, - 0x61, 0x73, 0x65, 0x20, 0x52, 0x31, 0x36, 0x55, 0x69, 0x6e, 0x74, 0x3a, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, - 0x74, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, - 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x33, 0x32, 0x55, 0x69, 0x6e, - 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, - 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, - 0x6e, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, - 0x4d, 0x61, 0x78, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x66, - 0x61, 0x75, 0x6c, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, - 0x54, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, - 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, - 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x20, - 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x54, 0x2a, 0x20, 0x73, 0x72, 0x63, - 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, - 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x54, 0x20, 0x76, - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x3c, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3e, 0x3a, 0x3a, 0x66, 0x6f, - 0x72, 0x6d, 0x61, 0x74, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, - 0x20, 0x74, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, - 0x61, 0x73, 0x65, 0x20, 0x52, 0x31, 0x36, 0x55, 0x69, 0x6e, 0x74, 0x3a, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, - 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, - 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x33, 0x32, 0x55, 0x69, 0x6e, - 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, - 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, - 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, - 0x6e, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, - 0x4d, 0x61, 0x78, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x66, - 0x61, 0x75, 0x6c, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x54, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, - 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, - 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3e, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3c, 0x54, - 0x2c, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x33, 0x3e, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, - 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x20, + 0x74, 0x79, 0x70, 0x65, 0x5f, 0x72, 0x67, 0x62, 0x31, 0x30, 0x61, 0x32, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, + 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x54, 0x2a, 0x20, 0x73, - 0x72, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x54, 0x28, 0x75, 0x6e, 0x70, 0x61, 0x63, + 0x6b, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x31, 0x30, 0x61, 0x32, 0x5f, + 0x74, 0x6f, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x28, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, + 0x6b, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x3e, 0x3a, 0x3a, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, + 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, + 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x20, 0x54, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, + 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x3e, 0x3a, 0x3a, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x66, 0x6c, 0x6f, + 0x61, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x31, + 0x30, 0x61, 0x32, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x28, 0x76, + 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x5f, 0x72, 0x67, 0x62, 0x31, 0x30, 0x61, 0x32, + 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, + 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, + 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x34, + 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x34, + 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, + 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x75, 0x6e, 0x70, 0x61, + 0x63, 0x6b, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x31, 0x30, 0x61, 0x32, + 0x5f, 0x74, 0x6f, 0x5f, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, + 0x6b, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, + 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x2a, 0x20, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, + 0x68, 0x61, 0x6c, 0x66, 0x34, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x3c, + 0x68, 0x61, 0x6c, 0x66, 0x34, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x3e, + 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x5f, + 0x68, 0x61, 0x6c, 0x66, 0x5f, 0x74, 0x6f, 0x5f, 0x75, 0x6e, 0x6f, 0x72, + 0x6d, 0x31, 0x30, 0x61, 0x32, 0x28, 0x76, 0x29, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3e, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x61, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, + 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x29, 0x29, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x26, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x26, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x63, - 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x77, 0x69, - 0x74, 0x63, 0x68, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3c, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x3e, 0x3a, 0x3a, 0x66, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x74, 0x29, - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, - 0x20, 0x52, 0x47, 0x42, 0x31, 0x36, 0x48, 0x61, 0x6c, 0x66, 0x3a, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, - 0x5f, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, - 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, - 0x47, 0x42, 0x33, 0x32, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, - 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, - 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, - 0x47, 0x31, 0x31, 0x42, 0x31, 0x30, 0x48, 0x61, 0x6c, 0x66, 0x3a, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x5f, 0x72, 0x67, 0x31, 0x31, 0x62, 0x31, 0x30, 0x3c, 0x54, + 0x74, 0x2a, 0x20, 0x74, 0x29, 0x20, 0x7b, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x3e, 0x20, + 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x3f, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x20, 0x3a, 0x20, 0x74, 0x5b, 0x28, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x20, 0x2a, 0x20, 0x32, 0x29, 0x20, 0x2b, 0x20, 0x31, + 0x5d, 0x3b, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, + 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, + 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x54, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, + 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x54, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, - 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, + 0x09, 0x09, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, + 0x29, 0x20, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x54, 0x2a, 0x20, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x26, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, + 0x54, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, + 0x54, 0x2c, 0x20, 0x54, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, + 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, + 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x2c, 0x20, 0x31, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, + 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, + 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x54, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x28, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x3c, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3e, + 0x3a, 0x3a, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x74, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x2f, + 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x38, 0x53, 0x69, + 0x6e, 0x74, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x35, 0x47, 0x36, 0x42, 0x35, 0x55, - 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x63, + 0x68, 0x61, 0x72, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x29, 0x3b, 0x0a, + 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x38, 0x53, + 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x72, 0x35, 0x67, - 0x36, 0x62, 0x35, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, - 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, - 0x42, 0x38, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, - 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, - 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, - 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x38, 0x55, - 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, - 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x75, 0x63, 0x68, 0x61, 0x72, 0x33, - 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, - 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x38, 0x53, 0x6e, 0x6f, - 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, - 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, - 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, - 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, - 0x38, 0x55, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, - 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, - 0x65, 0x64, 0x5f, 0x75, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, - 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, - 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, - 0x65, 0x20, 0x52, 0x47, 0x42, 0x31, 0x36, 0x53, 0x69, 0x6e, 0x74, 0x3a, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, - 0x64, 0x5f, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, - 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, - 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, - 0x20, 0x52, 0x47, 0x42, 0x31, 0x36, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, - 0x5f, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, - 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, - 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, - 0x20, 0x52, 0x47, 0x42, 0x31, 0x36, 0x53, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, + 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x3e, 0x3a, + 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, + 0x73, 0x65, 0x20, 0x52, 0x31, 0x36, 0x53, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, + 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, - 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x68, 0x6f, 0x72, - 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, - 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, + 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, + 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, + 0x31, 0x36, 0x48, 0x61, 0x6c, 0x66, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x31, 0x36, - 0x55, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, + 0x54, 0x2c, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, + 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x29, 0x29, 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, + 0x20, 0x52, 0x33, 0x32, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, - 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, - 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, - 0x64, 0x5f, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, - 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, - 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x3e, 0x3a, 0x3a, 0x6c, + 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, - 0x65, 0x20, 0x52, 0x47, 0x42, 0x33, 0x32, 0x53, 0x69, 0x6e, 0x74, 0x3a, + 0x65, 0x20, 0x52, 0x38, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x3e, 0x3a, + 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x61, 0x73, 0x65, 0x20, 0x52, 0x38, 0x55, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, - 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, + 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, + 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, - 0x47, 0x42, 0x33, 0x32, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, + 0x31, 0x36, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x75, - 0x69, 0x6e, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, - 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, + 0x54, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x3e, 0x3a, 0x3a, 0x6c, + 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, + 0x65, 0x20, 0x52, 0x31, 0x36, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x31, 0x36, 0x55, 0x6e, 0x6f, + 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, + 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x3e, 0x3a, + 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, + 0x73, 0x65, 0x20, 0x52, 0x33, 0x32, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x3e, + 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x33, 0x32, 0x46, 0x6c, 0x6f, 0x61, + 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, @@ -3455,8 +4647,312 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, - 0x54, 0x2c, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, - 0x65, 0x64, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, + 0x54, 0x2c, 0x20, 0x54, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, + 0x29, 0x20, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x54, 0x2a, 0x20, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x26, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, + 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x28, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x61, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x3c, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3e, 0x3a, + 0x3a, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x2c, 0x20, 0x74, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x2f, 0x2f, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x38, 0x53, 0x69, 0x6e, + 0x74, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, + 0x54, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x3e, 0x3a, 0x3a, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x29, 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x2f, + 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x38, 0x53, 0x6e, + 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x63, 0x68, + 0x61, 0x72, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, + 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, + 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, + 0x65, 0x20, 0x52, 0x31, 0x36, 0x53, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, + 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, + 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x3e, 0x3a, + 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x2f, 0x2f, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, + 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x31, + 0x36, 0x48, 0x61, 0x6c, 0x66, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x3e, + 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x29, 0x3b, 0x0a, + 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, + 0x6b, 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, + 0x52, 0x33, 0x32, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x69, 0x6e, 0x74, + 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x29, 0x3b, + 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, + 0x20, 0x52, 0x38, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x63, 0x68, + 0x61, 0x72, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, + 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, + 0x73, 0x65, 0x20, 0x52, 0x38, 0x55, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, + 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x3e, 0x3a, + 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x31, + 0x36, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, + 0x20, 0x52, 0x31, 0x36, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x73, + 0x68, 0x6f, 0x72, 0x74, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, + 0x76, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x31, 0x36, 0x55, 0x6e, 0x6f, 0x72, + 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, + 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, + 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, + 0x65, 0x20, 0x52, 0x33, 0x32, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, + 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x61, 0x73, 0x65, 0x20, 0x52, 0x33, 0x32, 0x46, 0x6c, 0x6f, 0x61, 0x74, + 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, + 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3e, 0x3a, 0x3a, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x55, 0x6e, 0x6b, 0x6e, + 0x6f, 0x77, 0x6e, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, + 0x65, 0x20, 0x4d, 0x61, 0x78, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x54, 0x3e, 0x3a, 0x3a, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, + 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x32, + 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, + 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, + 0x20, 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x54, 0x2a, + 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x26, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x77, 0x69, 0x74, 0x63, 0x68, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x3c, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3e, 0x3a, 0x3a, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, + 0x74, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, + 0x73, 0x65, 0x20, 0x52, 0x47, 0x38, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, + 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x32, 0x3e, + 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x38, 0x55, 0x69, 0x6e, 0x74, 0x3a, + 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, + 0x32, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x2f, 0x2f, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x38, 0x53, 0x6e, 0x6f, + 0x72, 0x6d, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, + 0x3c, 0x54, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x32, 0x3e, 0x3a, 0x3a, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, + 0x65, 0x20, 0x52, 0x47, 0x31, 0x36, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, + 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, + 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x31, 0x36, 0x53, 0x6e, 0x6f, + 0x72, 0x6d, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, + 0x3c, 0x54, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x3e, 0x3a, + 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, + 0x73, 0x65, 0x20, 0x52, 0x47, 0x33, 0x32, 0x53, 0x69, 0x6e, 0x74, 0x3a, + 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x32, 0x3e, + 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x33, 0x32, 0x55, 0x69, 0x6e, 0x74, + 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x32, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x38, 0x55, 0x6e, 0x6f, + 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, + 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x32, 0x3e, 0x3a, + 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, + 0x73, 0x65, 0x20, 0x52, 0x47, 0x31, 0x36, 0x55, 0x69, 0x6e, 0x74, 0x3a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x32, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x31, 0x36, 0x55, + 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, + 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x32, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x31, 0x36, 0x48, 0x61, + 0x6c, 0x66, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x68, 0x61, + 0x6c, 0x66, 0x32, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x33, 0x32, + 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, + 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, + 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, + 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x3a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x4d, 0x61, 0x78, 0x3a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x3a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x54, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, @@ -3482,133 +4978,442 @@ unsigned char ue4_stdlib_metal[] = { 0x78, 0x3e, 0x3a, 0x3a, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x74, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, - 0x42, 0x31, 0x36, 0x48, 0x61, 0x6c, 0x66, 0x3a, 0x0a, 0x20, 0x20, 0x20, + 0x38, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, - 0x65, 0x64, 0x5f, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x3e, 0x3a, 0x3a, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, - 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x33, - 0x32, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x32, 0x3e, + 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x2f, + 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, + 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, + 0x47, 0x38, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, - 0x64, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, - 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, + 0x32, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, + 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, + 0x20, 0x52, 0x47, 0x38, 0x53, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x2f, + 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, + 0x3c, 0x54, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x32, 0x3e, 0x3a, 0x3a, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, + 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x31, + 0x36, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x31, 0x31, - 0x42, 0x31, 0x30, 0x48, 0x61, 0x6c, 0x66, 0x3a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, + 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, + 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, + 0x6b, 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, + 0x52, 0x47, 0x31, 0x36, 0x53, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x2f, + 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, + 0x3c, 0x54, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x3e, 0x3a, + 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x2f, 0x2f, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, + 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, + 0x33, 0x32, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x32, 0x3e, + 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, + 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x2f, + 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, + 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, + 0x47, 0x33, 0x32, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x5f, 0x72, 0x67, 0x31, 0x31, 0x62, 0x31, 0x30, - 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, + 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x32, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, + 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, + 0x20, 0x52, 0x47, 0x38, 0x55, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, + 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x32, 0x3e, 0x3a, + 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, + 0x31, 0x36, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x32, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, - 0x65, 0x20, 0x52, 0x35, 0x47, 0x36, 0x42, 0x35, 0x55, 0x6e, 0x6f, 0x72, - 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x72, - 0x35, 0x67, 0x36, 0x62, 0x35, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x73, 0x74, + 0x65, 0x20, 0x52, 0x47, 0x31, 0x36, 0x55, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, + 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x32, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, + 0x20, 0x52, 0x47, 0x31, 0x36, 0x48, 0x61, 0x6c, 0x66, 0x3a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x68, 0x61, + 0x6c, 0x66, 0x32, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, + 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, + 0x73, 0x65, 0x20, 0x52, 0x47, 0x33, 0x32, 0x46, 0x6c, 0x6f, 0x61, 0x74, + 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, + 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x38, 0x53, - 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, + 0x77, 0x6e, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, + 0x20, 0x4d, 0x61, 0x78, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, - 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, - 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x54, 0x3e, 0x3a, 0x3a, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, - 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x38, 0x55, 0x69, 0x6e, 0x74, - 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, - 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, - 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x75, 0x63, 0x68, 0x61, - 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, - 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, - 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, + 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3c, + 0x54, 0x2c, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x33, 0x3e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, + 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, + 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x54, 0x2a, 0x20, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x26, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x77, + 0x69, 0x74, 0x63, 0x68, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3c, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3e, 0x3a, 0x3a, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x74, + 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, - 0x65, 0x20, 0x52, 0x47, 0x42, 0x38, 0x53, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, + 0x65, 0x20, 0x52, 0x47, 0x42, 0x31, 0x36, 0x48, 0x61, 0x6c, 0x66, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, - 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, - 0x5f, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, - 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, + 0x64, 0x5f, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, + 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, + 0x52, 0x47, 0x42, 0x33, 0x32, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, + 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, + 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, + 0x52, 0x47, 0x31, 0x31, 0x42, 0x31, 0x30, 0x48, 0x61, 0x6c, 0x66, 0x3a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x5f, 0x72, 0x67, 0x31, 0x31, 0x62, 0x31, 0x30, 0x3c, + 0x54, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x38, 0x55, 0x6e, + 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x35, 0x47, 0x36, 0x42, 0x35, + 0x55, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x72, 0x35, + 0x67, 0x36, 0x62, 0x35, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, + 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, + 0x47, 0x42, 0x38, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, + 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, 0x68, + 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x38, + 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, + 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x75, 0x63, 0x68, 0x61, 0x72, + 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x38, 0x53, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, + 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, + 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, + 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, + 0x42, 0x38, 0x55, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, + 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, + 0x6b, 0x65, 0x64, 0x5f, 0x75, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, + 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, + 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x31, 0x36, 0x53, 0x69, 0x6e, 0x74, + 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, + 0x65, 0x64, 0x5f, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, + 0x65, 0x20, 0x52, 0x47, 0x42, 0x31, 0x36, 0x55, 0x69, 0x6e, 0x74, 0x3a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, + 0x64, 0x5f, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, + 0x65, 0x20, 0x52, 0x47, 0x42, 0x31, 0x36, 0x53, 0x6e, 0x6f, 0x72, 0x6d, + 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, + 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x31, + 0x36, 0x55, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, + 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, + 0x65, 0x64, 0x5f, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, + 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, + 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x33, 0x32, 0x53, 0x69, 0x6e, 0x74, + 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, + 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, + 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, + 0x52, 0x47, 0x42, 0x33, 0x32, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, + 0x75, 0x69, 0x6e, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, + 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x55, 0x6e, + 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x61, 0x73, 0x65, 0x20, 0x4d, 0x61, 0x78, 0x3a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x3a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, - 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x75, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, + 0x3c, 0x54, 0x2c, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x70, 0x61, 0x63, + 0x6b, 0x65, 0x64, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x3e, 0x3a, 0x3a, 0x6c, + 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, + 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x20, 0x54, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, + 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, + 0x20, 0x6c, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x77, 0x69, 0x74, 0x63, + 0x68, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3c, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x3e, 0x3a, 0x3a, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x74, 0x29, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, + 0x47, 0x42, 0x31, 0x36, 0x48, 0x61, 0x6c, 0x66, 0x3a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, + 0x6b, 0x65, 0x64, 0x5f, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x3e, 0x3a, 0x3a, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, + 0x33, 0x32, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, + 0x65, 0x64, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x3e, 0x3a, 0x3a, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x31, + 0x31, 0x42, 0x31, 0x30, 0x48, 0x61, 0x6c, 0x66, 0x3a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x72, 0x67, 0x31, 0x31, 0x62, 0x31, + 0x30, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, + 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, + 0x73, 0x65, 0x20, 0x52, 0x35, 0x47, 0x36, 0x42, 0x35, 0x55, 0x6e, 0x6f, + 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, + 0x72, 0x35, 0x67, 0x36, 0x62, 0x35, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x38, + 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, + 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, + 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x38, 0x55, 0x69, 0x6e, + 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, + 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x75, 0x63, 0x68, + 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, + 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, + 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x38, 0x53, 0x6e, 0x6f, 0x72, 0x6d, + 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, + 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, + 0x64, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x38, 0x55, + 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x70, + 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x75, 0x63, 0x68, 0x61, 0x72, 0x33, + 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, + 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, + 0x52, 0x47, 0x42, 0x31, 0x36, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, + 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, - 0x47, 0x42, 0x31, 0x36, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, + 0x47, 0x42, 0x31, 0x36, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, - 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, - 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, - 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, - 0x42, 0x31, 0x36, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, - 0x65, 0x64, 0x5f, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, - 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, - 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, - 0x42, 0x31, 0x36, 0x53, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, - 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x73, 0x68, - 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, - 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, - 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x31, 0x36, 0x55, 0x6e, 0x6f, - 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, - 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, @@ -3616,293 +5421,645 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, - 0x47, 0x42, 0x33, 0x32, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, + 0x47, 0x42, 0x31, 0x36, 0x53, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, - 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, - 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, + 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x73, + 0x68, 0x6f, 0x72, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, + 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, + 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x33, - 0x32, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x31, 0x36, 0x55, 0x6e, + 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, - 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, - 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, + 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x33, + 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, - 0x6e, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, + 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, - 0x4d, 0x61, 0x78, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x66, - 0x61, 0x75, 0x6c, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x52, 0x47, 0x42, 0x33, 0x32, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x70, 0x61, - 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x3e, 0x3a, 0x3a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, + 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3e, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x54, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x6c, 0x6f, 0x61, - 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x63, 0x6f, 0x6e, - 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x54, 0x2a, - 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x26, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, - 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, - 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3c, - 0x54, 0x2c, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x74, 0x79, - 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x3c, 0x54, 0x3e, - 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, - 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, - 0x73, 0x72, 0x63, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x66, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, - 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3e, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, - 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x54, 0x2a, 0x20, 0x70, 0x2c, 0x20, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, - 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x66, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, - 0x73, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, - 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x28, 0x70, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, - 0x2c, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x6c, 0x2c, - 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x0a, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, - 0x3e, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x48, 0x61, 0x6c, - 0x66, 0x32, 0x78, 0x31, 0x36, 0x28, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, - 0x20, 0x32, 0x3e, 0x20, 0x68, 0x29, 0x0a, 0x09, 0x7b, 0x0a, 0x23, 0x69, - 0x66, 0x20, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x52, 0x55, 0x4e, 0x54, - 0x49, 0x4d, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x49, 0x4c, 0x45, 0x52, - 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, - 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3c, 0x3d, - 0x20, 0x31, 0x32, 0x30, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x61, 0x73, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x3c, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x3e, 0x28, - 0x68, 0x61, 0x6c, 0x66, 0x28, 0x68, 0x2e, 0x78, 0x29, 0x29, 0x29, 0x20, - 0x7c, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x61, 0x73, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x3c, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x3e, 0x28, - 0x68, 0x61, 0x6c, 0x66, 0x28, 0x68, 0x2e, 0x79, 0x29, 0x29, 0x29, 0x20, - 0x3c, 0x3c, 0x20, 0x31, 0x36, 0x75, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, - 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x20, 0x61, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x75, 0x69, 0x6e, - 0x74, 0x3e, 0x28, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x28, 0x68, 0x29, 0x29, - 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x7d, 0x0a, - 0x09, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x75, 0x6e, 0x70, 0x61, 0x63, - 0x6b, 0x48, 0x61, 0x6c, 0x66, 0x32, 0x78, 0x31, 0x36, 0x28, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x69, 0x29, 0x0a, 0x09, 0x7b, 0x0a, 0x23, 0x69, 0x66, - 0x20, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x52, 0x55, 0x4e, 0x54, 0x49, - 0x4d, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x49, 0x4c, 0x45, 0x52, 0x20, - 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, - 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3c, 0x3d, 0x20, - 0x31, 0x32, 0x30, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x28, 0x61, 0x73, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x3e, 0x28, 0x75, 0x73, - 0x68, 0x6f, 0x72, 0x74, 0x28, 0x69, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, - 0x66, 0x66, 0x66, 0x29, 0x29, 0x2c, 0x20, 0x61, 0x73, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x3e, 0x28, 0x75, 0x73, 0x68, - 0x6f, 0x72, 0x74, 0x28, 0x28, 0x69, 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x36, - 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x66, 0x66, 0x66, 0x29, 0x29, - 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x09, 0x72, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, - 0x28, 0x61, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, - 0x66, 0x32, 0x3e, 0x28, 0x69, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, - 0x64, 0x69, 0x66, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x23, 0x70, - 0x72, 0x61, 0x67, 0x6d, 0x61, 0x20, 0x6d, 0x61, 0x72, 0x6b, 0x20, 0x2d, - 0x2d, 0x20, 0x54, 0x79, 0x70, 0x65, 0x64, 0x20, 0x42, 0x75, 0x66, 0x66, - 0x65, 0x72, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x20, 0x2d, 0x2d, - 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, - 0x75, 0x65, 0x34, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, - 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, - 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x72, 0x69, - 0x65, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x62, - 0x75, 0x66, 0x66, 0x65, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, - 0x66, 0x65, 0x72, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, - 0x75, 0x66, 0x66, 0x65, 0x72, 0x26, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, - 0x29, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x64, - 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x69, - 0x76, 0x61, 0x74, 0x65, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x54, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x09, 0x0a, 0x09, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x74, 0x79, 0x70, - 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x69, 0x6e, - 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x20, - 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, - 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x6d, 0x65, 0x6d, 0x6f, - 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3e, 0x20, 0x66, 0x72, - 0x69, 0x65, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, - 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, - 0x63, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, - 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, - 0x72, 0x26, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x29, 0x20, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, - 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, - 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, - 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x29, 0x29, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x61, 0x64, - 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, - 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x69, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, - 0x6d, 0x69, 0x63, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x65, 0x78, 0x70, - 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, - 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x2a, - 0x29, 0x70, 0x29, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, - 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, - 0x61, 0x78, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, - 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, - 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, - 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, - 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x69, 0x6e, 0x74, 0x20, - 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, + 0x33, 0x32, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, + 0x64, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x3e, 0x3a, 0x3a, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, + 0x77, 0x6e, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, + 0x20, 0x4d, 0x61, 0x78, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x70, + 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x3e, 0x3a, + 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, - 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, - 0x5f, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, 0x29, 0x5b, 0x69, 0x5d, 0x2c, - 0x20, 0x76, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, - 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x78, 0x65, 0x64, - 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x2c, + 0x20, 0x34, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x29, 0x29, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, + 0x29, 0x29, 0x20, 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, + 0x54, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, + 0x6c, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x28, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x3c, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3e, 0x3a, 0x3a, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x2c, 0x20, 0x74, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x41, 0x38, 0x53, 0x69, + 0x6e, 0x74, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x63, 0x68, + 0x61, 0x72, 0x34, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x2f, + 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x41, + 0x33, 0x32, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, + 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x34, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, + 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, + 0x47, 0x42, 0x41, 0x38, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x34, 0x3e, 0x3a, + 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, + 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x41, 0x38, 0x53, 0x6e, 0x6f, 0x72, + 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, + 0x54, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x34, 0x3e, 0x3a, 0x3a, 0x6c, + 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, + 0x20, 0x52, 0x47, 0x42, 0x41, 0x38, 0x55, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, + 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x34, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, + 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, + 0x42, 0x47, 0x52, 0x41, 0x38, 0x55, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, + 0x75, 0x63, 0x68, 0x61, 0x72, 0x34, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, + 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, + 0x2e, 0x7a, 0x79, 0x78, 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x41, 0x31, 0x36, 0x53, 0x69, + 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x34, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, + 0x41, 0x31, 0x36, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, + 0x54, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x34, 0x3e, 0x3a, + 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, + 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x41, 0x31, 0x36, 0x53, 0x6e, 0x6f, + 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, + 0x3c, 0x54, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x34, 0x3e, 0x3a, + 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, + 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x41, 0x31, 0x36, 0x55, 0x6e, 0x6f, + 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, + 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x34, 0x3e, + 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x41, 0x31, 0x36, 0x48, 0x61, + 0x6c, 0x66, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x68, 0x61, + 0x6c, 0x66, 0x34, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x41, + 0x33, 0x32, 0x55, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, + 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, + 0x52, 0x47, 0x42, 0x41, 0x33, 0x32, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x3a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, + 0x34, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x31, 0x30, 0x41, + 0x32, 0x55, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x72, + 0x67, 0x62, 0x31, 0x30, 0x61, 0x32, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6c, + 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, + 0x20, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x3a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x4d, 0x61, 0x78, 0x3a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x3a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x54, 0x3e, 0x3a, 0x3a, 0x6c, + 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, + 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x20, 0x54, 0x2a, 0x20, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, + 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, + 0x20, 0x6c, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x77, 0x69, 0x74, 0x63, + 0x68, 0x28, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3c, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x3e, 0x3a, 0x3a, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x74, 0x29, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, + 0x47, 0x42, 0x41, 0x38, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x2f, 0x2f, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x63, 0x68, 0x61, + 0x72, 0x34, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, + 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, + 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, + 0x65, 0x20, 0x52, 0x47, 0x42, 0x41, 0x33, 0x32, 0x53, 0x69, 0x6e, 0x74, + 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, + 0x20, 0x69, 0x6e, 0x74, 0x34, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, + 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x41, 0x38, 0x55, 0x69, + 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, + 0x54, 0x2c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x34, 0x3e, 0x3a, 0x3a, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, + 0x41, 0x38, 0x53, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, + 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x34, 0x3e, 0x3a, 0x3a, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x41, 0x38, + 0x55, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, + 0x75, 0x63, 0x68, 0x61, 0x72, 0x34, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x42, 0x47, 0x52, 0x41, 0x38, 0x55, + 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, 0x54, 0x2c, 0x20, 0x75, + 0x63, 0x68, 0x61, 0x72, 0x34, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, + 0x20, 0x76, 0x2e, 0x7a, 0x79, 0x78, 0x77, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, + 0x42, 0x41, 0x31, 0x36, 0x53, 0x69, 0x6e, 0x74, 0x3a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x34, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, + 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, + 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x41, 0x31, 0x36, 0x55, 0x69, 0x6e, + 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, + 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x34, 0x3e, 0x3a, 0x3a, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, + 0x41, 0x31, 0x36, 0x53, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, + 0x54, 0x2c, 0x20, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x34, 0x3e, 0x3a, 0x3a, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, + 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, + 0x41, 0x31, 0x36, 0x55, 0x6e, 0x6f, 0x72, 0x6d, 0x3a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x75, 0x6e, 0x6f, 0x72, 0x6d, 0x3c, + 0x54, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x34, 0x3e, 0x3a, + 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, + 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, + 0x42, 0x41, 0x31, 0x36, 0x48, 0x61, 0x6c, 0x66, 0x3a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x68, 0x61, 0x6c, + 0x66, 0x34, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, + 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, + 0x65, 0x20, 0x52, 0x47, 0x42, 0x41, 0x33, 0x32, 0x55, 0x69, 0x6e, 0x74, + 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, + 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x52, 0x47, 0x42, 0x41, 0x33, 0x32, + 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, + 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, + 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, + 0x52, 0x47, 0x42, 0x31, 0x30, 0x41, 0x32, 0x55, 0x6e, 0x6f, 0x72, 0x6d, + 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x72, 0x67, + 0x62, 0x31, 0x30, 0x61, 0x32, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, + 0x77, 0x6e, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x61, 0x73, 0x65, + 0x20, 0x4d, 0x61, 0x78, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x54, 0x3e, 0x3a, 0x3a, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x28, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x6c, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, + 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x54, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, + 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, + 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x5f, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x54, 0x2a, 0x20, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x26, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x6c, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3c, 0x54, 0x2c, + 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, + 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, + 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, + 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x73, 0x72, + 0x63, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x54, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, + 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x26, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x26, 0x20, 0x6c, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, + 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x3c, + 0x54, 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x70, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2c, 0x20, 0x6c, 0x2c, 0x20, 0x76, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x0a, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, + 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, + 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x48, 0x61, 0x6c, 0x66, 0x32, + 0x78, 0x31, 0x36, 0x28, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x32, + 0x3e, 0x20, 0x68, 0x29, 0x0a, 0x09, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, + 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x52, 0x55, 0x4e, 0x54, 0x49, 0x4d, + 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x49, 0x4c, 0x45, 0x52, 0x20, 0x26, + 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, + 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3c, 0x3d, 0x20, 0x31, + 0x32, 0x30, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x28, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x61, 0x73, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x3c, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x3e, 0x28, 0x68, 0x61, + 0x6c, 0x66, 0x28, 0x68, 0x2e, 0x78, 0x29, 0x29, 0x29, 0x20, 0x7c, 0x20, + 0x28, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x61, 0x73, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x3c, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x3e, 0x28, 0x68, 0x61, + 0x6c, 0x66, 0x28, 0x68, 0x2e, 0x79, 0x29, 0x29, 0x29, 0x20, 0x3c, 0x3c, + 0x20, 0x31, 0x36, 0x75, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, + 0x65, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, + 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x3e, + 0x28, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x28, 0x68, 0x29, 0x29, 0x3b, 0x0a, + 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x0a, + 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x32, 0x20, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x48, + 0x61, 0x6c, 0x66, 0x32, 0x78, 0x31, 0x36, 0x28, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x29, 0x0a, 0x09, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x4d, + 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x52, 0x55, 0x4e, 0x54, 0x49, 0x4d, 0x45, + 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x49, 0x4c, 0x45, 0x52, 0x20, 0x26, 0x26, + 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, + 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3c, 0x3d, 0x20, 0x31, 0x32, + 0x30, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, + 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x28, 0x61, 0x73, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x3e, 0x28, 0x75, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x28, 0x69, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x66, 0x66, + 0x66, 0x29, 0x29, 0x2c, 0x20, 0x61, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x3e, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x28, 0x28, 0x69, 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x36, 0x29, 0x20, + 0x26, 0x20, 0x30, 0x78, 0x66, 0x66, 0x66, 0x66, 0x29, 0x29, 0x29, 0x3b, + 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x28, 0x61, + 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, 0x66, 0x32, + 0x3e, 0x28, 0x69, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, + 0x66, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x23, 0x70, 0x72, 0x61, + 0x67, 0x6d, 0x61, 0x20, 0x6d, 0x61, 0x72, 0x6b, 0x20, 0x2d, 0x2d, 0x20, + 0x54, 0x79, 0x70, 0x65, 0x64, 0x20, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x20, 0x2d, 0x2d, 0x0a, 0x0a, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x75, 0x65, + 0x34, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, + 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x72, 0x69, 0x65, 0x6e, + 0x64, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x62, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x26, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x29, 0x20, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x64, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x54, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x09, 0x0a, 0x09, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, + 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x69, 0x6e, 0x74, 0x3e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x20, 0x73, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, + 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3e, 0x20, 0x66, 0x72, 0x69, 0x65, + 0x6e, 0x64, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x3a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, + 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x26, + 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x29, 0x20, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x3d, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x3a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, + 0x29, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x61, + 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, + 0x63, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, + 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, + 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, + 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, + 0x29, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, + 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x78, + 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, + 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, - 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x65, 0x78, - 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, - 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x74, - 0x2a, 0x29, 0x70, 0x29, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x76, 0x2c, 0x20, - 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x78, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, - 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x62, - 0x6f, 0x6f, 0x6c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x77, 0x65, 0x61, - 0x6b, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, + 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, + 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, + 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, + 0x6e, 0x74, 0x2a, 0x29, 0x70, 0x29, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x76, + 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x78, 0x65, 0x64, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, + 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, + 0x20, 0x69, 0x6e, 0x74, 0x20, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x20, 0x69, 0x2c, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x69, - 0x6e, 0x74, 0x20, 0x2a, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x63, 0x6f, 0x6d, - 0x70, 0x61, 0x72, 0x65, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, - 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, - 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, - 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, - 0x29, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, - 0x65, 0x64, 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, - 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, - 0x78, 0x65, 0x64, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, + 0x74, 0x20, 0x69, 0x2c, 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x65, 0x78, 0x70, 0x6c, + 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, + 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, + 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x2a, 0x29, + 0x70, 0x29, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x6d, 0x65, + 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, + 0x65, 0x6c, 0x61, 0x78, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x62, 0x6f, 0x6f, + 0x6c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x5f, + 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x69, 0x2c, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x69, 0x6e, 0x74, + 0x20, 0x2a, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x2c, 0x20, + 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x72, 0x65, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, + 0x77, 0x65, 0x61, 0x6b, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, + 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, + 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, + 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, 0x29, 0x5b, + 0x69, 0x5d, 0x2c, 0x20, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x78, 0x65, - 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, - 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x29, 0x29, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x65, 0x74, 0x63, - 0x68, 0x5f, 0x61, 0x64, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, - 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, - 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x69, 0x6e, 0x74, 0x20, - 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, - 0x69, 0x63, 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, 0x64, 0x64, - 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, - 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, - 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, 0x29, 0x5b, 0x69, 0x5d, 0x2c, 0x20, - 0x76, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, + 0x64, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x78, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, @@ -3911,14 +6068,14 @@ unsigned char ue4_stdlib_metal[] = { 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, - 0x73, 0x75, 0x62, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, + 0x61, 0x64, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, - 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x65, + 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, 0x64, 0x64, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, @@ -3931,36 +6088,36 @@ unsigned char ue4_stdlib_metal[] = { 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x69, 0x6e, 0x74, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6f, 0x72, - 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, - 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x69, 0x2c, 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x66, 0x65, - 0x74, 0x63, 0x68, 0x5f, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, - 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, - 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, - 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, - 0x29, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x6d, 0x65, 0x6d, - 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, - 0x6c, 0x61, 0x78, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x69, 0x6e, 0x74, 0x20, - 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x78, 0x6f, 0x72, 0x5f, 0x61, 0x74, - 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, - 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, - 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, - 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, - 0x5f, 0x78, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, + 0x69, 0x6e, 0x74, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x75, + 0x62, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x69, 0x2c, 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x66, + 0x65, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x65, 0x78, 0x70, + 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, + 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x2a, + 0x29, 0x70, 0x29, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, + 0x72, 0x65, 0x6c, 0x61, 0x78, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x69, 0x6e, + 0x74, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6f, 0x72, 0x5f, 0x61, + 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x2c, 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x66, 0x65, 0x74, 0x63, + 0x68, 0x5f, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, 0x29, 0x5b, @@ -3973,15 +6130,15 @@ unsigned char ue4_stdlib_metal[] = { 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x65, - 0x74, 0x63, 0x68, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, + 0x74, 0x63, 0x68, 0x5f, 0x78, 0x6f, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, - 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, - 0x6e, 0x64, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, + 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x78, + 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, 0x29, 0x5b, 0x69, 0x5d, @@ -3994,14 +6151,14 @@ unsigned char ue4_stdlib_metal[] = { 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x65, 0x74, 0x63, - 0x68, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, + 0x68, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, - 0x69, 0x63, 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x69, 0x6e, + 0x69, 0x63, 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, @@ -4015,14 +6172,14 @@ unsigned char ue4_stdlib_metal[] = { 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, - 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, + 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, - 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x65, + 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, @@ -4030,116 +6187,158 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x78, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, - 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x74, 0x6f, 0x72, - 0x61, 0x67, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, - 0x09, 0x0a, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, - 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, - 0x72, 0x3c, 0x75, 0x69, 0x6e, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x72, - 0x69, 0x65, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, - 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x3c, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x3e, 0x20, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x20, 0x73, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, - 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, - 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x26, 0x20, 0x6f, 0x74, 0x68, 0x65, - 0x72, 0x29, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x3d, 0x20, - 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x09, 0x09, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x3a, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, - 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, - 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x61, 0x64, - 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, - 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x69, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, - 0x6d, 0x69, 0x63, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x65, 0x78, 0x70, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, + 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, + 0x69, 0x6e, 0x74, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x61, + 0x78, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x69, 0x2c, 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x66, + 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x75, 0x69, 0x6e, 0x74, - 0x2a, 0x29, 0x70, 0x29, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x6d, 0x65, 0x6d, - 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, - 0x6c, 0x61, 0x78, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, - 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, + 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x74, 0x2a, + 0x29, 0x70, 0x29, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, + 0x72, 0x65, 0x6c, 0x61, 0x78, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x09, 0x0a, + 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x74, + 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, + 0x75, 0x69, 0x6e, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x72, 0x69, 0x65, + 0x6e, 0x64, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3e, + 0x20, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, + 0x6f, 0x6d, 0x69, 0x63, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x26, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x29, + 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x09, 0x09, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x3a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, + 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x61, + 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, + 0x63, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, + 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, + 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, + 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x29, + 0x70, 0x29, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, + 0x78, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, + 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, + 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, + 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, + 0x63, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, 0x29, 0x5b, 0x69, + 0x5d, 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, + 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x78, + 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, + 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, - 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, - 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, - 0x6d, 0x69, 0x63, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, 0x29, - 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, - 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, - 0x61, 0x78, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, - 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, - 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x61, 0x74, 0x6f, - 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, - 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, - 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, - 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, - 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, - 0x6d, 0x69, 0x63, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, 0x29, - 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, - 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, - 0x61, 0x78, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, - 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, - 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x5f, 0x61, 0x74, - 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, - 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, - 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x2a, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x2c, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x70, - 0x61, 0x72, 0x65, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, - 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, - 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, - 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, - 0x29, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, - 0x65, 0x64, 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, - 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, - 0x78, 0x65, 0x64, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, + 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, + 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, + 0x63, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, 0x29, 0x5b, 0x69, + 0x5d, 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, + 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x78, + 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, + 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x29, 0x29, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x5f, 0x61, 0x74, 0x6f, 0x6d, + 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, + 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x74, + 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, + 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, + 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, + 0x65, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x77, + 0x65, 0x61, 0x6b, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, + 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, + 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, + 0x69, 0x63, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, 0x29, 0x5b, + 0x69, 0x5d, 0x2c, 0x20, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x78, 0x65, + 0x64, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, + 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x78, 0x65, 0x64, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, + 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, + 0x5f, 0x61, 0x64, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, + 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, + 0x6d, 0x69, 0x63, 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, 0x64, + 0x64, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, + 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, + 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, 0x29, 0x5b, 0x69, 0x5d, + 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x78, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, @@ -4148,7 +6347,7 @@ unsigned char ue4_stdlib_metal[] = { 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x65, 0x74, - 0x63, 0x68, 0x5f, 0x61, 0x64, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, + 0x63, 0x68, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x75, 0x69, @@ -4156,7 +6355,7 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, - 0x61, 0x64, 0x64, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, + 0x73, 0x75, 0x62, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, 0x29, 0x5b, @@ -4169,7 +6368,28 @@ unsigned char ue4_stdlib_metal[] = { 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x66, - 0x65, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x61, 0x74, 0x6f, + 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6f, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, + 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, + 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, + 0x5f, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, + 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, + 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, + 0x69, 0x63, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, 0x29, 0x5b, + 0x69, 0x5d, 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, + 0x78, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, + 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x66, + 0x65, 0x74, 0x63, 0x68, 0x5f, 0x78, 0x6f, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, @@ -4177,7 +6397,7 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x66, 0x65, 0x74, 0x63, - 0x68, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, + 0x68, 0x5f, 0x78, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, @@ -4190,28 +6410,7 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6f, 0x72, 0x5f, 0x61, 0x74, - 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, - 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x66, 0x65, 0x74, - 0x63, 0x68, 0x5f, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, - 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, - 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, - 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, - 0x29, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x6d, 0x65, 0x6d, - 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, - 0x6c, 0x61, 0x78, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x78, 0x6f, 0x72, 0x5f, 0x61, + 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, @@ -4219,7 +6418,7 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x66, 0x65, - 0x74, 0x63, 0x68, 0x5f, 0x78, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x70, 0x6c, + 0x74, 0x63, 0x68, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x2a, @@ -4232,7 +6431,7 @@ unsigned char ue4_stdlib_metal[] = { 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, 0x6e, 0x64, + 0x6e, 0x74, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, @@ -4240,7 +6439,7 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, - 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x65, 0x78, + 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x75, 0x69, 0x6e, @@ -4254,14 +6453,14 @@ unsigned char ue4_stdlib_metal[] = { 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, - 0x69, 0x6e, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, + 0x61, 0x78, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, - 0x63, 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, + 0x63, 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x75, @@ -4270,129 +6469,66 @@ unsigned char ue4_stdlib_metal[] = { 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x78, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, - 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, - 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, - 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, - 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, - 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2a, 0x20, 0x70, 0x2c, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x74, 0x6f, - 0x6d, 0x69, 0x63, 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x61, - 0x78, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x28, 0x26, - 0x28, 0x28, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, - 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x70, 0x29, 0x5b, 0x69, 0x5d, - 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x78, 0x65, - 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x3a, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x09, 0x0a, 0x09, 0x0a, 0x23, 0x69, 0x66, - 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, - 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x30, - 0x30, 0x0a, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, - 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, - 0x6d, 0x65, 0x74, 0x61, 0x6c, 0x3a, 0x3a, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x20, 0x41, 0x20, 0x3d, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x3a, 0x3a, 0x72, 0x65, 0x61, 0x64, 0x3e, 0x0a, 0x09, 0x73, 0x74, 0x72, - 0x75, 0x63, 0x74, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, - 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x0a, 0x09, 0x7b, 0x0a, 0x23, - 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, - 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, - 0x45, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x5f, 0x29, 0x0a, - 0x09, 0x09, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, - 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, - 0x54, 0x3e, 0x2c, 0x20, 0x41, 0x3e, 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, - 0x72, 0x65, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3b, 0x0a, 0x23, 0x65, - 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, - 0x65, 0x32, 0x64, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, - 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x54, - 0x3e, 0x2c, 0x20, 0x41, 0x3e, 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, + 0x20, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x3a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x73, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x3b, 0x0a, 0x09, 0x0a, 0x09, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, + 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, + 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x30, 0x30, 0x0a, + 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, + 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x6d, 0x65, + 0x74, 0x61, 0x6c, 0x3a, 0x3a, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, + 0x41, 0x20, 0x3d, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, 0x3a, + 0x72, 0x65, 0x61, 0x64, 0x3e, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x0a, 0x09, 0x7b, 0x0a, 0x23, 0x69, 0x66, + 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, + 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, + 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x5f, 0x29, 0x0a, 0x09, 0x09, + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, + 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x54, 0x3e, + 0x2c, 0x20, 0x41, 0x3e, 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, + 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, + 0x65, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, + 0x64, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, + 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x2c, + 0x20, 0x41, 0x3e, 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x42, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, + 0x66, 0x0a, 0x09, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, + 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, + 0x54, 0x3e, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x2a, 0x20, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0a, 0x09, 0x7d, 0x3b, 0x0a, 0x09, + 0x0a, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, + 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x09, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x3c, 0x54, + 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, 0x3a, 0x72, 0x65, + 0x61, 0x64, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x7b, + 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, + 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, + 0x55, 0x52, 0x45, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x5f, + 0x29, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, + 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, + 0x74, 0x3c, 0x54, 0x3e, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x3a, 0x3a, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x3e, 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x42, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, + 0x09, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x75, + 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, + 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x2c, 0x20, 0x61, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x3a, 0x3a, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x3e, 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, - 0x72, 0x3c, 0x54, 0x3e, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x2a, 0x20, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0a, 0x09, 0x7d, 0x3b, - 0x0a, 0x09, 0x0a, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, - 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x62, 0x75, 0x66, - 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, - 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, 0x3a, - 0x72, 0x65, 0x61, 0x64, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3e, 0x0a, - 0x09, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, - 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, - 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, - 0x5f, 0x5f, 0x29, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, - 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x75, 0x65, 0x34, - 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, - 0x72, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x3a, 0x3a, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x77, 0x72, 0x69, - 0x74, 0x65, 0x3e, 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x42, - 0x75, 0x66, 0x66, 0x65, 0x72, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, - 0x0a, 0x09, 0x09, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, - 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, - 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x2c, 0x20, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, 0x3a, 0x72, 0x65, 0x61, 0x64, - 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3e, 0x20, 0x54, 0x65, 0x78, 0x74, - 0x75, 0x72, 0x65, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3b, 0x0a, 0x23, - 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, - 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x50, 0x6f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x3b, 0x0a, 0x09, 0x7d, 0x3b, 0x0a, 0x23, 0x65, 0x6e, - 0x64, 0x69, 0x66, 0x0a, 0x09, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, - 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, - 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, - 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x2a, 0x20, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x28, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x54, 0x2a, - 0x29, 0x70, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x74, 0x5b, - 0x28, 0x49, 0x2a, 0x32, 0x29, 0x2b, 0x31, 0x5d, 0x2c, 0x20, 0x74, 0x5b, - 0x49, 0x2a, 0x32, 0x5d, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, - 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, - 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, - 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, - 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x28, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x20, 0x54, 0x2a, 0x29, 0x70, 0x2c, 0x20, 0x74, 0x2c, 0x20, - 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x28, 0x49, 0x2a, 0x32, 0x29, 0x2b, 0x31, - 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x2c, 0x20, 0x76, - 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x3b, 0x0a, 0x09, 0x7d, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, + 0x66, 0x0a, 0x09, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x3a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, @@ -4401,191 +6537,192 @@ unsigned char ue4_stdlib_metal[] = { 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x54, 0x2a, 0x20, 0x70, 0x2c, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x54, 0x3e, - 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x70, 0x2c, 0x20, 0x69, 0x2c, - 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, - 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, - 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, - 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x20, 0x54, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, - 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x54, 0x3e, 0x3a, 0x3a, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x28, 0x70, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x74, - 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x23, 0x69, - 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, - 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, - 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x5f, 0x29, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, - 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, - 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x29, 0x29, 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, - 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, - 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x46, 0x2c, 0x20, 0x41, - 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, - 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, - 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x46, 0x2c, - 0x20, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, - 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, - 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, - 0x61, 0x64, 0x28, 0x70, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, - 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, - 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x20, 0x41, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, + 0x20, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x3c, + 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x54, 0x2a, 0x29, 0x70, + 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x28, 0x49, + 0x2a, 0x32, 0x29, 0x2b, 0x31, 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, + 0x32, 0x5d, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, + 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, + 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, + 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, + 0x20, 0x74, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x3c, 0x54, + 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x20, 0x54, 0x2a, 0x29, 0x70, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x74, 0x5b, 0x28, 0x49, 0x2a, 0x32, 0x29, 0x2b, 0x31, 0x5d, 0x2c, + 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x29, 0x3b, + 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x6c, 0x6f, + 0x61, 0x64, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x54, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x54, 0x3e, 0x3a, 0x3a, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x70, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x74, + 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, + 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, - 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, + 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x54, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x54, + 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x54, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x70, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x49, + 0x2a, 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x23, 0x69, 0x66, 0x20, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, + 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x42, + 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, + 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, + 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, - 0x72, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x46, 0x2c, 0x20, 0x74, 0x79, 0x70, - 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x3c, 0x54, 0x3e, 0x3a, - 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, - 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, - 0x70, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, - 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, - 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x20, 0x41, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x6c, - 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, - 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, - 0x72, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, - 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x46, 0x2c, - 0x20, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, - 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, - 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, - 0x61, 0x64, 0x28, 0x70, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, - 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, - 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x20, 0x41, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, - 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, - 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, - 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, - 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, - 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x3c, 0x54, 0x2c, 0x20, - 0x46, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, - 0x74, 0x73, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, - 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x70, 0x2c, 0x20, 0x69, 0x2c, 0x20, - 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7d, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x09, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, - 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, - 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, - 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, - 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, - 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, - 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x29, 0x0a, - 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x74, 0x2a, 0x20, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x46, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, - 0x28, 0x70, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, - 0x5d, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, - 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, - 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, - 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, - 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, - 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, - 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, - 0x26, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, - 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, - 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x3c, 0x54, 0x2c, - 0x20, 0x46, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, - 0x69, 0x74, 0x73, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, - 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x3a, - 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x70, 0x2c, 0x20, 0x69, 0x2c, - 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x29, 0x3b, - 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, - 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, - 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, - 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, - 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, - 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, + 0x28, 0x70, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, + 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, + 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, + 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, + 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, + 0x20, 0x74, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, + 0x3c, 0x54, 0x2c, 0x20, 0x46, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x5f, + 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6e, + 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, + 0x73, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x70, 0x2c, + 0x20, 0x69, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, + 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, + 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, + 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, + 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, + 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, + 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x69, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, - 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x3c, 0x54, - 0x2c, 0x20, 0x46, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, - 0x61, 0x69, 0x74, 0x73, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, - 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, - 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x70, 0x2c, 0x20, 0x69, 0x29, + 0x6e, 0x74, 0x20, 0x69, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x65, 0x78, + 0x74, 0x75, 0x72, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x46, 0x2c, 0x20, 0x74, + 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x3c, 0x54, + 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, + 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, + 0x28, 0x70, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, + 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, + 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, + 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, + 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x54, 0x20, + 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x46, 0x2c, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, + 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, + 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x28, 0x70, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x76, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, + 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, + 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, + 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, 0x2c, + 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x29, 0x0a, 0x09, 0x09, + 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, + 0x72, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x46, 0x2c, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x3c, 0x54, 0x3e, 0x3a, + 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, + 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x70, + 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, @@ -4598,144 +6735,140 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, - 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x54, - 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x66, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, - 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x46, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, + 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, + 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x46, + 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, + 0x73, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, + 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x28, 0x70, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x74, + 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, + 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, + 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, + 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, + 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x46, 0x2c, + 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x3c, 0x54, 0x2c, 0x20, + 0x46, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, + 0x74, 0x73, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, + 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, + 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x70, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, + 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, + 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, + 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, + 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, + 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, + 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, + 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, + 0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, + 0x32, 0x64, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x54, 0x20, 0x76, + 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x3c, + 0x54, 0x2c, 0x20, 0x46, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, + 0x72, 0x61, 0x69, 0x74, 0x73, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6e, 0x75, + 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, + 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x70, 0x2c, 0x20, + 0x69, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x23, + 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x0a, 0x23, 0x69, 0x66, + 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, + 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x30, + 0x30, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, + 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, + 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x62, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x3c, + 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x29, 0x0a, + 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x74, + 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x3c, 0x54, + 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, + 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x21, 0x3d, 0x20, 0x33, 0x29, 0x0a, + 0x09, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, + 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x65, + 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, + 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x46, 0x3e, 0x2c, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x3c, 0x54, 0x3e, 0x3a, + 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, + 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x70, + 0x2e, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x42, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x7d, + 0x0a, 0x09, 0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x09, 0x09, + 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, + 0x64, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x70, 0x2e, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x74, 0x29, + 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, + 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, + 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, + 0x65, 0x6e, 0x74, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, + 0x20, 0x74, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, + 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, - 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x70, - 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, - 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x0a, 0x23, - 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, - 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, - 0x32, 0x30, 0x30, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, - 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, - 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x6c, 0x6f, - 0x61, 0x64, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, - 0x74, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, - 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, - 0x28, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, - 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, - 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x21, 0x3d, 0x20, 0x33, - 0x29, 0x0a, 0x09, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x72, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x74, 0x73, 0x20, 0x21, 0x3d, 0x20, 0x33, 0x29, 0x0a, 0x09, 0x09, 0x09, + 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x46, 0x3e, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, - 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, - 0x28, 0x70, 0x2e, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x42, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x09, 0x09, - 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, - 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3a, 0x3a, 0x6c, - 0x6f, 0x61, 0x64, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x70, 0x2e, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x69, 0x2c, 0x20, - 0x74, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x7d, - 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, - 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, - 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, - 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, - 0x64, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, - 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, - 0x20, 0x70, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x09, - 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, - 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x3c, 0x54, 0x3e, - 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, - 0x65, 0x6e, 0x74, 0x73, 0x20, 0x21, 0x3d, 0x20, 0x33, 0x29, 0x0a, 0x09, - 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x3c, 0x54, - 0x2c, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, - 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x46, 0x3e, 0x2c, - 0x20, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, - 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, - 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x28, 0x70, 0x2e, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, - 0x65, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x2c, 0x20, 0x69, 0x2c, 0x20, - 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, - 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, - 0x09, 0x09, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3a, 0x3a, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x70, 0x2e, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x69, 0x2c, 0x20, - 0x74, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0a, - 0x09, 0x09, 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, - 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, - 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x3e, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x28, 0x70, 0x2e, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x42, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x76, 0x29, + 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x65, 0x6c, + 0x73, 0x65, 0x0a, 0x09, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x70, 0x2e, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x74, 0x2c, + 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, + 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x6d, 0x65, + 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x20, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, - 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3c, 0x6d, 0x65, 0x6d, 0x6f, 0x72, - 0x79, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, - 0x78, 0x65, 0x64, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x3e, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, - 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x5f, - 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, - 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, - 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x29, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, - 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, - 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, - 0x61, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x70, 0x2c, - 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, - 0x32, 0x5d, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, - 0x54, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, - 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, - 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, - 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, - 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, - 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, - 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, - 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, - 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, - 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, - 0x69, 0x63, 0x28, 0x70, 0x2c, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, - 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x20, 0x2f, 0x20, 0x73, 0x69, - 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x54, 0x29, 0x29, 0x2c, 0x20, 0x76, 0x29, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, + 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x3c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, + 0x6f, 0x6d, 0x69, 0x63, 0x3c, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x78, 0x65, + 0x64, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, @@ -4743,130 +6876,52 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x29, 0x29, 0x20, 0x54, 0x20, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, 0x2c, 0x20, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, - 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, - 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x70, 0x2c, 0x20, - 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, - 0x5d, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x54, - 0x29, 0x29, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x62, 0x6f, 0x6f, - 0x6c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x5f, - 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, - 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, - 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, - 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x54, 0x20, 0x2a, 0x65, 0x78, - 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, - 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x5f, 0x61, 0x74, 0x6f, - 0x6d, 0x69, 0x63, 0x28, 0x70, 0x2c, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, - 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x20, 0x2f, 0x20, 0x73, - 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x54, 0x29, 0x29, 0x2c, 0x20, 0x65, - 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x76, 0x29, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, - 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, - 0x29, 0x20, 0x54, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, 0x64, - 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, 0x2c, 0x20, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, - 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, - 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, - 0x64, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x70, 0x2c, - 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, - 0x32, 0x5d, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, - 0x54, 0x29, 0x29, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, - 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, - 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, - 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x61, 0x74, + 0x29, 0x29, 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, - 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x54, 0x20, - 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, - 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x3a, - 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x61, - 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x70, 0x2c, 0x20, 0x6d, 0x69, 0x6e, - 0x28, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x20, 0x2f, - 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x54, 0x29, 0x29, 0x2c, - 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, - 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x3e, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, - 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, - 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x66, 0x65, 0x74, 0x63, - 0x68, 0x5f, 0x6f, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, - 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, - 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, - 0x70, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x20, 0x69, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, - 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x66, 0x65, 0x74, 0x63, - 0x68, 0x5f, 0x6f, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, - 0x70, 0x2c, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x74, 0x5b, - 0x49, 0x2a, 0x32, 0x5d, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, - 0x66, 0x28, 0x54, 0x29, 0x29, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, - 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x54, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x78, 0x6f, 0x72, 0x5f, + 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, 0x64, + 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x70, 0x2c, 0x20, 0x6d, + 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, + 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x54, 0x29, + 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x3e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, + 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, + 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, + 0x28, 0x70, 0x2c, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x74, + 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, + 0x6f, 0x66, 0x28, 0x54, 0x29, 0x29, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, + 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, + 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, + 0x20, 0x54, 0x20, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, @@ -4876,7 +6931,59 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, - 0x3e, 0x3a, 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x78, 0x6f, 0x72, + 0x3e, 0x3a, 0x3a, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, + 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x70, 0x2c, 0x20, 0x6d, 0x69, + 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x20, + 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x54, 0x29, 0x29, + 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, + 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, + 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, + 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, + 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x5f, 0x61, 0x74, + 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, + 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x74, 0x68, + 0x72, 0x65, 0x61, 0x64, 0x20, 0x54, 0x20, 0x2a, 0x65, 0x78, 0x70, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x72, 0x65, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, + 0x63, 0x28, 0x70, 0x2c, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, + 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, + 0x65, 0x6f, 0x66, 0x28, 0x54, 0x29, 0x29, 0x2c, 0x20, 0x65, 0x78, 0x70, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, + 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, + 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, + 0x54, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, 0x64, 0x64, 0x5f, + 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, + 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, + 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, + 0x3e, 0x3a, 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, 0x64, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x70, 0x2c, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x54, 0x29, @@ -4890,7 +6997,7 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x66, 0x65, - 0x74, 0x63, 0x68, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, + 0x74, 0x63, 0x68, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, @@ -4900,7 +7007,7 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x66, - 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x74, 0x6f, + 0x65, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x70, 0x2c, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x54, 0x29, 0x29, 0x2c, 0x20, 0x76, @@ -4914,180 +7021,207 @@ unsigned char ue4_stdlib_metal[] = { 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, - 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, - 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, - 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x69, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, - 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, - 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, - 0x70, 0x2c, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x74, 0x5b, - 0x49, 0x2a, 0x32, 0x5d, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, - 0x66, 0x28, 0x54, 0x29, 0x29, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, - 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x54, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x78, 0x5f, - 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, - 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, - 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, - 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, - 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, - 0x3e, 0x3a, 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x78, - 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x70, 0x2c, 0x20, 0x6d, - 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, - 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x54, 0x29, - 0x29, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x23, 0x69, 0x66, 0x20, - 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, - 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x30, 0x30, - 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, - 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, - 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x5f, - 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, - 0x64, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, - 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, - 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x69, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, - 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x75, 0x66, 0x66, - 0x65, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3a, 0x3a, 0x6c, - 0x6f, 0x61, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3c, 0x54, - 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x70, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x09, - 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, - 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, - 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, - 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, - 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, - 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, - 0x69, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x74, 0x6f, - 0x6d, 0x69, 0x63, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x62, - 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, - 0x6e, 0x74, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, + 0x6f, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, - 0x69, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, - 0x09, 0x09, 0x09, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, - 0x6f, 0x6d, 0x69, 0x63, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, - 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, - 0x28, 0x70, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, - 0x74, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, - 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, - 0x69, 0x63, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, - 0x74, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, - 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, - 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x75, 0x66, - 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3a, 0x3a, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x61, 0x74, 0x6f, - 0x6d, 0x69, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x70, 0x2e, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x74, 0x2c, 0x20, - 0x69, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, - 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, - 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, + 0x69, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, + 0x6f, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x70, 0x2c, + 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, + 0x32, 0x5d, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, + 0x54, 0x29, 0x29, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, + 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x78, 0x6f, 0x72, 0x5f, 0x61, 0x74, + 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, + 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x54, 0x20, + 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, + 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x3a, + 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x78, 0x6f, 0x72, 0x5f, 0x61, + 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x70, 0x2c, 0x20, 0x6d, 0x69, 0x6e, + 0x28, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x20, 0x2f, + 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x54, 0x29, 0x29, 0x2c, + 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, + 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x3e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x5f, 0x61, 0x74, 0x6f, - 0x6d, 0x69, 0x63, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x62, - 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, - 0x6e, 0x74, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, - 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, - 0x69, 0x2c, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x54, 0x20, - 0x2a, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x54, - 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, - 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3a, 0x3a, 0x63, 0x6f, 0x6d, - 0x70, 0x61, 0x72, 0x65, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, - 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x70, 0x2e, 0x50, 0x6f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, - 0x20, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x76, - 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, - 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, - 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, - 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, - 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x66, 0x65, 0x74, 0x63, + 0x68, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, + 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, + 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, + 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x66, 0x65, 0x74, + 0x63, 0x68, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, + 0x63, 0x28, 0x70, 0x2c, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, + 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, + 0x65, 0x6f, 0x66, 0x28, 0x54, 0x29, 0x29, 0x2c, 0x20, 0x76, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, - 0x29, 0x20, 0x54, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, 0x64, - 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x74, 0x68, 0x72, - 0x65, 0x61, 0x64, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, - 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x3c, 0x46, 0x2c, 0x20, 0x41, - 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, - 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, - 0x6f, 0x6d, 0x69, 0x63, 0x3a, 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, - 0x61, 0x64, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3c, 0x54, - 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x70, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x76, 0x29, - 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, - 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, - 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, - 0x20, 0x54, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x75, 0x62, - 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x74, 0x68, 0x72, 0x65, - 0x61, 0x64, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, - 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, - 0x26, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, - 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x6f, - 0x6d, 0x69, 0x63, 0x3a, 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x73, - 0x75, 0x62, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3c, 0x54, 0x2c, - 0x20, 0x49, 0x3e, 0x28, 0x70, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x76, 0x29, 0x3b, + 0x29, 0x20, 0x54, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x69, + 0x6e, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x3c, 0x54, 0x3e, 0x3a, 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, + 0x69, 0x6e, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x70, 0x2c, + 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, + 0x32, 0x5d, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, + 0x54, 0x29, 0x29, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x49, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, + 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x74, + 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, + 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x54, 0x20, + 0x76, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, + 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x3a, + 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, + 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x70, 0x2c, 0x20, 0x6d, 0x69, 0x6e, + 0x28, 0x69, 0x2c, 0x20, 0x74, 0x5b, 0x49, 0x2a, 0x32, 0x5d, 0x20, 0x2f, + 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x54, 0x29, 0x29, 0x2c, + 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, + 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, + 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x30, 0x30, 0x0a, 0x09, + 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, + 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, + 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, + 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x29, 0x29, 0x20, 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x61, 0x74, + 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, + 0x65, 0x6e, 0x74, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3a, 0x3a, 0x6c, 0x6f, 0x61, + 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3c, 0x54, 0x2c, 0x20, + 0x49, 0x3e, 0x28, 0x70, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, + 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, + 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, + 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, + 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, + 0x63, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x62, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, + 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, + 0x09, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, + 0x69, 0x63, 0x3a, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x74, + 0x6f, 0x6d, 0x69, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x70, + 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x74, 0x2c, + 0x20, 0x69, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, + 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, + 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, + 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, + 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x62, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x3c, + 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, + 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, + 0x54, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3a, 0x3a, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, + 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x70, 0x2e, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, + 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, + 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x72, 0x65, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, + 0x63, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x62, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, + 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x54, 0x20, 0x2a, 0x65, + 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x54, 0x20, 0x76, + 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, + 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3a, 0x3a, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x72, 0x65, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, + 0x77, 0x65, 0x61, 0x6b, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3c, + 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x70, 0x2e, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x65, + 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, @@ -5097,7 +7231,29 @@ unsigned char ue4_stdlib_metal[] = { 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x54, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6f, 0x72, 0x5f, 0x61, + 0x54, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, 0x64, 0x64, 0x5f, + 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, + 0x64, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, + 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x09, + 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, + 0x69, 0x63, 0x3a, 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, 0x64, + 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3c, 0x54, 0x2c, 0x20, + 0x49, 0x3e, 0x28, 0x70, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, + 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, + 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, + 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, + 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, + 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, + 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, + 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, @@ -5106,20 +7262,20 @@ unsigned char ue4_stdlib_metal[] = { 0x74, 0x20, 0x69, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, - 0x63, 0x3a, 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6f, 0x72, 0x5f, - 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, - 0x28, 0x70, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, - 0x74, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, - 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x66, - 0x65, 0x74, 0x63, 0x68, 0x5f, 0x78, 0x6f, 0x72, 0x5f, 0x61, 0x74, 0x6f, + 0x63, 0x3a, 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x75, 0x62, + 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x49, + 0x3e, 0x28, 0x70, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2c, + 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, + 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, + 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, + 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, + 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, + 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6f, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, @@ -5128,29 +7284,7 @@ unsigned char ue4_stdlib_metal[] = { 0x69, 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3a, - 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x78, 0x6f, 0x72, 0x5f, 0x61, - 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, - 0x70, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x74, - 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, - 0x0a, 0x09, 0x09, 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, - 0x54, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, - 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x66, 0x65, - 0x74, 0x63, 0x68, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, - 0x69, 0x63, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, - 0x74, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x2a, 0x20, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, - 0x2c, 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, - 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x75, 0x66, - 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3a, 0x3a, - 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x74, + 0x3a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6f, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x70, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, @@ -5163,7 +7297,7 @@ unsigned char ue4_stdlib_metal[] = { 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x66, 0x65, 0x74, - 0x63, 0x68, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, + 0x63, 0x68, 0x5f, 0x78, 0x6f, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, 0x63, @@ -5172,7 +7306,7 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x54, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3a, 0x3a, 0x66, - 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x74, 0x6f, + 0x65, 0x74, 0x63, 0x68, 0x5f, 0x78, 0x6f, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x70, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, @@ -5185,7 +7319,7 @@ unsigned char ue4_stdlib_metal[] = { 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x66, 0x65, 0x74, 0x63, - 0x68, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, + 0x68, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x3c, 0x46, 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, @@ -5194,110 +7328,126 @@ unsigned char ue4_stdlib_metal[] = { 0x54, 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3a, 0x3a, 0x66, 0x65, - 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x74, 0x6f, 0x6d, + 0x74, 0x63, 0x68, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x70, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, - 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x23, 0x65, - 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, - 0x0a, 0x2f, 0x2f, 0x20, 0x4e, 0x6f, 0x20, 0x54, 0x79, 0x70, 0x65, 0x64, + 0x2c, 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, + 0x0a, 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, + 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, + 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, + 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, + 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, + 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x3c, 0x46, + 0x2c, 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, + 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x54, + 0x20, 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3a, 0x3a, 0x66, 0x65, 0x74, + 0x63, 0x68, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, + 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x70, 0x2e, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, + 0x20, 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x09, 0x0a, + 0x09, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, + 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x49, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x20, 0x46, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x20, 0x41, 0x3e, 0x0a, 0x09, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, + 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, 0x74, + 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x3c, 0x46, 0x2c, + 0x20, 0x41, 0x3e, 0x26, 0x20, 0x70, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x74, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x20, 0x54, 0x20, + 0x76, 0x29, 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, + 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x3a, 0x3a, 0x66, 0x65, 0x74, 0x63, + 0x68, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, + 0x3c, 0x54, 0x2c, 0x20, 0x49, 0x3e, 0x28, 0x70, 0x2e, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x69, 0x2c, 0x20, + 0x76, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, + 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x0a, 0x2f, + 0x2f, 0x20, 0x4e, 0x6f, 0x20, 0x54, 0x79, 0x70, 0x65, 0x64, 0x2d, 0x62, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x2d, 0x20, 0x62, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x22, 0x72, 0x61, 0x77, + 0x22, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, + 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, 0x5f, + 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x41, 0x57, 0x5f, 0x5f, + 0x20, 0x30, 0x0a, 0x2f, 0x2f, 0x20, 0x54, 0x79, 0x70, 0x65, 0x64, 0x2d, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, 0x65, 0x6d, 0x75, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x74, + 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x0a, 0x23, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, + 0x52, 0x5f, 0x32, 0x44, 0x5f, 0x5f, 0x20, 0x31, 0x0a, 0x2f, 0x2f, 0x20, + 0x54, 0x79, 0x70, 0x65, 0x64, 0x2d, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x2d, 0x62, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, + 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x42, 0x5f, 0x5f, + 0x20, 0x33, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x54, 0x79, 0x70, 0x65, 0x64, 0x2d, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, 0x69, 0x6d, 0x70, 0x6c, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x2d, - 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x73, 0x20, 0x61, 0x72, 0x65, - 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x22, 0x72, - 0x61, 0x77, 0x22, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, - 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x44, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x41, 0x57, - 0x5f, 0x5f, 0x20, 0x30, 0x0a, 0x2f, 0x2f, 0x20, 0x54, 0x79, 0x70, 0x65, - 0x64, 0x2d, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, 0x65, 0x6d, 0x75, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, - 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x0a, 0x23, - 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x72, 0x65, 0x61, 0x64, 0x2d, 0x6f, 0x6e, 0x6c, 0x79, + 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x73, 0x0a, 0x23, 0x69, 0x66, + 0x6e, 0x64, 0x65, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, + 0x52, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, + 0x5f, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, + 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, 0x5f, + 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, + 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x5f, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, 0x5f, 0x42, 0x55, 0x46, - 0x46, 0x45, 0x52, 0x5f, 0x32, 0x44, 0x5f, 0x5f, 0x20, 0x31, 0x0a, 0x2f, - 0x2f, 0x20, 0x54, 0x79, 0x70, 0x65, 0x64, 0x2d, 0x62, 0x75, 0x66, 0x66, - 0x65, 0x72, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, - 0x61, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x2d, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x44, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x42, - 0x5f, 0x5f, 0x20, 0x33, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x54, 0x79, 0x70, + 0x46, 0x45, 0x52, 0x5f, 0x52, 0x41, 0x57, 0x5f, 0x5f, 0x0a, 0x23, 0x65, + 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x54, 0x79, 0x70, 0x65, 0x64, 0x2d, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x61, 0x64, 0x2d, 0x6f, 0x6e, - 0x6c, 0x79, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x73, 0x0a, 0x23, - 0x69, 0x66, 0x6e, 0x64, 0x65, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, - 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, 0x5f, 0x42, 0x55, 0x46, - 0x46, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x49, 0x4d, 0x50, - 0x4c, 0x5f, 0x5f, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, - 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x44, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x41, - 0x44, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x5f, 0x20, 0x5f, 0x5f, 0x4d, - 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, 0x5f, 0x42, - 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x41, 0x57, 0x5f, 0x5f, 0x0a, - 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x54, - 0x79, 0x70, 0x65, 0x64, 0x2d, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, - 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x61, 0x64, 0x2d, - 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, - 0x73, 0x0a, 0x23, 0x69, 0x66, 0x6e, 0x64, 0x65, 0x66, 0x20, 0x5f, 0x5f, - 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, 0x5f, - 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x57, 0x5f, 0x49, 0x4d, - 0x50, 0x4c, 0x5f, 0x5f, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x44, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x57, - 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x5f, 0x20, 0x5f, 0x5f, 0x4d, 0x45, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x61, 0x64, 0x2d, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x73, 0x0a, + 0x23, 0x69, 0x66, 0x6e, 0x64, 0x65, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, 0x5f, 0x42, 0x55, - 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x41, 0x57, 0x5f, 0x5f, 0x0a, 0x23, - 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x23, - 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, - 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, - 0x45, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x5f, 0x29, 0x20, - 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x44, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, - 0x52, 0x45, 0x41, 0x44, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x5f, 0x20, - 0x3d, 0x3d, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x44, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, - 0x54, 0x42, 0x5f, 0x5f, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, - 0x31, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, - 0x20, 0x49, 0x29, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, - 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, - 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, - 0x74, 0x3c, 0x54, 0x3e, 0x3e, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x74, - 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, - 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, - 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x32, 0x5f, 0x72, 0x65, - 0x61, 0x64, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, - 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, - 0x65, 0x72, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, - 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x54, 0x3e, - 0x3e, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, - 0x72, 0x65, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x64, 0x65, - 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x34, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x54, - 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x74, 0x65, 0x78, 0x74, - 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x75, - 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, - 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x3e, 0x20, 0x4e, 0x20, - 0x5b, 0x5b, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x49, - 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, 0x5f, + 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x57, 0x5f, 0x49, 0x4d, 0x50, 0x4c, + 0x5f, 0x5f, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, - 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x41, 0x44, - 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x5f, 0x20, 0x3d, 0x3d, 0x20, 0x5f, - 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, - 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x32, 0x44, 0x5f, 0x5f, - 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, - 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x31, 0x5f, 0x72, 0x65, - 0x61, 0x64, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, - 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x75, 0x65, - 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, - 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x3e, 0x20, 0x4e, 0x20, 0x5b, - 0x5b, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x49, 0x29, - 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x32, - 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, - 0x49, 0x29, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, + 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x57, 0x5f, 0x49, + 0x4d, 0x50, 0x4c, 0x5f, 0x5f, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, + 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, 0x5f, 0x42, 0x55, 0x46, 0x46, + 0x45, 0x52, 0x5f, 0x52, 0x41, 0x57, 0x5f, 0x5f, 0x0a, 0x23, 0x65, 0x6e, + 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x23, 0x69, 0x66, + 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, + 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, + 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x5f, 0x29, 0x20, 0x26, 0x26, + 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x44, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x45, + 0x41, 0x44, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x5f, 0x20, 0x3d, 0x3d, + 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x44, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x42, + 0x5f, 0x5f, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, + 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x31, 0x5f, + 0x72, 0x65, 0x61, 0x64, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, + 0x29, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, + 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, + 0x54, 0x3e, 0x3e, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x74, 0x65, 0x78, + 0x74, 0x75, 0x72, 0x65, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, + 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x32, 0x5f, 0x72, 0x65, 0x61, 0x64, + 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x74, 0x65, + 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x3e, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, @@ -5305,42 +7455,79 @@ unsigned char ue4_stdlib_metal[] = { 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x34, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, - 0x65, 0x32, 0x64, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, - 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x54, - 0x3e, 0x3e, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x74, 0x65, 0x78, 0x74, - 0x75, 0x72, 0x65, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x65, - 0x6c, 0x73, 0x65, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x31, - 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, - 0x49, 0x29, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x4e, 0x20, 0x5b, - 0x5b, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, 0x49, 0x29, 0x20, - 0x5d, 0x5d, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, - 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x32, 0x5f, - 0x72, 0x65, 0x61, 0x64, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, - 0x29, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, - 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x4e, 0x20, 0x5b, 0x5b, - 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, 0x49, 0x29, 0x20, 0x5d, + 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x75, 0x65, 0x34, + 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, + 0x72, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x3e, 0x20, 0x4e, 0x20, 0x5b, 0x5b, + 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x49, 0x29, 0x20, + 0x5d, 0x5d, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, + 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, 0x5f, 0x42, + 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x49, + 0x4d, 0x50, 0x4c, 0x5f, 0x5f, 0x20, 0x3d, 0x3d, 0x20, 0x5f, 0x5f, 0x4d, + 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, 0x5f, 0x42, + 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x32, 0x44, 0x5f, 0x5f, 0x0a, 0x23, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, + 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x31, 0x5f, 0x72, 0x65, 0x61, 0x64, + 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x74, 0x65, + 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x75, 0x65, 0x34, 0x3a, + 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, + 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x3e, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, - 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x34, 0x5f, 0x72, + 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x32, 0x5f, 0x72, + 0x65, 0x61, 0x64, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, + 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x75, + 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, + 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x3e, 0x20, 0x4e, 0x20, + 0x5b, 0x5b, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x49, + 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x34, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, + 0x20, 0x49, 0x29, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, + 0x64, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, + 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x3e, + 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, + 0x65, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x65, 0x6c, 0x73, + 0x65, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x31, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, - 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x0a, 0x23, 0x69, 0x66, - 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, - 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, - 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x5f, 0x29, 0x20, 0x26, 0x26, - 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x44, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x57, - 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x5f, 0x20, 0x3d, 0x3d, 0x20, 0x5f, - 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, - 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x42, 0x5f, 0x5f, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, - 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x31, 0x5f, 0x72, 0x77, + 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x32, 0x5f, 0x72, 0x65, + 0x61, 0x64, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x62, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, + 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, + 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x34, 0x5f, 0x72, 0x65, 0x61, + 0x64, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, + 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, + 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, + 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x42, 0x55, + 0x46, 0x46, 0x45, 0x52, 0x5f, 0x5f, 0x29, 0x20, 0x26, 0x26, 0x20, 0x5f, + 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, + 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x57, 0x5f, 0x49, + 0x4d, 0x50, 0x4c, 0x5f, 0x5f, 0x20, 0x3d, 0x3d, 0x20, 0x5f, 0x5f, 0x4d, + 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, 0x5f, 0x42, + 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x42, 0x5f, 0x5f, 0x0a, 0x23, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, + 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x31, 0x5f, 0x72, 0x77, 0x28, 0x54, + 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x74, 0x65, 0x78, 0x74, + 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x75, + 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, + 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x2c, 0x20, 0x61, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x3a, 0x3a, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x3e, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x74, + 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, + 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x32, 0x5f, 0x72, 0x77, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, @@ -5349,7 +7536,7 @@ unsigned char ue4_stdlib_metal[] = { 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3e, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, - 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x32, 0x5f, + 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x34, 0x5f, 0x72, 0x77, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, @@ -5357,343 +7544,263 @@ unsigned char ue4_stdlib_metal[] = { 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, 0x3a, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3e, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x49, + 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, 0x5f, + 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, + 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x57, 0x5f, 0x49, + 0x4d, 0x50, 0x4c, 0x5f, 0x5f, 0x20, 0x3d, 0x3d, 0x20, 0x5f, 0x5f, 0x4d, + 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, 0x5f, 0x42, + 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x32, 0x44, 0x5f, 0x5f, 0x0a, 0x23, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, + 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x31, 0x5f, 0x72, 0x77, 0x28, 0x54, + 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x74, 0x65, 0x78, 0x74, + 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, + 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, + 0x3c, 0x54, 0x3e, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, + 0x3a, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3e, + 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, + 0x65, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x32, 0x5f, 0x72, 0x77, 0x28, 0x54, 0x2c, 0x20, 0x4e, + 0x2c, 0x20, 0x49, 0x29, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, + 0x32, 0x64, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, + 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x54, 0x3e, + 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, 0x3a, 0x72, 0x65, + 0x61, 0x64, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3e, 0x20, 0x4e, 0x20, + 0x5b, 0x5b, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x34, 0x5f, 0x72, 0x77, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, - 0x29, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, - 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, - 0x54, 0x3e, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, 0x3a, - 0x72, 0x65, 0x61, 0x64, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3e, 0x20, - 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, - 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, - 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x44, 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x52, 0x57, - 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x5f, 0x5f, 0x20, 0x3d, 0x3d, 0x20, 0x5f, - 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x44, - 0x5f, 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x5f, 0x32, 0x44, 0x5f, 0x5f, - 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, - 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x31, 0x5f, 0x72, 0x77, - 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x74, 0x65, - 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x75, 0x65, 0x34, 0x3a, - 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, - 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x3a, 0x3a, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x77, 0x72, 0x69, 0x74, - 0x65, 0x3e, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x74, 0x65, 0x78, 0x74, - 0x75, 0x72, 0x65, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x64, - 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, - 0x75, 0x66, 0x66, 0x65, 0x72, 0x32, 0x5f, 0x72, 0x77, 0x28, 0x54, 0x2c, - 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, - 0x72, 0x65, 0x32, 0x64, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, - 0x6b, 0x65, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, - 0x54, 0x3e, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, 0x3a, - 0x72, 0x65, 0x61, 0x64, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3e, 0x20, - 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, - 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, - 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, - 0x65, 0x72, 0x34, 0x5f, 0x72, 0x77, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, - 0x20, 0x49, 0x29, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, - 0x64, 0x3c, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, - 0x73, 0x63, 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x2c, - 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, 0x3a, 0x72, 0x65, 0x61, - 0x64, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3e, 0x20, 0x4e, 0x20, 0x5b, - 0x5b, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x49, 0x29, - 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x23, 0x64, - 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, - 0x75, 0x66, 0x66, 0x65, 0x72, 0x31, 0x5f, 0x72, 0x77, 0x28, 0x54, 0x2c, - 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, - 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, - 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, - 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, - 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x32, 0x5f, 0x72, 0x77, - 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, - 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x4e, 0x20, - 0x5b, 0x5b, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, 0x49, 0x29, - 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x34, - 0x5f, 0x72, 0x77, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, - 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, - 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, - 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, - 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, - 0x66, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x44, 0x75, 0x65, 0x20, 0x74, 0x6f, - 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x2d, 0x66, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x20, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x33, 0x3e, 0x20, 0x74, - 0x79, 0x70, 0x65, 0x73, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x61, 0x6c, - 0x77, 0x61, 0x79, 0x73, 0x20, 0x75, 0x73, 0x65, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x2d, 0x62, 0x75, 0x66, 0x66, 0x65, - 0x72, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, - 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, - 0x72, 0x33, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x54, 0x2c, 0x20, 0x4e, - 0x2c, 0x20, 0x49, 0x29, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, + 0x29, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, + 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x6d, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x63, + 0x61, 0x6c, 0x61, 0x72, 0x5f, 0x74, 0x3c, 0x54, 0x3e, 0x2c, 0x20, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, 0x3a, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x77, 0x72, 0x69, 0x74, 0x65, 0x3e, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x49, 0x29, 0x20, 0x5d, + 0x5d, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x23, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x31, 0x5f, 0x72, 0x77, 0x28, 0x54, 0x2c, 0x20, 0x4e, + 0x2c, 0x20, 0x49, 0x29, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, + 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, + 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, + 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x32, 0x5f, 0x72, 0x77, 0x28, 0x54, + 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x4e, 0x20, 0x5b, 0x5b, + 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, 0x49, 0x29, 0x20, 0x5d, + 0x5d, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x34, 0x5f, 0x72, + 0x77, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, 0x49, - 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, - 0x33, 0x5f, 0x72, 0x77, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, - 0x29, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, - 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, - 0x2a, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, - 0x72, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, - 0x53, 0x69, 0x6d, 0x69, 0x6c, 0x61, 0x72, 0x6c, 0x79, 0x20, 0x61, 0x6c, - 0x6c, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x73, 0x20, 0x6d, 0x75, - 0x73, 0x74, 0x20, 0x67, 0x6f, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, - 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x2d, - 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x23, - 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, - 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, - 0x63, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x63, - 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, - 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x62, 0x75, - 0x66, 0x66, 0x65, 0x72, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x7d, - 0x0a, 0x0a, 0x23, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x20, 0x6d, 0x61, - 0x72, 0x6b, 0x20, 0x2d, 0x2d, 0x20, 0x43, 0x75, 0x62, 0x65, 0x20, 0x41, - 0x72, 0x72, 0x61, 0x79, 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, - 0x20, 0x2d, 0x2d, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, - 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, - 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, - 0x41, 0x52, 0x52, 0x41, 0x59, 0x5f, 0x5f, 0x29, 0x20, 0x26, 0x26, 0x20, - 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x4d, 0x45, - 0x54, 0x41, 0x4c, 0x5f, 0x55, 0x53, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, - 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, - 0x41, 0x59, 0x5f, 0x5f, 0x29, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x48, - 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, - 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x5f, 0x5f, - 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, - 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x55, 0x53, 0x45, 0x5f, 0x54, 0x45, 0x58, + 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, + 0x0a, 0x2f, 0x2f, 0x20, 0x44, 0x75, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x70, + 0x69, 0x78, 0x65, 0x6c, 0x2d, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, + 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x33, 0x3e, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x61, 0x6c, 0x77, 0x61, + 0x79, 0x73, 0x20, 0x75, 0x73, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, + 0x79, 0x70, 0x65, 0x64, 0x2d, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, + 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, + 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x33, + 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, + 0x49, 0x29, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, 0x4e, 0x20, 0x5b, + 0x5b, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, 0x49, 0x29, 0x20, + 0x5d, 0x5d, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, + 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x33, 0x5f, + 0x72, 0x77, 0x28, 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, + 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, 0x3e, 0x2a, 0x20, + 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, + 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x53, 0x69, + 0x6d, 0x69, 0x6c, 0x61, 0x72, 0x6c, 0x79, 0x20, 0x61, 0x6c, 0x6c, 0x20, + 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x73, 0x20, 0x6d, 0x75, 0x73, 0x74, + 0x20, 0x67, 0x6f, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x2d, 0x62, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x23, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x42, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x5f, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x28, + 0x54, 0x2c, 0x20, 0x4e, 0x2c, 0x20, 0x49, 0x29, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3c, 0x54, + 0x3e, 0x2a, 0x20, 0x4e, 0x20, 0x5b, 0x5b, 0x20, 0x62, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x28, 0x49, 0x29, 0x20, 0x5d, 0x5d, 0x0a, 0x7d, 0x0a, 0x0a, + 0x23, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x20, 0x6d, 0x61, 0x72, 0x6b, + 0x20, 0x2d, 0x2d, 0x20, 0x43, 0x75, 0x62, 0x65, 0x20, 0x41, 0x72, 0x72, + 0x61, 0x79, 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x20, 0x2d, + 0x2d, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, - 0x52, 0x41, 0x59, 0x5f, 0x5f, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x0a, 0x23, - 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, - 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, - 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x20, 0x31, 0x0a, 0x23, 0x65, 0x6c, - 0x73, 0x65, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x48, - 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, - 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x20, 0x30, - 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x23, 0x69, 0x66, 0x20, - 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, - 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, - 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, - 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, - 0x79, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, - 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x0a, 0x23, 0x64, 0x65, 0x66, - 0x69, 0x6e, 0x65, 0x20, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, - 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x64, 0x65, 0x70, 0x74, - 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x0a, - 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, - 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, - 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x74, 0x65, 0x78, 0x74, - 0x75, 0x72, 0x65, 0x32, 0x64, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x0a, - 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x65, 0x70, 0x74, - 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, - 0x64, 0x65, 0x70, 0x74, 0x68, 0x32, 0x64, 0x5f, 0x61, 0x72, 0x72, 0x61, - 0x79, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x0a, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x75, 0x65, 0x34, 0x0a, - 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x21, 0x48, 0x41, 0x56, 0x45, 0x5f, - 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, - 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, - 0x2f, 0x20, 0x43, 0x75, 0x62, 0x65, 0x46, 0x61, 0x63, 0x65, 0x73, 0x20, - 0x61, 0x73, 0x20, 0x6c, 0x61, 0x69, 0x64, 0x20, 0x6f, 0x75, 0x74, 0x20, - 0x69, 0x6e, 0x20, 0x55, 0x45, 0x34, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, - 0x66, 0x6c, 0x61, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x73, - 0x65, 0x65, 0x6d, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, - 0x78, 0x2b, 0x29, 0x2c, 0x20, 0x4c, 0x65, 0x66, 0x74, 0x28, 0x78, 0x2d, - 0x29, 0x2c, 0x20, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x28, 0x79, - 0x2b, 0x29, 0x2c, 0x20, 0x42, 0x61, 0x63, 0x6b, 0x28, 0x79, 0x2d, 0x29, - 0x2c, 0x20, 0x55, 0x70, 0x20, 0x28, 0x7a, 0x2b, 0x29, 0x2c, 0x20, 0x44, - 0x6f, 0x77, 0x6e, 0x20, 0x28, 0x7a, 0x2d, 0x29, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x2f, 0x2f, 0x20, 0x4c, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, - 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6f, - 0x6e, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x63, 0x68, 0x6f, 0x6f, 0x73, - 0x65, 0x73, 0x20, 0x61, 0x20, 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, 0x61, - 0x6e, 0x64, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, - 0x6f, 0x20, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x77, 0x6f, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, - 0x61, 0x20, 0x30, 0x2d, 0x31, 0x20, 0x55, 0x56, 0x20, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x66, - 0x61, 0x63, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, - 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, - 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, - 0x20, 0x43, 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, 0x32, 0x44, - 0x41, 0x72, 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, 0x66, 0x6c, - 0x6f, 0x61, 0x74, 0x33, 0x20, 0x50, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x29, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x74, 0x61, 0x6b, 0x65, 0x20, 0x61, 0x62, - 0x73, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, - 0x67, 0x20, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x74, 0x6f, 0x20, - 0x6d, 0x61, 0x6b, 0x65, 0x20, 0x66, 0x61, 0x63, 0x65, 0x20, 0x73, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x70, - 0x6c, 0x65, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x43, 0x6f, 0x6f, 0x72, 0x64, - 0x73, 0x20, 0x3d, 0x20, 0x61, 0x62, 0x73, 0x28, 0x50, 0x2e, 0x78, 0x79, - 0x7a, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x43, 0x75, 0x62, 0x65, 0x46, 0x61, - 0x63, 0x65, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x50, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x78, 0x69, 0x73, + 0x52, 0x41, 0x59, 0x5f, 0x5f, 0x29, 0x20, 0x26, 0x26, 0x20, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, + 0x4c, 0x5f, 0x55, 0x53, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, + 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, + 0x5f, 0x5f, 0x29, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x48, 0x41, 0x56, + 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, + 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x5f, 0x5f, 0x20, 0x3d, + 0x3d, 0x20, 0x31, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, + 0x41, 0x4c, 0x5f, 0x55, 0x53, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, + 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, + 0x59, 0x5f, 0x5f, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x0a, 0x23, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, + 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, + 0x52, 0x52, 0x41, 0x59, 0x20, 0x31, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, + 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x48, 0x41, 0x56, + 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, + 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x20, 0x30, 0x0a, 0x23, + 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, + 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, + 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x23, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, + 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x20, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, + 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x0a, 0x23, 0x65, + 0x6c, 0x73, 0x65, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, + 0x65, 0x32, 0x64, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x0a, 0x23, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, + 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x64, 0x65, + 0x70, 0x74, 0x68, 0x32, 0x64, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x0a, + 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x75, 0x65, 0x34, 0x0a, 0x7b, 0x0a, + 0x23, 0x69, 0x66, 0x20, 0x21, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, + 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, + 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x43, 0x75, 0x62, 0x65, 0x46, 0x61, 0x63, 0x65, 0x73, 0x20, 0x61, 0x73, + 0x20, 0x6c, 0x61, 0x69, 0x64, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x69, 0x6e, + 0x20, 0x55, 0x45, 0x34, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x66, 0x6c, + 0x61, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x73, 0x65, 0x65, + 0x6d, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x52, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x78, 0x2b, + 0x29, 0x2c, 0x20, 0x4c, 0x65, 0x66, 0x74, 0x28, 0x78, 0x2d, 0x29, 0x2c, + 0x20, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x28, 0x79, 0x2b, 0x29, + 0x2c, 0x20, 0x42, 0x61, 0x63, 0x6b, 0x28, 0x79, 0x2d, 0x29, 0x2c, 0x20, + 0x55, 0x70, 0x20, 0x28, 0x7a, 0x2b, 0x29, 0x2c, 0x20, 0x44, 0x6f, 0x77, + 0x6e, 0x20, 0x28, 0x7a, 0x2d, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x4c, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x76, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, + 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x20, 0x63, 0x68, 0x6f, 0x6f, 0x73, 0x65, 0x73, + 0x20, 0x61, 0x20, 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x77, 0x6f, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x61, 0x20, + 0x30, 0x2d, 0x31, 0x20, 0x55, 0x56, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x66, 0x61, 0x63, + 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x43, + 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, 0x32, 0x44, 0x41, 0x72, + 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, 0x66, 0x6c, 0x6f, 0x61, + 0x74, 0x33, 0x20, 0x50, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x74, 0x61, 0x6b, 0x65, 0x20, 0x61, 0x62, 0x73, 0x20, + 0x6f, 0x66, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x20, + 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x61, + 0x6b, 0x65, 0x20, 0x66, 0x61, 0x63, 0x65, 0x20, 0x73, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, + 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x33, 0x20, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x20, + 0x3d, 0x20, 0x61, 0x62, 0x73, 0x28, 0x50, 0x2e, 0x78, 0x79, 0x7a, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x20, 0x43, 0x75, 0x62, 0x65, 0x46, 0x61, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x75, 0x20, 0x3d, 0x20, - 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, - 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x76, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, 0x43, - 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x78, 0x20, 0x3e, 0x3d, 0x20, 0x43, - 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x79, 0x20, 0x26, 0x26, 0x20, 0x43, - 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x78, 0x20, 0x3e, 0x3d, 0x20, 0x43, - 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x78, 0x69, 0x73, 0x20, 0x3d, + 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x75, 0x20, 0x3d, 0x20, 0x30, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, + 0x61, 0x74, 0x20, 0x76, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, 0x43, 0x6f, 0x6f, + 0x72, 0x64, 0x73, 0x2e, 0x78, 0x20, 0x3e, 0x3d, 0x20, 0x43, 0x6f, 0x6f, + 0x72, 0x64, 0x73, 0x2e, 0x79, 0x20, 0x26, 0x26, 0x20, 0x43, 0x6f, 0x6f, + 0x72, 0x64, 0x73, 0x2e, 0x78, 0x20, 0x3e, 0x3d, 0x20, 0x43, 0x6f, 0x6f, + 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x68, 0x65, 0x72, 0x65, 0x20, + 0x77, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, 0x2b, 0x2d, 0x58, 0x20, 0x66, + 0x61, 0x63, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x43, 0x75, 0x62, 0x65, 0x46, 0x61, 0x63, 0x65, + 0x20, 0x3d, 0x20, 0x50, 0x2e, 0x78, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x20, + 0x3f, 0x20, 0x30, 0x20, 0x3a, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x78, 0x69, 0x73, 0x20, + 0x3d, 0x20, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x78, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x75, 0x20, 0x3d, 0x20, 0x50, 0x2e, 0x78, 0x20, 0x3e, 0x3d, 0x20, 0x30, + 0x20, 0x3f, 0x20, 0x2d, 0x50, 0x2e, 0x7a, 0x20, 0x3a, 0x20, 0x50, 0x2e, + 0x7a, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x20, 0x3d, 0x20, 0x2d, 0x50, 0x2e, 0x79, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x68, 0x65, 0x72, - 0x65, 0x20, 0x77, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, 0x2b, 0x2d, 0x58, + 0x65, 0x20, 0x77, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, 0x2b, 0x2d, 0x59, 0x20, 0x66, 0x61, 0x63, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x28, 0x43, 0x6f, + 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x79, 0x20, 0x3e, 0x3d, 0x20, 0x43, 0x6f, + 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x78, 0x20, 0x26, 0x26, 0x20, 0x43, 0x6f, + 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x79, 0x20, 0x3e, 0x3d, 0x20, 0x43, 0x6f, + 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x43, 0x75, 0x62, 0x65, 0x46, 0x61, - 0x63, 0x65, 0x20, 0x3d, 0x20, 0x50, 0x2e, 0x78, 0x20, 0x3e, 0x3d, 0x20, - 0x30, 0x20, 0x3f, 0x20, 0x30, 0x20, 0x3a, 0x20, 0x31, 0x3b, 0x0a, 0x20, + 0x63, 0x65, 0x20, 0x3d, 0x20, 0x50, 0x2e, 0x79, 0x20, 0x3e, 0x3d, 0x20, + 0x30, 0x20, 0x3f, 0x20, 0x32, 0x20, 0x3a, 0x20, 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x78, 0x69, - 0x73, 0x20, 0x3d, 0x20, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x78, + 0x73, 0x20, 0x3d, 0x20, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x75, 0x20, 0x3d, 0x20, 0x50, 0x2e, 0x78, 0x20, 0x3e, 0x3d, - 0x20, 0x30, 0x20, 0x3f, 0x20, 0x2d, 0x50, 0x2e, 0x7a, 0x20, 0x3a, 0x20, - 0x50, 0x2e, 0x7a, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x75, 0x20, 0x3d, 0x20, 0x50, 0x2e, 0x78, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x20, 0x3d, 0x20, 0x50, 0x2e, 0x79, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x20, + 0x3f, 0x20, 0x50, 0x2e, 0x7a, 0x20, 0x3a, 0x20, 0x2d, 0x50, 0x2e, 0x7a, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x68, 0x65, + 0x72, 0x65, 0x20, 0x77, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, 0x2b, 0x2d, + 0x5a, 0x20, 0x66, 0x61, 0x63, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x43, 0x75, 0x62, 0x65, 0x46, 0x61, + 0x63, 0x65, 0x20, 0x3d, 0x20, 0x50, 0x2e, 0x7a, 0x20, 0x3e, 0x3d, 0x20, + 0x30, 0x20, 0x3f, 0x20, 0x34, 0x20, 0x3a, 0x20, 0x35, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x78, 0x69, + 0x73, 0x20, 0x3d, 0x20, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x75, 0x20, 0x3d, 0x20, 0x50, 0x2e, 0x7a, 0x20, 0x3e, 0x3d, + 0x20, 0x30, 0x20, 0x3f, 0x20, 0x50, 0x2e, 0x78, 0x20, 0x3a, 0x20, 0x2d, + 0x50, 0x2e, 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x20, 0x3d, 0x20, 0x2d, 0x50, 0x2e, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x68, - 0x65, 0x72, 0x65, 0x20, 0x77, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, 0x2b, - 0x2d, 0x59, 0x20, 0x66, 0x61, 0x63, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x28, - 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x79, 0x20, 0x3e, 0x3d, 0x20, - 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x78, 0x20, 0x26, 0x26, 0x20, - 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x79, 0x20, 0x3e, 0x3d, 0x20, - 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x43, 0x75, 0x62, 0x65, - 0x46, 0x61, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x50, 0x2e, 0x79, 0x20, 0x3e, - 0x3d, 0x20, 0x30, 0x20, 0x3f, 0x20, 0x32, 0x20, 0x3a, 0x20, 0x33, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, - 0x78, 0x69, 0x73, 0x20, 0x3d, 0x20, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x73, - 0x2e, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x75, 0x20, 0x3d, 0x20, 0x50, 0x2e, 0x78, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x76, 0x20, 0x3d, 0x20, 0x50, 0x2e, 0x79, 0x20, 0x3e, 0x3d, 0x20, - 0x30, 0x20, 0x3f, 0x20, 0x50, 0x2e, 0x7a, 0x20, 0x3a, 0x20, 0x2d, 0x50, - 0x2e, 0x7a, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, - 0x68, 0x65, 0x72, 0x65, 0x20, 0x77, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, - 0x2b, 0x2d, 0x5a, 0x20, 0x66, 0x61, 0x63, 0x65, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x43, 0x75, 0x62, 0x65, - 0x46, 0x61, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x50, 0x2e, 0x7a, 0x20, 0x3e, - 0x3d, 0x20, 0x30, 0x20, 0x3f, 0x20, 0x34, 0x20, 0x3a, 0x20, 0x35, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, - 0x78, 0x69, 0x73, 0x20, 0x3d, 0x20, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x73, - 0x2e, 0x7a, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x75, 0x20, 0x3d, 0x20, 0x50, 0x2e, 0x7a, 0x20, - 0x3e, 0x3d, 0x20, 0x30, 0x20, 0x3f, 0x20, 0x50, 0x2e, 0x78, 0x20, 0x3a, - 0x20, 0x2d, 0x50, 0x2e, 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x20, 0x3d, 0x20, 0x2d, - 0x50, 0x2e, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, - 0x20, 0x3d, 0x20, 0x30, 0x2e, 0x35, 0x20, 0x2a, 0x20, 0x28, 0x75, 0x2f, - 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x78, - 0x69, 0x73, 0x20, 0x2b, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x20, 0x3d, 0x20, 0x30, 0x2e, 0x35, - 0x20, 0x2a, 0x20, 0x28, 0x76, 0x2f, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x78, 0x69, 0x73, 0x20, 0x2b, 0x20, 0x31, - 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, - 0x28, 0x75, 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x43, 0x75, 0x62, 0x65, 0x46, - 0x61, 0x63, 0x65, 0x20, 0x2b, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x20, 0x2a, 0x20, 0x36, 0x29, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, - 0x75, 0x63, 0x74, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, - 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, - 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, - 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, - 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, 0x20, 0x73, - 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, - 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, - 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x73, - 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x2c, 0x20, 0x66, 0x6c, - 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, - 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, - 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, - 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, - 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, - 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, - 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, - 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x3d, 0x20, - 0x43, 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, 0x32, 0x44, 0x41, - 0x72, 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, 0x63, 0x6f, 0x6f, - 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x73, - 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, - 0x72, 0x64, 0x73, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x29, 0x3b, - 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, - 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, - 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, - 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, 0x20, - 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x74, 0x65, 0x78, 0x74, 0x75, - 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, - 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, - 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x2c, 0x20, 0x66, - 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, - 0x20, 0x62, 0x69, 0x61, 0x73, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, - 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, - 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, - 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, - 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, - 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, - 0x61, 0x79, 0x2c, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, - 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, - 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x3d, 0x20, - 0x43, 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, 0x32, 0x44, 0x41, - 0x72, 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, 0x63, 0x6f, 0x6f, - 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x73, - 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, - 0x72, 0x64, 0x73, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x2c, 0x20, - 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x3b, 0x0a, 0x23, 0x65, - 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x20, 0x3d, + 0x20, 0x30, 0x2e, 0x35, 0x20, 0x2a, 0x20, 0x28, 0x75, 0x2f, 0x50, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x78, 0x69, 0x73, + 0x20, 0x2b, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x76, 0x20, 0x3d, 0x20, 0x30, 0x2e, 0x35, 0x20, 0x2a, + 0x20, 0x28, 0x76, 0x2f, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x41, 0x78, 0x69, 0x73, 0x20, 0x2b, 0x20, 0x31, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x28, 0x75, + 0x2c, 0x20, 0x76, 0x2c, 0x20, 0x43, 0x75, 0x62, 0x65, 0x46, 0x61, 0x63, + 0x65, 0x20, 0x2b, 0x20, 0x41, 0x72, 0x72, 0x61, 0x79, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x20, 0x2a, 0x20, 0x36, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x63, 0x75, + 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, @@ -5707,8 +7814,42 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6c, 0x65, - 0x76, 0x65, 0x6c, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, + 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, + 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, + 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x73, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, + 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3b, 0x0a, + 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, + 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x3d, 0x20, 0x43, 0x75, + 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, 0x32, 0x44, 0x41, 0x72, 0x72, + 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, + 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, + 0x73, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x63, + 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x29, 0x3b, 0x0a, 0x23, + 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, + 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, + 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, 0x20, 0x73, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, + 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, + 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x73, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x2c, 0x20, 0x66, 0x6c, 0x6f, + 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x62, + 0x69, 0x61, 0x73, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, @@ -5744,461 +7885,6 @@ unsigned char ue4_stdlib_metal[] = { 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x67, 0x72, 0x61, 0x64, - 0x69, 0x65, 0x6e, 0x74, 0x63, 0x75, 0x62, 0x65, 0x20, 0x6f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, - 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, - 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, - 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, - 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, - 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, - 0x20, 0x3d, 0x20, 0x43, 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, - 0x32, 0x44, 0x41, 0x72, 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, - 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, - 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, - 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, - 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, - 0x29, 0x2c, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x3b, - 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, - 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x31, 0x36, - 0x42, 0x5f, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x53, 0x5f, 0x5f, 0x29, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, - 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, - 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, - 0x54, 0x2c, 0x20, 0x34, 0x3e, 0x20, 0x72, 0x65, 0x61, 0x64, 0x28, 0x74, - 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, - 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, - 0x65, 0x78, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x20, - 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, - 0x74, 0x20, 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, - 0x72, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x75, 0x73, - 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6c, 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x30, - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, - 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, - 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, - 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, - 0x65, 0x78, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x66, 0x61, 0x63, 0x65, - 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6c, 0x6f, 0x64, - 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x72, 0x65, 0x61, 0x64, - 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x28, 0x28, 0x61, 0x72, - 0x72, 0x61, 0x79, 0x20, 0x2a, 0x20, 0x36, 0x29, 0x20, 0x2b, 0x20, 0x66, - 0x61, 0x63, 0x65, 0x29, 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, - 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, - 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, - 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, - 0x54, 0x2c, 0x20, 0x34, 0x3e, 0x20, 0x72, 0x65, 0x61, 0x64, 0x28, 0x74, - 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, - 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, - 0x65, 0x78, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x63, 0x6f, - 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x61, - 0x63, 0x65, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, - 0x61, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x64, - 0x20, 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, - 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, - 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, - 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, - 0x20, 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, - 0x72, 0x65, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, - 0x28, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x2a, 0x20, 0x36, 0x29, - 0x20, 0x2b, 0x20, 0x66, 0x61, 0x63, 0x65, 0x29, 0x2c, 0x20, 0x6c, 0x6f, - 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, - 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, - 0x5f, 0x31, 0x36, 0x42, 0x5f, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x53, 0x5f, - 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, - 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, - 0x6f, 0x69, 0x64, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x74, 0x65, - 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, - 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, - 0x78, 0x2c, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, - 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, - 0x72, 0x74, 0x32, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, - 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, - 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, - 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6c, 0x6f, 0x64, - 0x20, 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, - 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, - 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x2e, - 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, - 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x66, 0x61, 0x63, 0x65, - 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6c, 0x6f, 0x64, - 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, - 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, - 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x28, 0x28, 0x61, - 0x72, 0x72, 0x61, 0x79, 0x20, 0x2a, 0x20, 0x36, 0x29, 0x20, 0x2b, 0x20, - 0x66, 0x61, 0x63, 0x65, 0x29, 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, 0x3b, - 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, - 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, - 0x5f, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x53, - 0x5f, 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, - 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x20, 0x61, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, - 0x61, 0x6d, 0x65, 0x20, 0x45, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, - 0x61, 0x6d, 0x65, 0x20, 0x4c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x76, 0x6f, 0x69, 0x64, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x74, - 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, - 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, - 0x65, 0x78, 0x2c, 0x20, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x3c, 0x45, 0x2c, 0x20, - 0x4c, 0x3e, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2c, 0x20, 0x75, 0x73, - 0x68, 0x6f, 0x72, 0x74, 0x32, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, - 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x66, 0x61, 0x63, 0x65, - 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x61, 0x72, 0x72, - 0x61, 0x79, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6c, - 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, - 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, - 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, - 0x78, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x73, 0x6c, 0x69, 0x63, - 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x66, 0x61, - 0x63, 0x65, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6c, - 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, - 0x65, 0x78, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x73, 0x6c, 0x69, - 0x63, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x28, - 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x2a, 0x20, 0x36, 0x29, 0x20, - 0x2b, 0x20, 0x66, 0x61, 0x63, 0x65, 0x29, 0x2c, 0x20, 0x6c, 0x6f, 0x64, - 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, - 0x69, 0x66, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, - 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, - 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, - 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x77, - 0x72, 0x69, 0x74, 0x65, 0x28, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, - 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, - 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x76, 0x65, - 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, 0x20, 0x63, 0x6f, 0x6c, 0x6f, - 0x72, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x63, 0x6f, 0x6f, - 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x61, 0x63, - 0x65, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, - 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x64, 0x20, - 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, - 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, - 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x77, - 0x72, 0x69, 0x74, 0x65, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, - 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x66, 0x61, 0x63, 0x65, 0x2c, - 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, - 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x2e, - 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, - 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x28, 0x28, 0x61, 0x72, - 0x72, 0x61, 0x79, 0x20, 0x2a, 0x20, 0x36, 0x29, 0x20, 0x2b, 0x20, 0x66, - 0x61, 0x63, 0x65, 0x29, 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, - 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, - 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, - 0x49, 0x4d, 0x41, 0x47, 0x45, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x53, 0x5f, - 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x20, 0x61, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, - 0x6d, 0x65, 0x20, 0x45, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, - 0x6d, 0x65, 0x20, 0x4c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, - 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, - 0x6f, 0x69, 0x64, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x74, 0x65, - 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, - 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, - 0x78, 0x2c, 0x20, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x3c, 0x45, 0x2c, 0x20, 0x4c, - 0x3e, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2c, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x32, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x20, 0x6c, 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, - 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, - 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x74, 0x65, 0x78, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x73, - 0x6c, 0x69, 0x63, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, - 0x20, 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, - 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, - 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, - 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, - 0x2c, 0x20, 0x28, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x2a, 0x20, - 0x36, 0x29, 0x20, 0x2b, 0x20, 0x66, 0x61, 0x63, 0x65, 0x29, 0x2c, 0x20, - 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x23, - 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, - 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, - 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, 0x20, 0x67, 0x61, 0x74, - 0x68, 0x65, 0x72, 0x28, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, - 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, - 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x73, 0x61, 0x6d, - 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, - 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x63, 0x6f, - 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x20, 0x3d, 0x20, - 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x3a, 0x3a, 0x78, - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, - 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, - 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, - 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, - 0x65, 0x78, 0x2e, 0x67, 0x61, 0x74, 0x68, 0x65, 0x72, 0x28, 0x73, 0x2c, - 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, - 0x79, 0x2c, 0x20, 0x63, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, - 0x64, 0x73, 0x20, 0x3d, 0x20, 0x43, 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, - 0x54, 0x6f, 0x32, 0x44, 0x41, 0x72, 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, - 0x65, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, - 0x61, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, - 0x74, 0x65, 0x78, 0x2e, 0x67, 0x61, 0x74, 0x68, 0x65, 0x72, 0x28, 0x73, - 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x78, 0x79, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, - 0x2e, 0x7a, 0x29, 0x2c, 0x20, 0x63, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, - 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x23, - 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, - 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, - 0x45, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x57, 0x52, 0x49, 0x54, 0x45, 0x5f, - 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, - 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, - 0x6f, 0x69, 0x64, 0x20, 0x66, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x74, 0x65, - 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, - 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, - 0x78, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x74, 0x65, 0x78, 0x2e, 0x66, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x29, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, - 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, - 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, - 0x74, 0x68, 0x28, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, - 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, - 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x6c, 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, - 0x69, 0x64, 0x74, 0x68, 0x28, 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, - 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, - 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x67, 0x65, - 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x74, 0x65, 0x78, - 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, - 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, - 0x2c, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x64, 0x20, 0x3d, 0x20, - 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, - 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x6c, - 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, - 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x72, 0x72, 0x61, - 0x79, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x74, 0x65, 0x78, 0x74, 0x75, - 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, - 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x29, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, - 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, - 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, - 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, - 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5f, 0x73, - 0x69, 0x7a, 0x65, 0x28, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x74, 0x65, 0x78, - 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5f, 0x73, - 0x69, 0x7a, 0x65, 0x28, 0x29, 0x20, 0x2f, 0x20, 0x36, 0x29, 0x3b, 0x0a, - 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, - 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, - 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x75, - 0x6d, 0x5f, 0x6d, 0x69, 0x70, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, - 0x28, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, - 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, - 0x20, 0x74, 0x65, 0x78, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, - 0x65, 0x78, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, - 0x69, 0x70, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x28, 0x29, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x64, 0x65, - 0x70, 0x74, 0x68, 0x5f, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, - 0x61, 0x79, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, - 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, - 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, - 0x34, 0x3e, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x64, 0x65, - 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, - 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, - 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x2c, 0x20, - 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, - 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, - 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, - 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, - 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, - 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, - 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, - 0x79, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, - 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x20, - 0x3d, 0x20, 0x43, 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, 0x32, - 0x44, 0x41, 0x72, 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, 0x63, - 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, - 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, - 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, - 0x6e, 0x74, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, - 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, - 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, - 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x34, - 0x3e, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x64, 0x65, 0x70, - 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, - 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, - 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x2c, 0x20, 0x66, - 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, - 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, - 0x20, 0x62, 0x69, 0x61, 0x73, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, - 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, - 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, - 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, - 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, - 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, - 0x61, 0x79, 0x2c, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, - 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, - 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x3d, 0x20, - 0x43, 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, 0x32, 0x44, 0x41, - 0x72, 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, 0x63, 0x6f, 0x6f, - 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x73, - 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, - 0x72, 0x64, 0x73, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x2c, 0x20, - 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x3b, 0x0a, 0x23, 0x65, - 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, - 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, - 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, 0x20, 0x73, 0x61, 0x6d, - 0x70, 0x6c, 0x65, 0x28, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, - 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, - 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, - 0x65, 0x72, 0x20, 0x73, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, - 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, @@ -6231,60 +7917,403 @@ unsigned char ue4_stdlib_metal[] = { 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, - 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, - 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, - 0x78, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, - 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, - 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, - 0x61, 0x79, 0x2c, 0x20, 0x67, 0x72, 0x61, 0x64, 0x69, 0x65, 0x6e, 0x74, - 0x63, 0x75, 0x62, 0x65, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, - 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, - 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, - 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, - 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, - 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, - 0x79, 0x2c, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x3b, + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, + 0x74, 0x65, 0x78, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, + 0x20, 0x73, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, + 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x67, 0x72, 0x61, 0x64, 0x69, 0x65, + 0x6e, 0x74, 0x63, 0x75, 0x62, 0x65, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, + 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, + 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, + 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, + 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, + 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x3d, + 0x20, 0x43, 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, 0x32, 0x44, + 0x41, 0x72, 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, 0x63, 0x6f, + 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, + 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, + 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x2c, + 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x3b, 0x0a, 0x23, + 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, + 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x31, 0x36, 0x42, 0x5f, + 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x53, 0x5f, 0x5f, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, + 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, + 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, + 0x20, 0x34, 0x3e, 0x20, 0x72, 0x65, 0x61, 0x64, 0x28, 0x74, 0x65, 0x78, + 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, + 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x20, 0x63, 0x6f, + 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, + 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x20, 0x6c, 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x30, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, + 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, + 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, + 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, + 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, - 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x3d, 0x20, 0x43, - 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, 0x32, 0x44, 0x41, 0x72, - 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, 0x63, 0x6f, 0x6f, 0x72, - 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x63, + 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x28, 0x28, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x20, 0x2a, 0x20, 0x36, 0x29, 0x20, 0x2b, 0x20, 0x66, 0x61, 0x63, + 0x65, 0x29, 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, + 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, + 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, + 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, + 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, + 0x20, 0x34, 0x3e, 0x20, 0x72, 0x65, 0x61, 0x64, 0x28, 0x74, 0x65, 0x78, + 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x63, 0x6f, 0x6f, 0x72, + 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x61, 0x63, 0x65, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x64, 0x20, 0x3d, + 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, + 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, + 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x74, 0x65, 0x78, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x66, 0x61, + 0x63, 0x65, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6c, + 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, - 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, - 0x64, 0x73, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, - 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x2c, 0x20, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x72, 0x65, + 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x28, 0x28, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x2a, 0x20, 0x36, 0x29, 0x20, 0x2b, + 0x20, 0x66, 0x61, 0x63, 0x65, 0x29, 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, + 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, + 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x31, + 0x36, 0x42, 0x5f, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x53, 0x5f, 0x5f, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, + 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, + 0x64, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x74, 0x65, 0x78, 0x74, + 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, + 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, 0x20, 0x63, + 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x32, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x20, 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, 0x75, 0x73, + 0x68, 0x6f, 0x72, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, + 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6c, 0x6f, 0x64, 0x20, 0x3d, + 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, + 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, + 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x63, + 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, 0x3b, + 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, + 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x28, 0x28, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x20, 0x2a, 0x20, 0x36, 0x29, 0x20, 0x2b, 0x20, 0x66, 0x61, + 0x63, 0x65, 0x29, 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x23, + 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, + 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x49, + 0x4d, 0x41, 0x47, 0x45, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x53, 0x5f, 0x5f, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x20, 0x61, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, + 0x65, 0x20, 0x45, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, + 0x65, 0x20, 0x4c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, + 0x69, 0x64, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x74, 0x65, 0x78, + 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, + 0x2c, 0x20, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x3c, 0x45, 0x2c, 0x20, 0x4c, 0x3e, + 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x32, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, + 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, + 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6c, 0x6f, 0x64, + 0x20, 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, + 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, + 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x2e, + 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2c, + 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x66, 0x61, 0x63, 0x65, + 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6c, 0x6f, 0x64, + 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, + 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x73, 0x6c, 0x69, 0x63, 0x65, + 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x28, 0x28, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x20, 0x2a, 0x20, 0x36, 0x29, 0x20, 0x2b, 0x20, + 0x66, 0x61, 0x63, 0x65, 0x29, 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, 0x3b, + 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, + 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, + 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x28, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, + 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, + 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x76, 0x65, 0x63, 0x3c, + 0x54, 0x2c, 0x20, 0x34, 0x3e, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x61, 0x63, 0x65, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x64, 0x20, 0x3d, 0x20, + 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, + 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, + 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x63, 0x6f, + 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, + 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x28, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x63, + 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x28, 0x28, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x20, 0x2a, 0x20, 0x36, 0x29, 0x20, 0x2b, 0x20, 0x66, 0x61, 0x63, + 0x65, 0x29, 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, + 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x49, 0x4d, + 0x41, 0x47, 0x45, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x53, 0x5f, 0x5f, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, + 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x20, 0x61, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x20, 0x45, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x20, 0x4c, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, + 0x64, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x74, 0x65, 0x78, 0x74, + 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, + 0x20, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, + 0x73, 0x6c, 0x69, 0x63, 0x65, 0x3c, 0x45, 0x2c, 0x20, 0x4c, 0x3e, 0x20, + 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, + 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x6c, 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, + 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, + 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x65, 0x78, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x73, 0x6c, 0x69, + 0x63, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x66, + 0x61, 0x63, 0x65, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, + 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x65, 0x78, 0x2e, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x73, 0x6c, + 0x69, 0x63, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, + 0x28, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x2a, 0x20, 0x36, 0x29, + 0x20, 0x2b, 0x20, 0x66, 0x61, 0x63, 0x65, 0x29, 0x2c, 0x20, 0x6c, 0x6f, + 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, - 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, - 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, - 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x73, 0x61, 0x6d, - 0x70, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x28, - 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, - 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, - 0x78, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, - 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, - 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, - 0x61, 0x79, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, - 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, - 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, - 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, + 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, + 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, 0x20, 0x67, 0x61, 0x74, 0x68, 0x65, + 0x72, 0x28, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, + 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, + 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x72, 0x20, 0x73, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, + 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x63, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x20, 0x3d, 0x20, 0x63, 0x6f, + 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x3a, 0x3a, 0x78, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, + 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, + 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, + 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, + 0x2e, 0x67, 0x61, 0x74, 0x68, 0x65, 0x72, 0x28, 0x73, 0x2c, 0x20, 0x63, + 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, + 0x20, 0x63, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, + 0x20, 0x3d, 0x20, 0x43, 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, + 0x32, 0x44, 0x41, 0x72, 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, + 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, - 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6d, - 0x70, 0x61, 0x72, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, - 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, + 0x78, 0x2e, 0x67, 0x61, 0x74, 0x68, 0x65, 0x72, 0x28, 0x73, 0x2c, 0x20, + 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, + 0x29, 0x2c, 0x20, 0x63, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, + 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x23, 0x69, 0x66, + 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, + 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, + 0x52, 0x45, 0x41, 0x44, 0x57, 0x52, 0x49, 0x54, 0x45, 0x5f, 0x5f, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, + 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, + 0x64, 0x20, 0x66, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x74, 0x65, 0x78, 0x74, + 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x65, 0x78, 0x2e, 0x66, 0x65, 0x6e, 0x63, 0x65, 0x28, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x23, 0x65, + 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x28, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, + 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, + 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6c, + 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x74, 0x65, 0x78, 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x28, 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, + 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, + 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x67, 0x65, 0x74, 0x5f, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x74, 0x65, 0x78, 0x74, 0x75, + 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x30, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x67, 0x65, + 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x6c, 0x6f, 0x64, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, + 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5f, + 0x73, 0x69, 0x7a, 0x65, 0x28, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, + 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, + 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, + 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, + 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x67, + 0x65, 0x74, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5f, 0x73, 0x69, 0x7a, + 0x65, 0x28, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x74, 0x65, 0x78, 0x2e, 0x67, + 0x65, 0x74, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5f, 0x73, 0x69, 0x7a, + 0x65, 0x28, 0x29, 0x20, 0x2f, 0x20, 0x36, 0x29, 0x3b, 0x0a, 0x23, 0x65, + 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, + 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, + 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, + 0x6d, 0x69, 0x70, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x28, 0x74, + 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, + 0x65, 0x78, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, + 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x69, 0x70, + 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x28, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x64, 0x65, 0x70, 0x74, + 0x68, 0x5f, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, + 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, + 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, + 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x64, 0x65, 0x70, 0x74, + 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, + 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x73, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x2c, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, + 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, + 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, + 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, + 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, + 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x3d, 0x20, @@ -6293,74 +8322,253 @@ unsigned char ue4_stdlib_metal[] = { 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x73, - 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, - 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, - 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x63, 0x6f, 0x6f, - 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x2c, 0x20, 0x63, 0x6f, 0x6d, 0x70, - 0x61, 0x72, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, - 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, - 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, - 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, - 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x72, 0x65, 0x28, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, - 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, - 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, - 0x72, 0x20, 0x73, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, - 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, - 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, - 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x2c, 0x20, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x20, 0x6f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, - 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, - 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, - 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x28, 0x73, - 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, - 0x61, 0x79, 0x2c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, - 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, - 0x20, 0x3d, 0x20, 0x43, 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, - 0x32, 0x44, 0x41, 0x72, 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, - 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, - 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, + 0x72, 0x64, 0x73, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x29, 0x3b, + 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, + 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, + 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, 0x20, + 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x64, 0x65, 0x70, 0x74, 0x68, + 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, + 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x73, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x2c, 0x20, 0x66, 0x6c, 0x6f, + 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x62, + 0x69, 0x61, 0x73, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, + 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, + 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, + 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, - 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6d, - 0x70, 0x61, 0x72, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, - 0x64, 0x73, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, - 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x2c, 0x20, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, + 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x3b, 0x0a, - 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x31, 0x36, 0x42, - 0x5f, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x53, 0x5f, 0x5f, 0x29, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, - 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, - 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, - 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, - 0x2c, 0x20, 0x34, 0x3e, 0x20, 0x72, 0x65, 0x61, 0x64, 0x28, 0x64, 0x65, + 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, + 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x3d, 0x20, 0x43, 0x75, + 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, 0x32, 0x44, 0x41, 0x72, 0x72, + 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, + 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, + 0x73, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x63, + 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x2c, 0x20, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, + 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, + 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, + 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x28, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, + 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, + 0x74, 0x65, 0x78, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, + 0x20, 0x73, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, + 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x20, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, + 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, + 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, + 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, + 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, + 0x72, 0x64, 0x73, 0x20, 0x3d, 0x20, 0x43, 0x75, 0x62, 0x65, 0x6d, 0x61, + 0x70, 0x54, 0x6f, 0x32, 0x44, 0x41, 0x72, 0x72, 0x61, 0x79, 0x46, 0x61, + 0x63, 0x65, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, + 0x72, 0x61, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, + 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x78, 0x79, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, + 0x73, 0x2e, 0x7a, 0x29, 0x2c, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, + 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, + 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, + 0x34, 0x3e, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, - 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x20, 0x63, 0x6f, 0x6f, - 0x72, 0x64, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x66, - 0x61, 0x63, 0x65, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, - 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, + 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x2c, 0x20, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x2c, 0x20, 0x67, 0x72, 0x61, 0x64, 0x69, 0x65, 0x6e, 0x74, 0x63, 0x75, + 0x62, 0x65, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, + 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, + 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, + 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, + 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, + 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, + 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x3b, 0x0a, 0x23, + 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, + 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x3d, 0x20, 0x43, 0x75, 0x62, + 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, 0x32, 0x44, 0x41, 0x72, 0x72, 0x61, + 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, + 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x63, 0x6f, + 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x2c, 0x20, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, + 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, + 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, + 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x28, 0x64, 0x65, + 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, + 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x2c, 0x20, + 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x72, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, + 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, + 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, + 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x72, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x72, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, + 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, + 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x3d, 0x20, 0x43, 0x75, + 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, 0x32, 0x44, 0x41, 0x72, 0x72, + 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, + 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x28, + 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x78, 0x79, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, + 0x73, 0x2e, 0x7a, 0x29, 0x2c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, + 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x23, 0x65, + 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, + 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, + 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x73, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, + 0x28, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, + 0x65, 0x78, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, + 0x73, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, + 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, + 0x72, 0x61, 0x79, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x2c, 0x20, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x20, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, + 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, + 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x28, 0x73, 0x2c, 0x20, + 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x2c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x2c, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, + 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x3d, + 0x20, 0x43, 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, 0x32, 0x44, + 0x41, 0x72, 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, 0x63, 0x6f, + 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, + 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x72, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, + 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x63, 0x6f, + 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x2c, 0x20, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2c, 0x20, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x3b, 0x0a, 0x23, 0x65, + 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, + 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, + 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x31, 0x36, 0x42, 0x5f, 0x43, + 0x4f, 0x4f, 0x52, 0x44, 0x53, 0x5f, 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, + 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, + 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, + 0x34, 0x3e, 0x20, 0x72, 0x65, 0x61, 0x64, 0x28, 0x64, 0x65, 0x70, 0x74, + 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, + 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x75, + 0x73, 0x68, 0x6f, 0x72, 0x74, 0x32, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, + 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x66, 0x61, 0x63, + 0x65, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x61, 0x72, + 0x72, 0x61, 0x79, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, + 0x6c, 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, + 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, + 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x72, 0x65, + 0x61, 0x64, 0x28, 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, + 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, + 0x65, 0x78, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x63, 0x6f, 0x6f, 0x72, + 0x64, 0x2c, 0x20, 0x28, 0x28, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x2a, + 0x20, 0x36, 0x29, 0x20, 0x2b, 0x20, 0x66, 0x61, 0x63, 0x65, 0x29, 0x2c, + 0x20, 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, + 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, + 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, + 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, + 0x20, 0x72, 0x65, 0x61, 0x64, 0x28, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, + 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, + 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x32, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, @@ -6376,74 +8584,104 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x2a, 0x20, 0x36, 0x29, 0x20, 0x2b, 0x20, 0x66, 0x61, 0x63, 0x65, 0x29, 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, - 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, - 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, - 0x34, 0x3e, 0x20, 0x72, 0x65, 0x61, 0x64, 0x28, 0x64, 0x65, 0x70, 0x74, - 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, - 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x32, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x61, 0x63, 0x65, 0x2c, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x30, 0x29, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, - 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, - 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, - 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, - 0x78, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, 0x66, 0x61, 0x63, 0x65, 0x2c, - 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, - 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x28, - 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x28, 0x28, 0x61, 0x72, 0x72, - 0x61, 0x79, 0x20, 0x2a, 0x20, 0x36, 0x29, 0x20, 0x2b, 0x20, 0x66, 0x61, - 0x63, 0x65, 0x29, 0x2c, 0x20, 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x23, - 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, - 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, - 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, 0x20, 0x67, 0x61, 0x74, 0x68, 0x65, - 0x72, 0x28, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, + 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, + 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, + 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, + 0x2c, 0x20, 0x34, 0x3e, 0x20, 0x67, 0x61, 0x74, 0x68, 0x65, 0x72, 0x28, + 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, + 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, + 0x78, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, + 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, + 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, 0x72, 0x72, + 0x61, 0x79, 0x2c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, + 0x74, 0x20, 0x63, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, + 0x65, 0x6e, 0x74, 0x3a, 0x3a, 0x78, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, + 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, + 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x67, 0x61, 0x74, + 0x68, 0x65, 0x72, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, + 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x63, 0x29, 0x3b, + 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, + 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x3d, 0x20, 0x43, + 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, 0x32, 0x44, 0x41, 0x72, + 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, 0x63, 0x6f, 0x6f, 0x72, + 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x67, 0x61, + 0x74, 0x68, 0x65, 0x72, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, + 0x64, 0x73, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, + 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x2c, 0x20, 0x63, + 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, + 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, + 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, + 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, 0x20, 0x67, + 0x61, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, + 0x65, 0x28, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, - 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, - 0x65, 0x6e, 0x74, 0x20, 0x63, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6d, 0x70, - 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x3a, 0x3a, 0x78, 0x29, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, - 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, - 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x67, - 0x61, 0x74, 0x68, 0x65, 0x72, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, - 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x63, - 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, - 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x3d, - 0x20, 0x43, 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, 0x32, 0x44, - 0x41, 0x72, 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, 0x63, 0x6f, - 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, - 0x67, 0x61, 0x74, 0x68, 0x65, 0x72, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, - 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x2c, - 0x20, 0x63, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, + 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, + 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x74, 0x65, 0x78, 0x2e, 0x67, 0x61, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, + 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x20, + 0x3d, 0x20, 0x43, 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, 0x6f, 0x32, + 0x44, 0x41, 0x72, 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, 0x28, 0x63, + 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, + 0x2e, 0x67, 0x61, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x72, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, + 0x73, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x28, 0x63, + 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x2c, 0x20, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x29, + 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, + 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, + 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x57, + 0x52, 0x49, 0x54, 0x45, 0x5f, 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, + 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x66, 0x65, 0x6e, + 0x63, 0x65, 0x28, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, + 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, + 0x20, 0x74, 0x65, 0x78, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x66, 0x65, 0x6e, 0x63, + 0x65, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, @@ -6452,43 +8690,16 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x29, 0x29, 0x20, 0x76, 0x65, 0x63, 0x3c, 0x54, 0x2c, 0x20, 0x34, 0x3e, - 0x20, 0x67, 0x61, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6d, 0x70, - 0x61, 0x72, 0x65, 0x28, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, - 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, - 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, - 0x65, 0x72, 0x20, 0x73, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, - 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, - 0x74, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, - 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, - 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x67, 0x61, 0x74, 0x68, 0x65, 0x72, - 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x28, 0x73, 0x2c, 0x20, - 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, - 0x2c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6f, 0x72, 0x64, - 0x73, 0x20, 0x3d, 0x20, 0x43, 0x75, 0x62, 0x65, 0x6d, 0x61, 0x70, 0x54, - 0x6f, 0x32, 0x44, 0x41, 0x72, 0x72, 0x61, 0x79, 0x46, 0x61, 0x63, 0x65, - 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x2c, 0x20, 0x61, 0x72, 0x72, 0x61, - 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, - 0x65, 0x78, 0x2e, 0x67, 0x61, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x72, 0x65, 0x28, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6f, - 0x72, 0x64, 0x73, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x28, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x7a, 0x29, 0x2c, 0x20, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, - 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, - 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, - 0x44, 0x57, 0x52, 0x49, 0x54, 0x45, 0x5f, 0x5f, 0x29, 0x0a, 0x20, 0x20, + 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x67, 0x65, 0x74, 0x5f, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, + 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, + 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x6c, 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x67, 0x65, 0x74, 0x5f, + 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, @@ -6496,231 +8707,172 @@ unsigned char ue4_stdlib_metal[] = { 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x66, - 0x65, 0x6e, 0x63, 0x65, 0x28, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, - 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, - 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x66, 0x65, - 0x6e, 0x63, 0x65, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, - 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, - 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x67, 0x65, - 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x64, 0x65, 0x70, 0x74, - 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, - 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, 0x20, 0x75, - 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x30, 0x29, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x67, + 0x65, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x64, 0x65, + 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x2c, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x64, 0x20, 0x3d, 0x20, 0x30, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x67, + 0x65, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x6c, 0x6f, + 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, + 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, + 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x48, + 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, 0x45, 0x5f, + 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x67, 0x65, + 0x74, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5f, 0x73, 0x69, 0x7a, 0x65, + 0x28, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x74, 0x65, 0x78, 0x2e, 0x67, 0x65, + 0x74, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5f, 0x73, 0x69, 0x7a, 0x65, + 0x28, 0x29, 0x20, 0x2f, 0x20, 0x36, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, + 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, + 0x69, 0x70, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x28, 0x64, 0x65, + 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, 0x67, 0x65, - 0x74, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x28, 0x6c, 0x6f, 0x64, 0x29, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, - 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, - 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, - 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, - 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, - 0x78, 0x2c, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x64, 0x20, 0x3d, - 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, - 0x2e, 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, - 0x6c, 0x6f, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, - 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x72, 0x72, - 0x61, 0x79, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x64, 0x65, 0x70, 0x74, - 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, - 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, 0x78, 0x29, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, - 0x20, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x55, 0x52, - 0x45, 0x5f, 0x43, 0x55, 0x42, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, - 0x67, 0x65, 0x74, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5f, 0x73, 0x69, - 0x7a, 0x65, 0x28, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x74, 0x65, 0x78, 0x2e, - 0x67, 0x65, 0x74, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5f, 0x73, 0x69, - 0x7a, 0x65, 0x28, 0x29, 0x20, 0x2f, 0x20, 0x36, 0x29, 0x3b, 0x0a, 0x23, - 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, - 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x2c, 0x20, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x20, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x75, 0x69, 0x6e, 0x74, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x75, 0x6d, - 0x5f, 0x6d, 0x69, 0x70, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x28, - 0x64, 0x65, 0x70, 0x74, 0x68, 0x63, 0x75, 0x62, 0x65, 0x5f, 0x61, 0x72, - 0x72, 0x61, 0x79, 0x3c, 0x54, 0x2c, 0x20, 0x61, 0x3e, 0x20, 0x74, 0x65, - 0x78, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x2e, - 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x69, 0x70, 0x5f, - 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x7d, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x23, 0x70, 0x72, 0x61, 0x67, 0x6d, - 0x61, 0x20, 0x6d, 0x61, 0x72, 0x6b, 0x20, 0x2d, 0x2d, 0x20, 0x57, 0x61, - 0x76, 0x65, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x20, 0x2d, 0x2d, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, - 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x51, - 0x55, 0x41, 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x5f, 0x29, 0x20, - 0x7c, 0x7c, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, - 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x53, 0x49, 0x4d, 0x44, 0x47, 0x52, - 0x4f, 0x55, 0x50, 0x5f, 0x5f, 0x29, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, - 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, - 0x45, 0x5f, 0x53, 0x49, 0x4d, 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, - 0x5f, 0x29, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x64, - 0x65, 0x63, 0x6c, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x5f, 0x76, 0x61, 0x72, 0x73, 0x20, 0x20, 0x20, 0x20, 0x75, - 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, - 0x6d, 0x64, 0x20, 0x5b, 0x5b, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, - 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x20, 0x5d, 0x5d, 0x2c, 0x20, 0x5c, 0x0a, 0x75, 0x73, - 0x68, 0x6f, 0x72, 0x74, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x69, 0x64, - 0x20, 0x5b, 0x5b, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x69, 0x6d, 0x64, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x64, 0x65, - 0x66, 0x69, 0x6e, 0x65, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, - 0x65, 0x61, 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, - 0x65, 0x28, 0x29, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, - 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x67, 0x65, 0x74, - 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x20, - 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x69, 0x64, 0x0a, 0x23, 0x65, 0x6c, 0x69, - 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, - 0x48, 0x41, 0x56, 0x45, 0x5f, 0x51, 0x55, 0x41, 0x44, 0x47, 0x52, 0x4f, - 0x55, 0x50, 0x5f, 0x5f, 0x29, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, - 0x65, 0x20, 0x64, 0x65, 0x63, 0x6c, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x5f, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x76, 0x61, 0x72, 0x73, 0x20, 0x75, - 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x69, - 0x64, 0x20, 0x5b, 0x5b, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x71, 0x75, 0x61, - 0x64, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x64, - 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, - 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, - 0x76, 0x65, 0x28, 0x29, 0x20, 0x34, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, + 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x69, 0x70, 0x5f, 0x6c, 0x65, + 0x76, 0x65, 0x6c, 0x73, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, + 0x0a, 0x7d, 0x0a, 0x0a, 0x23, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x20, + 0x6d, 0x61, 0x72, 0x6b, 0x20, 0x2d, 0x2d, 0x20, 0x57, 0x61, 0x76, 0x65, + 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x2d, + 0x2d, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x51, 0x55, 0x41, + 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x5f, 0x29, 0x20, 0x7c, 0x7c, + 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, + 0x41, 0x56, 0x45, 0x5f, 0x53, 0x49, 0x4d, 0x44, 0x47, 0x52, 0x4f, 0x55, + 0x50, 0x5f, 0x5f, 0x29, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, + 0x53, 0x49, 0x4d, 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x5f, 0x29, + 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x65, 0x63, + 0x6c, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x5f, 0x76, 0x61, 0x72, 0x73, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, + 0x20, 0x5b, 0x5b, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, + 0x70, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x20, 0x5d, 0x5d, 0x2c, 0x20, 0x5c, 0x0a, 0x75, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x69, 0x64, 0x20, 0x5b, + 0x5b, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, - 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x77, - 0x61, 0x76, 0x65, 0x28, 0x29, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x69, - 0x64, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x0a, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x75, 0x65, 0x34, 0x0a, - 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, - 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, - 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x29, 0x29, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x77, 0x61, 0x76, 0x65, - 0x5f, 0x6f, 0x6e, 0x63, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, - 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, - 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, - 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, - 0x5f, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, - 0x69, 0x6d, 0x64, 0x5f, 0x69, 0x73, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, - 0x28, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, - 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x09, 0x0a, 0x09, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x6c, - 0x61, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x28, 0x29, 0x0a, - 0x09, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, - 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, - 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, - 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, - 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x76, 0x6f, - 0x74, 0x65, 0x3a, 0x3a, 0x76, 0x6f, 0x74, 0x65, 0x5f, 0x74, 0x20, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x28, 0x73, 0x69, 0x6d, 0x64, - 0x5f, 0x76, 0x6f, 0x74, 0x65, 0x3a, 0x3a, 0x76, 0x6f, 0x74, 0x65, 0x5f, - 0x74, 0x29, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, - 0x65, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, 0x6d, 0x61, - 0x73, 0x6b, 0x28, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x78, 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, - 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x79, 0x20, 0x3d, 0x20, 0x28, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3e, 0x3e, 0x20, 0x33, 0x32, 0x29, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, - 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x70, 0x6f, 0x70, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x28, 0x78, 0x29, 0x20, 0x2b, 0x20, 0x70, 0x6f, 0x70, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x28, 0x79, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, - 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x29, 0x30, 0x3b, 0x0a, 0x23, 0x65, - 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x0a, 0x09, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, - 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x6c, 0x61, - 0x6e, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x28, 0x29, 0x0a, 0x09, - 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, - 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, - 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, - 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, - 0x5f, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, - 0x69, 0x6d, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x65, - 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x73, 0x75, 0x6d, - 0x28, 0x31, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, - 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x75, 0x69, 0x6e, - 0x74, 0x29, 0x30, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x09, 0x7d, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, - 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, + 0x29, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x0a, 0x23, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, + 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, + 0x69, 0x6e, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x20, 0x73, 0x69, + 0x6d, 0x64, 0x5f, 0x69, 0x64, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, + 0x56, 0x45, 0x5f, 0x51, 0x55, 0x41, 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, + 0x5f, 0x5f, 0x29, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, + 0x64, 0x65, 0x63, 0x6c, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x5f, 0x76, 0x61, 0x72, 0x73, 0x20, 0x75, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x69, 0x64, 0x20, + 0x5b, 0x5b, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x71, 0x75, 0x61, 0x64, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x20, 0x5d, 0x5d, 0x0a, 0x23, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, + 0x61, 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, + 0x28, 0x29, 0x20, 0x34, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x77, 0x61, 0x76, + 0x65, 0x28, 0x29, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x69, 0x64, 0x0a, + 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x75, 0x65, 0x34, 0x0a, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, + 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, + 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x6f, + 0x6e, 0x63, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, + 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, + 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, + 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, + 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, + 0x64, 0x5f, 0x69, 0x73, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x28, 0x29, + 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, + 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x09, 0x0a, 0x09, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, + 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, + 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x77, 0x61, 0x76, 0x65, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x6c, 0x61, 0x6e, + 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x28, 0x29, 0x0a, 0x09, 0x7b, + 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, + 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, + 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, + 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, + 0x0a, 0x09, 0x09, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x76, 0x6f, 0x74, 0x65, + 0x3a, 0x3a, 0x76, 0x6f, 0x74, 0x65, 0x5f, 0x74, 0x20, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x20, 0x3d, 0x20, 0x28, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x76, + 0x6f, 0x74, 0x65, 0x3a, 0x3a, 0x76, 0x6f, 0x74, 0x65, 0x5f, 0x74, 0x29, + 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, + 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, 0x6d, 0x61, 0x73, 0x6b, + 0x28, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x78, + 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3b, 0x0a, 0x09, 0x09, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x79, 0x20, 0x3d, 0x20, 0x28, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x20, 0x3e, 0x3e, 0x20, 0x33, 0x32, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x28, 0x70, 0x6f, 0x70, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x28, 0x78, 0x29, 0x20, 0x2b, 0x20, 0x70, 0x6f, 0x70, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x28, 0x79, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, + 0x65, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, + 0x75, 0x69, 0x6e, 0x74, 0x29, 0x30, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, + 0x69, 0x66, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, - 0x5f, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x28, 0x54, - 0x20, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, - 0x74, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x5f, - 0x69, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, - 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, - 0x48, 0x41, 0x56, 0x45, 0x5f, 0x51, 0x55, 0x41, 0x44, 0x47, 0x52, 0x4f, - 0x55, 0x50, 0x5f, 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x71, 0x75, 0x61, - 0x64, 0x5f, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x28, - 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, - 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, - 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, - 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x53, 0x49, 0x4d, 0x44, 0x47, 0x52, - 0x4f, 0x55, 0x50, 0x5f, 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, - 0x6d, 0x64, 0x5f, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, - 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, - 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, - 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, - 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, - 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, - 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, - 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x54, 0x20, 0x64, + 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x77, + 0x61, 0x76, 0x65, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x6c, 0x61, 0x6e, 0x65, + 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x28, 0x29, 0x0a, 0x09, 0x7b, 0x0a, + 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, + 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, + 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, + 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, + 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, + 0x64, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x65, 0x78, 0x63, + 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x31, + 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x09, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x29, + 0x30, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x09, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, + 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x62, + 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x28, 0x54, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, @@ -6728,124 +8880,183 @@ unsigned char ue4_stdlib_metal[] = { 0x56, 0x45, 0x5f, 0x51, 0x55, 0x41, 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x71, 0x75, 0x61, 0x64, 0x5f, - 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x64, 0x61, 0x74, 0x61, - 0x2c, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x5f, - 0x69, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, 0x64, - 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, - 0x45, 0x5f, 0x53, 0x49, 0x4d, 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, - 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x73, - 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, - 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, - 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, - 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, - 0x66, 0x69, 0x72, 0x73, 0x74, 0x28, 0x54, 0x20, 0x64, 0x61, 0x74, 0x61, - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x77, - 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, - 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, - 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, - 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, - 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x54, - 0x20, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, - 0x74, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x5f, - 0x69, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, - 0x65, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x73, 0x69, 0x6d, 0x64, - 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x29, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, - 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, - 0x6c, 0x65, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x28, 0x54, 0x20, 0x64, 0x61, - 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x64, - 0x65, 0x6c, 0x74, 0x61, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, - 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, - 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x51, 0x55, 0x41, 0x44, 0x47, - 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x71, - 0x75, 0x61, 0x64, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x5f, - 0x64, 0x6f, 0x77, 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x64, - 0x65, 0x6c, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, + 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x28, 0x64, 0x61, + 0x74, 0x61, 0x2c, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, + 0x65, 0x5f, 0x69, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x53, 0x49, 0x4d, 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, - 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x5f, 0x64, 0x6f, 0x77, - 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x64, 0x65, 0x6c, 0x74, - 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, - 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, - 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x28, 0x54, 0x20, 0x64, 0x61, 0x74, 0x61, + 0x5f, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x28, 0x64, + 0x61, 0x74, 0x61, 0x2c, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, + 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, + 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, + 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, + 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x54, 0x20, 0x64, 0x61, 0x74, + 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x73, 0x69, + 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, + 0x5f, 0x51, 0x55, 0x41, 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x5f, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x71, 0x75, 0x61, 0x64, 0x5f, 0x73, 0x68, + 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, + 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x64, + 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, + 0x53, 0x49, 0x4d, 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x5f, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x73, 0x68, 0x75, + 0x66, 0x66, 0x6c, 0x65, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x73, + 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x29, + 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, + 0x77, 0x61, 0x76, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x66, 0x69, + 0x72, 0x73, 0x74, 0x28, 0x54, 0x20, 0x64, 0x61, 0x74, 0x61, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x77, 0x61, 0x76, + 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x64, 0x61, + 0x74, 0x61, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, + 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x77, + 0x61, 0x76, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x54, 0x20, 0x64, + 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, + 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x64, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x77, + 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, + 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, + 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, + 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, + 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x28, 0x54, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x51, 0x55, 0x41, 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x71, 0x75, 0x61, - 0x64, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x5f, 0x75, 0x70, - 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, - 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, - 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, - 0x53, 0x49, 0x4d, 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x5f, 0x29, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x73, 0x68, 0x75, - 0x66, 0x66, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x28, 0x64, 0x61, 0x74, 0x61, - 0x2c, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, - 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, - 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, - 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, - 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, - 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, + 0x64, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x5f, 0x64, 0x6f, + 0x77, 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x64, 0x65, 0x6c, + 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, + 0x45, 0x5f, 0x53, 0x49, 0x4d, 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, + 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x73, + 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x28, + 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x29, + 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, + 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, + 0x5f, 0x75, 0x70, 0x28, 0x54, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, + 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, + 0x56, 0x45, 0x5f, 0x51, 0x55, 0x41, 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, + 0x5f, 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x71, 0x75, 0x61, 0x64, 0x5f, + 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x28, 0x64, + 0x61, 0x74, 0x61, 0x2c, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x29, 0x3b, + 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x53, 0x49, + 0x4d, 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x5f, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, + 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, + 0x64, 0x65, 0x6c, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, + 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, + 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, + 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, + 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x5f, 0x78, 0x6f, 0x72, 0x28, 0x54, + 0x20, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x51, 0x55, 0x41, + 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x5f, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x71, 0x75, 0x61, 0x64, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, + 0x65, 0x5f, 0x78, 0x6f, 0x72, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, + 0x6d, 0x61, 0x73, 0x6b, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, + 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, + 0x41, 0x56, 0x45, 0x5f, 0x53, 0x49, 0x4d, 0x44, 0x47, 0x52, 0x4f, 0x55, + 0x50, 0x5f, 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x5f, 0x78, 0x6f, 0x72, - 0x28, 0x54, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, - 0x6f, 0x72, 0x74, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x29, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, - 0x6e, 0x65, 0x64, 0x28, 0x5f, 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x51, - 0x55, 0x41, 0x44, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x5f, 0x29, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x20, 0x71, 0x75, 0x61, 0x64, 0x5f, 0x73, 0x68, 0x75, 0x66, - 0x66, 0x6c, 0x65, 0x5f, 0x78, 0x6f, 0x72, 0x28, 0x64, 0x61, 0x74, 0x61, - 0x2c, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, - 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x5f, - 0x5f, 0x48, 0x41, 0x56, 0x45, 0x5f, 0x53, 0x49, 0x4d, 0x44, 0x47, 0x52, - 0x4f, 0x55, 0x50, 0x5f, 0x5f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, - 0x6d, 0x64, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x5f, 0x78, - 0x6f, 0x72, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x6d, 0x61, 0x73, - 0x6b, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, + 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x29, + 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x62, 0x6f, 0x6f, + 0x6c, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x61, 0x6e, 0x79, 0x28, 0x62, + 0x6f, 0x6f, 0x6c, 0x20, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, + 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, + 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, + 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, + 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, + 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, + 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x61, 0x6e, 0x79, 0x28, 0x44, 0x61, 0x74, + 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x41, + 0x6e, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, + 0x30, 0x3b, 0x20, 0x41, 0x6e, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x66, 0x61, + 0x6c, 0x73, 0x65, 0x20, 0x26, 0x26, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, + 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, + 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x6e, 0x79, 0x20, 0x7c, 0x3d, + 0x20, 0x28, 0x28, 0x62, 0x6f, 0x6f, 0x6c, 0x29, 0x77, 0x61, 0x76, 0x65, + 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x28, 0x75, 0x69, + 0x6e, 0x74, 0x29, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, 0x20, + 0x3d, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x41, + 0x6e, 0x79, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x62, - 0x6f, 0x6f, 0x6c, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x61, 0x6e, 0x79, + 0x6f, 0x6f, 0x6c, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x61, 0x6c, 0x6c, 0x28, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x29, 0x0a, 0x20, @@ -6854,77 +9065,9 @@ unsigned char ue4_stdlib_metal[] = { 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x61, 0x6e, 0x79, 0x28, 0x44, + 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x61, 0x6c, 0x6c, 0x28, 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x6f, 0x6c, - 0x20, 0x41, 0x6e, 0x79, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, - 0x72, 0x20, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x20, - 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x41, 0x6e, 0x79, 0x20, 0x3d, 0x3d, 0x20, - 0x66, 0x61, 0x6c, 0x73, 0x65, 0x20, 0x26, 0x26, 0x20, 0x69, 0x20, 0x3c, - 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, - 0x6e, 0x65, 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x6e, 0x79, 0x20, - 0x7c, 0x3d, 0x20, 0x28, 0x28, 0x62, 0x6f, 0x6f, 0x6c, 0x29, 0x77, 0x61, - 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x28, - 0x75, 0x69, 0x6e, 0x74, 0x29, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, - 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x20, 0x41, 0x6e, 0x79, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, - 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, - 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x61, - 0x6c, 0x6c, 0x28, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x44, 0x61, 0x74, 0x61, - 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, - 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x29, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, - 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, - 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, - 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, - 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x61, 0x6c, 0x6c, - 0x28, 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, - 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, - 0x6f, 0x6c, 0x20, 0x41, 0x6e, 0x79, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, - 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, - 0x6f, 0x72, 0x20, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x69, - 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x41, 0x6e, 0x79, 0x20, 0x3d, 0x3d, - 0x20, 0x74, 0x72, 0x75, 0x65, 0x20, 0x26, 0x26, 0x20, 0x69, 0x20, 0x3c, - 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, - 0x6e, 0x65, 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x6e, 0x79, 0x20, - 0x26, 0x3d, 0x20, 0x28, 0x28, 0x62, 0x6f, 0x6f, 0x6c, 0x29, 0x77, 0x61, - 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x28, - 0x75, 0x69, 0x6e, 0x74, 0x29, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, - 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x20, 0x41, 0x6e, 0x79, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, - 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, - 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x65, - 0x71, 0x75, 0x61, 0x6c, 0x28, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x44, 0x61, - 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, - 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, - 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, - 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, - 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, - 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, - 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x72, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x73, 0x69, 0x6d, 0x64, 0x5f, - 0x61, 0x6e, 0x79, 0x28, 0x44, 0x61, 0x74, 0x61, 0x29, 0x20, 0x3d, 0x3d, - 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x61, 0x6c, 0x6c, 0x28, 0x44, 0x61, - 0x74, 0x61, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x41, 0x6e, 0x79, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x20, 0x3d, @@ -6937,143 +9080,103 @@ unsigned char ue4_stdlib_metal[] = { 0x20, 0x28, 0x28, 0x62, 0x6f, 0x6f, 0x6c, 0x29, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x29, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, 0x20, - 0x3d, 0x3d, 0x20, 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x3d, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x41, 0x6e, 0x79, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, - 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x54, - 0x20, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, - 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, - 0x61, 0x6e, 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, - 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, - 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, - 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, - 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, - 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, - 0x64, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, - 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x54, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, - 0x3d, 0x20, 0x54, 0x28, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x73, 0x68, - 0x6f, 0x72, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, - 0x20, 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, - 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x20, 0x2b, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, - 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x44, 0x61, 0x74, - 0x61, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, - 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, - 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x64, 0x75, - 0x63, 0x74, 0x28, 0x54, 0x20, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, - 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, - 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, - 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, - 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, - 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, - 0x53, 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, - 0x74, 0x28, 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, - 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, - 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x77, 0x61, - 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x44, - 0x61, 0x74, 0x61, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x73, - 0x68, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x20, - 0x69, 0x20, 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, - 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x2a, 0x3d, 0x20, 0x77, 0x61, 0x76, - 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x44, 0x61, - 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, + 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x62, + 0x6f, 0x6f, 0x6c, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x65, 0x71, 0x75, + 0x61, 0x6c, 0x28, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x44, 0x61, 0x74, 0x61, + 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, + 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, + 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, + 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, + 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, + 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x28, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x61, 0x6e, + 0x79, 0x28, 0x44, 0x61, 0x74, 0x61, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x73, + 0x69, 0x6d, 0x64, 0x5f, 0x61, 0x6c, 0x6c, 0x28, 0x44, 0x61, 0x74, 0x61, + 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x41, + 0x6e, 0x79, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, + 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, + 0x3b, 0x20, 0x41, 0x6e, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x74, 0x72, 0x75, + 0x65, 0x20, 0x26, 0x26, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, 0x75, 0x6d, + 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x3b, + 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x41, 0x6e, 0x79, 0x20, 0x26, 0x3d, 0x20, 0x28, + 0x28, 0x62, 0x6f, 0x6f, 0x6c, 0x29, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, + 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x28, 0x75, 0x69, 0x6e, 0x74, + 0x29, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, 0x20, 0x3d, 0x3d, + 0x20, 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x52, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, - 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, - 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, - 0x20, 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, - 0x69, 0x78, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x54, 0x20, 0x44, 0x61, 0x74, - 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6c, 0x61, - 0x6e, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x75, 0x73, - 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, - 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, - 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, - 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, - 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, - 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, - 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, - 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x73, 0x75, - 0x6d, 0x28, 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, - 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, - 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x54, 0x28, - 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, - 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, - 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, - 0x73, 0x20, 0x26, 0x26, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x61, 0x6e, - 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x20, 0x69, 0x2b, 0x2b, - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x2b, 0x3d, 0x20, 0x77, 0x61, - 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x44, - 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x52, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, - 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, - 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, - 0x29, 0x20, 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, - 0x66, 0x69, 0x78, 0x5f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x28, - 0x54, 0x20, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, - 0x72, 0x74, 0x20, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, - 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, - 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, - 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, - 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, - 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x72, 0x65, - 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x70, 0x72, - 0x65, 0x66, 0x69, 0x78, 0x5f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, - 0x76, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x28, 0x44, - 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x20, 0x52, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x54, 0x28, 0x31, 0x29, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, - 0x20, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x20, 0x3d, - 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x5f, - 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x20, 0x26, - 0x26, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x41, 0x6e, 0x79, + 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, + 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x54, 0x20, 0x44, + 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, + 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, + 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, + 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, + 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, + 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, + 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x09, 0x09, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, + 0x73, 0x75, 0x6d, 0x28, 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, + 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x54, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, + 0x54, 0x28, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, + 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, + 0x6e, 0x65, 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x20, 0x2b, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, + 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x44, 0x61, 0x74, 0x61, 0x2c, + 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, + 0x77, 0x61, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, + 0x28, 0x54, 0x20, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, + 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, + 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, + 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, + 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, + 0x5f, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, + 0x69, 0x6d, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x28, + 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x20, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, + 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x44, 0x61, 0x74, + 0x61, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x20, 0x69, 0x20, + 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, + 0x61, 0x6e, 0x65, 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x2a, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, @@ -7089,94 +9192,31 @@ unsigned char ue4_stdlib_metal[] = { 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, - 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x28, 0x54, 0x20, - 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, - 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, - 0x6e, 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, - 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, - 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, - 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, - 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x09, - 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, - 0x5f, 0x61, 0x6e, 0x64, 0x28, 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, - 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x54, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, - 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, - 0x65, 0x28, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, - 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, - 0x31, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, - 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x3b, 0x20, 0x69, - 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x26, 0x3d, 0x20, - 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, - 0x28, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, - 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, - 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, - 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, - 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, - 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x6f, - 0x72, 0x28, 0x54, 0x20, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, - 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, - 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, - 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, - 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, - 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, - 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, - 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6f, 0x72, 0x28, 0x44, 0x61, 0x74, 0x61, - 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x20, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, - 0x66, 0x66, 0x6c, 0x65, 0x28, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x30, - 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, - 0x6f, 0x72, 0x20, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x69, - 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, 0x75, - 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, - 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, - 0x7c, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, - 0x66, 0x6c, 0x65, 0x28, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x23, - 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, - 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, - 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, - 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x77, 0x61, 0x76, - 0x65, 0x5f, 0x78, 0x6f, 0x72, 0x28, 0x54, 0x20, 0x44, 0x61, 0x74, 0x61, - 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, - 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x29, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, - 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, - 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, - 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, - 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x78, 0x6f, 0x72, - 0x28, 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, - 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x20, - 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x77, 0x61, 0x76, - 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x44, 0x61, - 0x74, 0x61, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x73, 0x68, - 0x6f, 0x72, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x20, 0x69, - 0x20, 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, - 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, + 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, + 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x54, 0x20, 0x44, 0x61, 0x74, 0x61, 0x2c, + 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6c, 0x61, 0x6e, 0x65, + 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, + 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, + 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, + 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, + 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, + 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, + 0x6d, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x65, 0x78, + 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x28, + 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x20, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x54, 0x28, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x20, + 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, 0x75, 0x6d, + 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x20, + 0x26, 0x26, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x61, 0x6e, 0x65, 0x5f, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x20, 0x5e, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x20, 0x2b, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, @@ -7189,16 +9229,87 @@ unsigned char ue4_stdlib_metal[] = { 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, - 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x28, 0x54, - 0x20, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, - 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, - 0x61, 0x6e, 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, - 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, - 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, - 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, - 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, - 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, - 0x64, 0x5f, 0x6d, 0x69, 0x6e, 0x28, 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, + 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, + 0x78, 0x5f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x28, 0x54, 0x20, + 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x20, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, + 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, + 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, + 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, + 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, + 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, + 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x66, + 0x69, 0x78, 0x5f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, + 0x5f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x28, 0x44, 0x61, 0x74, + 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x20, 0x52, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x54, 0x28, 0x31, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, + 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, + 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, + 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x20, 0x26, 0x26, 0x20, + 0x69, 0x20, 0x3c, 0x20, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x20, 0x2a, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, + 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, + 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, + 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, + 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x77, + 0x61, 0x76, 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x28, 0x54, 0x20, 0x44, 0x61, + 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, + 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, + 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, + 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, + 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, + 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, + 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x61, + 0x6e, 0x64, 0x28, 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, + 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x54, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x77, + 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, + 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, + 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x31, 0x3b, + 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, + 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x26, 0x3d, 0x20, 0x77, 0x61, + 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x44, + 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x52, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, + 0x29, 0x20, 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x6f, 0x72, 0x28, + 0x54, 0x20, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, + 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, + 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, + 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, + 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, + 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, + 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, + 0x6d, 0x64, 0x5f, 0x6f, 0x72, 0x28, 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, @@ -7209,10 +9320,9 @@ unsigned char ue4_stdlib_metal[] = { 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, - 0x6d, 0x69, 0x6e, 0x28, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2c, 0x20, - 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, - 0x28, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x7c, 0x3d, + 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, + 0x65, 0x28, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x23, 0x65, 0x6e, @@ -7224,7 +9334,7 @@ unsigned char ue4_stdlib_metal[] = { 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, - 0x6d, 0x61, 0x78, 0x28, 0x54, 0x20, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, + 0x78, 0x6f, 0x72, 0x28, 0x54, 0x20, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, @@ -7232,7 +9342,7 @@ unsigned char ue4_stdlib_metal[] = { 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6d, 0x61, 0x78, 0x28, 0x44, + 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x78, 0x6f, 0x72, 0x28, 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, @@ -7244,265 +9354,332 @@ unsigned char ue4_stdlib_metal[] = { 0x6e, 0x65, 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x6d, 0x61, 0x78, 0x28, 0x52, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, - 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, - 0x69, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6c, 0x74, 0x20, 0x5e, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, + 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x44, 0x61, 0x74, 0x61, 0x2c, + 0x20, 0x69, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, - 0x74, 0x34, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x62, 0x61, 0x6c, 0x6c, - 0x6f, 0x74, 0x28, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x44, 0x61, 0x74, 0x61, - 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, - 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x29, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, - 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, - 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, - 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, - 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x75, 0x69, 0x6e, - 0x74, 0x34, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x28, 0x30, 0x29, - 0x3b, 0x0a, 0x09, 0x09, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x76, 0x6f, 0x74, - 0x65, 0x3a, 0x3a, 0x76, 0x6f, 0x74, 0x65, 0x5f, 0x74, 0x20, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x20, 0x3d, 0x20, 0x28, 0x73, 0x69, 0x6d, 0x64, 0x5f, - 0x76, 0x6f, 0x74, 0x65, 0x3a, 0x3a, 0x76, 0x6f, 0x74, 0x65, 0x5f, 0x74, - 0x29, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x6c, 0x6f, 0x74, - 0x28, 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x52, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3b, 0x0a, 0x09, 0x09, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, - 0x3e, 0x3e, 0x20, 0x33, 0x32, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, - 0x74, 0x75, 0x72, 0x6e, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, - 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, 0x52, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x28, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x73, 0x68, - 0x6f, 0x72, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, - 0x20, 0x3c, 0x20, 0x33, 0x32, 0x20, 0x26, 0x26, 0x20, 0x69, 0x20, 0x3c, - 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, - 0x6e, 0x65, 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x2e, 0x78, 0x20, 0x7c, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, - 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x28, 0x75, 0x69, - 0x6e, 0x74, 0x29, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, 0x20, - 0x3c, 0x3c, 0x20, 0x69, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x7d, 0x0a, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, - 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x33, 0x32, - 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x36, 0x34, 0x20, 0x26, 0x26, 0x20, - 0x69, 0x20, 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, - 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, - 0x0a, 0x09, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x2e, 0x79, 0x20, 0x7c, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, - 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x28, 0x75, 0x69, - 0x6e, 0x74, 0x29, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, 0x20, - 0x3c, 0x3c, 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x23, 0x65, 0x6e, - 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x0a, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, - 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, - 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, - 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, - 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x20, 0x57, 0x61, 0x76, 0x65, 0x4f, 0x6e, 0x63, 0x65, 0x28, 0x29, 0x20, - 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x6f, 0x6e, - 0x63, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, - 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x47, 0x65, 0x74, - 0x4c, 0x61, 0x6e, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x28, 0x29, 0x20, - 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x67, 0x65, - 0x74, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x3c, 0x74, 0x79, 0x70, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x3e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, + 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x54, 0x20, + 0x77, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x28, 0x54, 0x20, 0x44, + 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, + 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, + 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, + 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, + 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, + 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, + 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x09, 0x09, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x69, 0x6d, 0x64, 0x5f, + 0x6d, 0x69, 0x6e, 0x28, 0x44, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x23, + 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x54, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, + 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, + 0x28, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, + 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x31, + 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, + 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x3b, 0x20, 0x69, 0x2b, + 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x6d, 0x69, + 0x6e, 0x28, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x77, 0x61, + 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x44, + 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, + 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x3c, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x54, + 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x5f, 0x5f, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x5f, 0x28, 0x28, 0x61, + 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x29, 0x29, 0x20, 0x54, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x61, + 0x78, 0x28, 0x54, 0x20, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x75, 0x73, + 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, + 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, + 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, + 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, + 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, + 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6d, 0x61, 0x78, 0x28, 0x44, 0x61, 0x74, + 0x61, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x20, 0x52, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, + 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, + 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, + 0x69, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, + 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, + 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x20, 0x3d, 0x20, 0x6d, 0x61, 0x78, 0x28, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x2c, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x68, 0x75, 0x66, + 0x66, 0x6c, 0x65, 0x28, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, + 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, + 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, + 0x5f, 0x28, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x29, 0x29, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, + 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x62, 0x61, 0x6c, 0x6c, 0x6f, 0x74, + 0x28, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, + 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, + 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, + 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, + 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, + 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, + 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x34, + 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x28, 0x30, 0x29, 0x3b, 0x0a, + 0x09, 0x09, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x76, 0x6f, 0x74, 0x65, 0x3a, + 0x3a, 0x76, 0x6f, 0x74, 0x65, 0x5f, 0x74, 0x20, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x20, 0x3d, 0x20, 0x28, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x76, 0x6f, + 0x74, 0x65, 0x3a, 0x3a, 0x76, 0x6f, 0x74, 0x65, 0x5f, 0x74, 0x29, 0x73, + 0x69, 0x6d, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x6c, 0x6f, 0x74, 0x28, 0x44, + 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x52, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3b, 0x0a, 0x09, 0x09, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x79, + 0x20, 0x3d, 0x20, 0x28, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3e, 0x3e, + 0x20, 0x33, 0x32, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x23, + 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x28, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, + 0x20, 0x33, 0x32, 0x20, 0x26, 0x26, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x6e, + 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, 0x61, 0x6e, 0x65, + 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x2e, 0x78, 0x20, 0x7c, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, + 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x28, 0x75, 0x69, 0x6e, 0x74, + 0x29, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, 0x20, 0x3c, 0x3c, + 0x20, 0x69, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x73, 0x68, + 0x6f, 0x72, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x33, 0x32, 0x3b, 0x20, + 0x69, 0x20, 0x3c, 0x20, 0x36, 0x34, 0x20, 0x26, 0x26, 0x20, 0x69, 0x20, + 0x3c, 0x20, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x6d, 0x64, 0x5f, 0x6c, + 0x61, 0x6e, 0x65, 0x73, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x09, + 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x2e, 0x79, 0x20, 0x7c, 0x3d, 0x20, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, + 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x28, 0x28, 0x75, 0x69, 0x6e, 0x74, + 0x29, 0x44, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x69, 0x29, 0x20, 0x3c, 0x3c, + 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, + 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x73, 0x20, + 0x74, 0x68, 0x61, 0x74, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x0a, 0x23, + 0x69, 0x66, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x5f, 0x56, + 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x5f, 0x20, 0x3e, 0x3d, 0x20, + 0x32, 0x31, 0x30, 0x20, 0x26, 0x26, 0x20, 0x5f, 0x5f, 0x4d, 0x45, 0x54, + 0x41, 0x4c, 0x5f, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x5f, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, + 0x61, 0x76, 0x65, 0x4f, 0x6e, 0x63, 0x65, 0x28, 0x29, 0x20, 0x75, 0x65, + 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x6f, 0x6e, 0x63, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x47, 0x65, 0x74, 0x4c, 0x61, - 0x6e, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x28, 0x29, 0x20, 0x75, 0x65, + 0x6e, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x28, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x67, 0x65, 0x74, 0x5f, - 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x28, 0x29, + 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6e, 0x79, 0x54, 0x72, 0x75, 0x65, + 0x20, 0x57, 0x61, 0x76, 0x65, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x6e, 0x65, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x28, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, + 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x67, 0x65, 0x74, 0x5f, 0x6c, 0x61, + 0x6e, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x28, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, + 0x61, 0x76, 0x65, 0x41, 0x6e, 0x79, 0x54, 0x72, 0x75, 0x65, 0x28, 0x45, + 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, + 0x76, 0x65, 0x5f, 0x61, 0x6e, 0x79, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, + 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x54, + 0x72, 0x75, 0x65, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, + 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x61, 0x6c, 0x6c, 0x28, + 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, + 0x65, 0x41, 0x6c, 0x6c, 0x45, 0x71, 0x75, 0x61, 0x6c, 0x28, 0x45, 0x78, + 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, + 0x65, 0x5f, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x28, 0x45, 0x78, 0x70, 0x72, + 0x2c, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x42, 0x61, 0x6c, + 0x6c, 0x6f, 0x74, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, + 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x62, 0x61, 0x6c, 0x6c, + 0x6f, 0x74, 0x36, 0x34, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x30, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x53, 0x75, 0x6d, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, - 0x77, 0x61, 0x76, 0x65, 0x5f, 0x61, 0x6e, 0x79, 0x28, 0x45, 0x78, 0x70, + 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, - 0x6c, 0x54, 0x72, 0x75, 0x65, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, - 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x61, 0x6c, - 0x6c, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x30, 0x29, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, - 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x45, 0x71, 0x75, 0x61, 0x6c, 0x28, + 0x6c, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x28, 0x45, 0x78, 0x70, + 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, + 0x5f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x28, 0x45, 0x78, 0x70, + 0x72, 0x2c, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, + 0x6c, 0x42, 0x69, 0x74, 0x41, 0x6e, 0x64, 0x28, 0x45, 0x78, 0x70, 0x72, + 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, + 0x61, 0x6e, 0x64, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x30, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x42, 0x69, 0x74, 0x4f, + 0x72, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, + 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x6f, 0x72, 0x28, 0x45, 0x78, 0x70, + 0x72, 0x2c, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, + 0x6c, 0x42, 0x69, 0x74, 0x58, 0x6f, 0x72, 0x28, 0x45, 0x78, 0x70, 0x72, + 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, + 0x78, 0x6f, 0x72, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x30, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x4d, 0x69, 0x6e, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, - 0x61, 0x76, 0x65, 0x5f, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x28, 0x45, 0x78, - 0x70, 0x72, 0x2c, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, + 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x28, 0x45, 0x78, 0x70, 0x72, + 0x2c, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, + 0x4d, 0x61, 0x78, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, + 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x28, + 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, + 0x65, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x53, 0x75, 0x6d, 0x28, 0x45, + 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, + 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x73, 0x75, + 0x6d, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, + 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x28, 0x45, 0x78, 0x70, 0x72, + 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, + 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x70, 0x72, 0x6f, 0x64, 0x75, + 0x63, 0x74, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x30, 0x29, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, + 0x65, 0x4f, 0x6e, 0x63, 0x65, 0x28, 0x29, 0x20, 0x28, 0x67, 0x65, 0x74, + 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x20, + 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x47, 0x65, + 0x74, 0x4c, 0x61, 0x6e, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x28, 0x29, + 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, + 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, + 0x57, 0x61, 0x76, 0x65, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x6e, 0x65, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x28, 0x29, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, + 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, + 0x69, 0x6e, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, + 0x76, 0x65, 0x41, 0x6e, 0x79, 0x54, 0x72, 0x75, 0x65, 0x28, 0x45, 0x78, + 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, + 0x65, 0x5f, 0x61, 0x6e, 0x79, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, + 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, + 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, + 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x54, 0x72, 0x75, 0x65, 0x28, + 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, + 0x61, 0x76, 0x65, 0x5f, 0x61, 0x6c, 0x6c, 0x28, 0x45, 0x78, 0x70, 0x72, + 0x2c, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x45, 0x71, 0x75, + 0x61, 0x6c, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, + 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x65, 0x71, 0x75, 0x61, 0x6c, + 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, + 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, + 0x61, 0x76, 0x65, 0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x42, 0x61, 0x6c, 0x6c, 0x6f, 0x74, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x62, 0x61, 0x6c, 0x6c, 0x6f, 0x74, 0x36, 0x34, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, - 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, - 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x53, - 0x75, 0x6d, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, - 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x45, - 0x78, 0x70, 0x72, 0x2c, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, - 0x41, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x28, 0x45, - 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, - 0x76, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x28, 0x45, - 0x78, 0x70, 0x72, 0x2c, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, - 0x41, 0x6c, 0x6c, 0x42, 0x69, 0x74, 0x41, 0x6e, 0x64, 0x28, 0x45, 0x78, - 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, - 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, - 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, - 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x42, 0x69, - 0x74, 0x4f, 0x72, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, - 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x6f, 0x72, 0x28, 0x45, - 0x78, 0x70, 0x72, 0x2c, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, - 0x41, 0x6c, 0x6c, 0x42, 0x69, 0x74, 0x58, 0x6f, 0x72, 0x28, 0x45, 0x78, - 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, - 0x65, 0x5f, 0x78, 0x6f, 0x72, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, - 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, - 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x4d, 0x69, - 0x6e, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, - 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x28, 0x45, 0x78, - 0x70, 0x72, 0x2c, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, - 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, - 0x6c, 0x6c, 0x4d, 0x61, 0x78, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, - 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x6d, 0x61, - 0x78, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x30, 0x29, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, - 0x61, 0x76, 0x65, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x53, 0x75, 0x6d, - 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, - 0x77, 0x61, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, - 0x73, 0x75, 0x6d, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x30, 0x2c, - 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, - 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x50, 0x72, 0x65, 0x66, - 0x69, 0x78, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x28, 0x45, 0x78, - 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, - 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x70, 0x72, 0x6f, - 0x64, 0x75, 0x63, 0x74, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x30, - 0x2c, 0x20, 0x30, 0x29, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, - 0x61, 0x76, 0x65, 0x4f, 0x6e, 0x63, 0x65, 0x28, 0x29, 0x20, 0x28, 0x67, - 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, - 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, - 0x47, 0x65, 0x74, 0x4c, 0x61, 0x6e, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, - 0x28, 0x29, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, - 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, - 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x6e, - 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x28, 0x29, 0x20, 0x67, 0x65, 0x74, - 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, - 0x57, 0x61, 0x76, 0x65, 0x41, 0x6e, 0x79, 0x54, 0x72, 0x75, 0x65, 0x28, - 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, - 0x61, 0x76, 0x65, 0x5f, 0x61, 0x6e, 0x79, 0x28, 0x45, 0x78, 0x70, 0x72, - 0x2c, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, - 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, - 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, - 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x54, 0x72, 0x75, - 0x65, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, - 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x61, 0x6c, 0x6c, 0x28, 0x45, 0x78, - 0x70, 0x72, 0x2c, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, - 0x61, 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, - 0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, - 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x45, - 0x71, 0x75, 0x61, 0x6c, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, - 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x65, 0x71, 0x75, - 0x61, 0x6c, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x67, 0x65, 0x74, - 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, - 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, - 0x65, 0x42, 0x61, 0x6c, 0x6c, 0x6f, 0x74, 0x28, 0x45, 0x78, 0x70, 0x72, - 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, - 0x62, 0x61, 0x6c, 0x6c, 0x6f, 0x74, 0x36, 0x34, 0x28, 0x45, 0x78, 0x70, - 0x72, 0x2c, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, - 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, - 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, - 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x53, 0x75, - 0x6d, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, - 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x45, 0x78, - 0x70, 0x72, 0x2c, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, - 0x61, 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, - 0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, - 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x50, - 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, - 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x70, - 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x42, 0x69, 0x74, 0x41, - 0x6e, 0x64, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, - 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x28, 0x45, + 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x53, 0x75, 0x6d, 0x28, + 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, + 0x61, 0x76, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x45, 0x78, 0x70, 0x72, + 0x2c, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x72, 0x6f, + 0x64, 0x75, 0x63, 0x74, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, + 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x6f, + 0x64, 0x75, 0x63, 0x74, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x67, + 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, 0x70, + 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, + 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x42, 0x69, 0x74, 0x41, 0x6e, 0x64, + 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, + 0x77, 0x61, 0x76, 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x28, 0x45, 0x78, 0x70, + 0x72, 0x2c, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, + 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, + 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, + 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x42, 0x69, + 0x74, 0x4f, 0x72, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, + 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x6f, 0x72, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, - 0x42, 0x69, 0x74, 0x4f, 0x72, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, - 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x6f, 0x72, - 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, - 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, - 0x61, 0x76, 0x65, 0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, - 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x41, - 0x6c, 0x6c, 0x42, 0x69, 0x74, 0x58, 0x6f, 0x72, 0x28, 0x45, 0x78, 0x70, + 0x42, 0x69, 0x74, 0x58, 0x6f, 0x72, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, + 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x78, + 0x6f, 0x72, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x67, 0x65, 0x74, + 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, + 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, + 0x65, 0x41, 0x6c, 0x6c, 0x4d, 0x69, 0x6e, 0x28, 0x45, 0x78, 0x70, 0x72, + 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, + 0x6d, 0x69, 0x6e, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x67, 0x65, + 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, 0x70, 0x65, + 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, + 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x4d, 0x61, 0x78, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, - 0x5f, 0x78, 0x6f, 0x72, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x67, + 0x5f, 0x6d, 0x61, 0x78, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, - 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x4d, 0x69, 0x6e, 0x28, 0x45, 0x78, - 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, - 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, - 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, - 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x29, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, - 0x57, 0x61, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x4d, 0x61, 0x78, 0x28, 0x45, - 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, - 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, + 0x61, 0x76, 0x65, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x53, 0x75, 0x6d, + 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, + 0x77, 0x61, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, + 0x73, 0x75, 0x6d, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x67, 0x65, + 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, + 0x2c, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, + 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x28, 0x45, 0x78, 0x70, 0x72, + 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, + 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x70, 0x72, 0x6f, 0x64, 0x75, + 0x63, 0x74, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x67, 0x65, 0x74, + 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x2c, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, 0x29, 0x29, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x20, 0x57, 0x61, 0x76, 0x65, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x53, - 0x75, 0x6d, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, - 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, - 0x78, 0x5f, 0x73, 0x75, 0x6d, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, - 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x77, 0x61, 0x76, 0x65, - 0x28, 0x29, 0x2c, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, - 0x61, 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, - 0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x23, 0x64, 0x65, 0x66, - 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x50, 0x72, 0x65, 0x66, - 0x69, 0x78, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x28, 0x45, 0x78, - 0x70, 0x72, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, - 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x70, 0x72, 0x6f, - 0x64, 0x75, 0x63, 0x74, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, 0x67, - 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x5f, 0x69, 0x6e, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, - 0x29, 0x2c, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, - 0x64, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x77, 0x61, 0x76, 0x65, 0x28, - 0x29, 0x29, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x23, 0x64, - 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x52, 0x65, - 0x61, 0x64, 0x4c, 0x61, 0x6e, 0x65, 0x41, 0x74, 0x28, 0x45, 0x78, 0x70, - 0x72, 0x2c, 0x20, 0x69, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, - 0x61, 0x76, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x45, 0x78, 0x70, - 0x72, 0x2c, 0x20, 0x69, 0x29, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, - 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x52, 0x65, 0x61, 0x64, 0x46, 0x69, - 0x72, 0x73, 0x74, 0x4c, 0x61, 0x6e, 0x65, 0x28, 0x45, 0x78, 0x70, 0x72, - 0x2c, 0x20, 0x69, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, - 0x76, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x66, 0x69, 0x72, 0x73, - 0x74, 0x28, 0x45, 0x78, 0x70, 0x72, 0x29, 0x0a, 0x0a, 0x7d, 0x0a, 0x23, - 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x0a, 0x23, 0x70, 0x72, 0x61, - 0x67, 0x6d, 0x61, 0x20, 0x63, 0x6c, 0x61, 0x6e, 0x67, 0x20, 0x64, 0x69, - 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x20, 0x70, 0x6f, 0x70, - 0x0a + 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x23, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x20, 0x57, 0x61, 0x76, 0x65, 0x52, 0x65, 0x61, 0x64, + 0x4c, 0x61, 0x6e, 0x65, 0x41, 0x74, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, + 0x20, 0x69, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, + 0x65, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, + 0x20, 0x69, 0x29, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, + 0x57, 0x61, 0x76, 0x65, 0x52, 0x65, 0x61, 0x64, 0x46, 0x69, 0x72, 0x73, + 0x74, 0x4c, 0x61, 0x6e, 0x65, 0x28, 0x45, 0x78, 0x70, 0x72, 0x2c, 0x20, + 0x69, 0x29, 0x20, 0x75, 0x65, 0x34, 0x3a, 0x3a, 0x77, 0x61, 0x76, 0x65, + 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x28, + 0x45, 0x78, 0x70, 0x72, 0x29, 0x0a, 0x0a, 0x7d, 0x0a, 0x23, 0x65, 0x6e, + 0x64, 0x69, 0x66, 0x0a, 0x09, 0x0a, 0x23, 0x70, 0x72, 0x61, 0x67, 0x6d, + 0x61, 0x20, 0x63, 0x6c, 0x61, 0x6e, 0x67, 0x20, 0x64, 0x69, 0x61, 0x67, + 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x20, 0x70, 0x6f, 0x70, 0x0a }; -unsigned int ue4_stdlib_metal_len = 90049; +unsigned int ue4_stdlib_metal_len = 116183; diff --git a/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSAppDelegate.cpp b/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSAppDelegate.cpp index b1565285f0cc..383dafe8c16a 100644 --- a/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSAppDelegate.cpp +++ b/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSAppDelegate.cpp @@ -507,7 +507,7 @@ static IOSAppDelegate* CachedDelegate = nil; - (void)ToggleAudioSession:(bool)bActive force:(bool)bForce { // @todo kairos: resolve old vs new before we go to main - if (true) + if (false) { // we can actually override bActive based on backgrounding behavior, as that's the only time we actually deactivate the session // @todo kairos: is this a valid check? @@ -891,7 +891,7 @@ bool GIsSuspended = 0; // Don't deadlock here because a msg box may appear super early blocking the game thread and then the app may go into the background double startTime = FPlatformTime::Seconds(); - // don't wait for FDefaultGameMoviePlayer::WaitForMovieToFinish(), crash with 0x8badf00d if Wait for Movies to Complete is checked + // don't wait for FDefaultGameMoviePlayer::WaitForMovieToFinish(), crash with 0x8badf00d if "Wait for Movies to Complete" is checked while(!self.bHasSuspended && !FAppEntry::IsStartupMoviePlaying() && (FPlatformTime::Seconds() - startTime) < cMaxThreadWaitTime) { FIOSPlatformRHIFramePacer::Suspend(); diff --git a/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSBackgroundURLSessionHandler.cpp b/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSBackgroundURLSessionHandler.cpp index c09acdddf8a9..5ce96be4d376 100644 --- a/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSBackgroundURLSessionHandler.cpp +++ b/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSBackgroundURLSessionHandler.cpp @@ -102,9 +102,9 @@ NSURLSession* FBackgroundURLSessionHandler::GetBackgroundSession() void FBackgroundURLSessionHandler::CreateBackgroundSessionWorkingDirectory() { - const FString DirectoryPath = GetBackgroundSessionWorkingDirectoryPath(); + const FString& DirectoryPath = GetBackgroundSessionWorkingDirectoryPath(); - if (!DirectoryPath.IsEmpty()) + if (ensureAlwaysMsgf(!DirectoryPath.IsEmpty(), TEXT("Invalid empty BackgroundSessionWorkingDirectoryPath!"))) { IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); PlatformFile.CreateDirectory(*DirectoryPath); @@ -113,19 +113,20 @@ void FBackgroundURLSessionHandler::CreateBackgroundSessionWorkingDirectory() { NSString* PathAsNSString = DirectoryPath.GetNSString(); //May not yet have UE_Log - NSLog(@"Warning: Error in CreateBackgroundSessionWorkingDirectory: %s", [PathAsNSString UTF8String]); + ensureAlwaysMsgf(false, TEXT("Warning: Error in CreateBackgroundSessionWorkingDirectory: %s"), [PathAsNSString UTF8String]); } } } -FString FBackgroundURLSessionHandler::GetBackgroundSessionWorkingDirectoryPath() +const FString& FBackgroundURLSessionHandler::GetBackgroundSessionWorkingDirectoryPath() { - return FPaths::Combine(FPlatformMisc::GamePersistentDownloadDir(), TEXT("BackgroundHttp")); + static FString BackgroundHttpDir = FPaths::Combine(FPlatformMisc::GamePersistentDownloadDir(), TEXT("BackgroundHttp")); + return BackgroundHttpDir; } const FString FBackgroundURLSessionHandler::GetTemporaryFilePathFromURL(const FString& URL) { - static FString BackgroundHttpDir = GetBackgroundSessionWorkingDirectoryPath(); + const FString& BackgroundHttpDir = GetBackgroundSessionWorkingDirectoryPath(); const FString FileName = FPaths::MakeValidFileName(URL); return FPaths::Combine(BackgroundHttpDir, FileName); diff --git a/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSFeedbackContext.cpp b/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSFeedbackContext.cpp index c1ba61d39883..95caeb669459 100644 --- a/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSFeedbackContext.cpp +++ b/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSFeedbackContext.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "IOS/IOSFeedbackContext.h" diff --git a/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSInputInterface.cpp b/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSInputInterface.cpp index 9eaa8dd168e5..98342be6e611 100644 --- a/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSInputInterface.cpp +++ b/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSInputInterface.cpp @@ -18,7 +18,6 @@ static TAutoConsoleVariable CVarHapticsKickMedium(TEXT("ios.VibrationHapt static TAutoConsoleVariable CVarHapticsKickLight(TEXT("ios.VibrationHapticsKickLightValue"), 0.3f, TEXT("Vibation values higher than this will kick a haptics light Impact")); static TAutoConsoleVariable CVarHapticsRest(TEXT("ios.VibrationHapticsRestValue"), 0.2f, TEXT("Vibation values lower than this will allow haptics to Kick again when going over ios.VibrationHapticsKickValue")); - //@interface FControllerHelper : NSObject //{ // FIOSInputInterface @@ -49,9 +48,10 @@ FIOSInputInterface::FIOSInputInterface( const TSharedRef< FGenericApplicationMes #if !PLATFORM_TVOS MotionManager = nil; ReferenceAttitude = nil; - bPauseMotion = false; #endif - + bPauseMotion = false; + GConfig->GetBool(TEXT("/Script/IOSRuntimeSettings.IOSRuntimeSettings"), TEXT("bDisableMotionData"), bPauseMotion, GEngineIni); + GConfig->GetBool(TEXT("/Script/IOSRuntimeSettings.IOSRuntimeSettings"), TEXT("bTreatRemoteAsSeparateController"), bTreatRemoteAsSeparateController, GEngineIni); GConfig->GetBool(TEXT("/Script/IOSRuntimeSettings.IOSRuntimeSettings"), TEXT("bAllowRemoteRotation"), bAllowRemoteRotation, GEngineIni); GConfig->GetBool(TEXT("/Script/IOSRuntimeSettings.IOSRuntimeSettings"), TEXT("bUseRemoteAsVirtualJoystick"), bUseRemoteAsVirtualJoystick, GEngineIni); @@ -315,29 +315,30 @@ void FIOSInputInterface::SendControllerEvents() #if !PLATFORM_TVOS // @todo tvos: This needs to come from the Microcontroller rotation - // Update motion controls. - FVector Attitude; - FVector RotationRate; - FVector Gravity; - FVector Acceleration; + if (!bPauseMotion) + { + // Update motion controls. + FVector Attitude; + FVector RotationRate; + FVector Gravity; + FVector Acceleration; - GetMovementData(Attitude, RotationRate, Gravity, Acceleration); + GetMovementData(Attitude, RotationRate, Gravity, Acceleration); - // Fix-up yaw to match directions - Attitude.Y = -Attitude.Y; - RotationRate.Y = -RotationRate.Y; + // Fix-up yaw to match directions + Attitude.Y = -Attitude.Y; + RotationRate.Y = -RotationRate.Y; - // munge the vectors based on the orientation - ModifyVectorByOrientation(Attitude, true); - ModifyVectorByOrientation(RotationRate, true); - ModifyVectorByOrientation(Gravity, false); - ModifyVectorByOrientation(Acceleration, false); + // munge the vectors based on the orientation + ModifyVectorByOrientation(Attitude, true); + ModifyVectorByOrientation(RotationRate, true); + ModifyVectorByOrientation(Gravity, false); + ModifyVectorByOrientation(Acceleration, false); - MessageHandler->OnMotionDetected(Attitude, RotationRate, Gravity, Acceleration, 0); + MessageHandler->OnMotionDetected(Attitude, RotationRate, Gravity, Acceleration, 0); + } #endif - - - + for (GCController* Cont in [GCController controllers]) { GCGamepad* Gamepad = Cont.gamepad; @@ -577,101 +578,119 @@ void FIOSInputInterface::QueueKeyInput(int32 Key, int32 Char) FIOSInputInterface::KeyInputStack.Add(Char); } +void FIOSInputInterface::EnableMotionData(bool bEnable) +{ + bPauseMotion = !bEnable; + +#if !PLATFORM_TVOS + if (bPauseMotion && MotionManager != nil) + { + [ReferenceAttitude release]; + ReferenceAttitude = nil; + + [MotionManager release]; + MotionManager = nil; + } + // When enabled MotionManager will be initialized on first use +#endif +} + +bool FIOSInputInterface::IsMotionDataEnabled() const +{ + return !bPauseMotion; +} void FIOSInputInterface::GetMovementData(FVector& Attitude, FVector& RotationRate, FVector& Gravity, FVector& Acceleration) { #if !PLATFORM_TVOS - if (!bPauseMotion) + // initialize on first use + if (MotionManager == nil) { - // initialize on first use - if (MotionManager == nil) + // Look to see if we can create the motion manager + MotionManager = [[CMMotionManager alloc] init]; + + // Check to see if the device supports full motion (gyro + accelerometer) + if (MotionManager.deviceMotionAvailable) { - // Look to see if we can create the motion manager - MotionManager = [[CMMotionManager alloc] init]; + MotionManager.deviceMotionUpdateInterval = 0.02; - // Check to see if the device supports full motion (gyro + accelerometer) - if (MotionManager.deviceMotionAvailable) - { - MotionManager.deviceMotionUpdateInterval = 0.02; - - // Start the Device updating motion - [MotionManager startDeviceMotionUpdates]; - } - else - { - [MotionManager startAccelerometerUpdates]; - CenterPitch = CenterPitch = 0; - bIsCalibrationRequested = false; - } - } - - // do we have full motion data? - if (MotionManager.deviceMotionActive) - { - // Grab the values - CMAttitude* CurrentAttitude = MotionManager.deviceMotion.attitude; - CMRotationRate CurrentRotationRate = MotionManager.deviceMotion.rotationRate; - CMAcceleration CurrentGravity = MotionManager.deviceMotion.gravity; - CMAcceleration CurrentUserAcceleration = MotionManager.deviceMotion.userAcceleration; - - // apply a reference attitude if we have been calibrated away from default - if (ReferenceAttitude) - { - [CurrentAttitude multiplyByInverseOfAttitude : ReferenceAttitude]; - } - - // convert to UE3 - Attitude = FVector(float(CurrentAttitude.pitch), float(CurrentAttitude.yaw), float(CurrentAttitude.roll)); - RotationRate = FVector(float(CurrentRotationRate.x), float(CurrentRotationRate.y), float(CurrentRotationRate.z)); - Gravity = FVector(float(CurrentGravity.x), float(CurrentGravity.y), float(CurrentGravity.z)); - Acceleration = FVector(float(CurrentUserAcceleration.x), float(CurrentUserAcceleration.y), float(CurrentUserAcceleration.z)); + // Start the Device updating motion + [MotionManager startDeviceMotionUpdates]; } else { - // get the plain accleration - CMAcceleration RawAcceleration = [MotionManager accelerometerData].acceleration; - FVector NewAcceleration(RawAcceleration.x, RawAcceleration.y, RawAcceleration.z); - - // storage for keeping the accelerometer values over time (for filtering) - static bool bFirstAccel = true; - - // how much of the previous frame's acceleration to keep - const float VectorFilter = bFirstAccel ? 0.0f : 0.85f; - bFirstAccel = false; - - // apply new accelerometer values to last frames - FilteredAccelerometer = FilteredAccelerometer * VectorFilter + (1.0f - VectorFilter) * NewAcceleration; - - // create an normalized acceleration vector - FVector FinalAcceleration = -FilteredAccelerometer.GetSafeNormal(); - - // calculate Roll/Pitch - float CurrentPitch = FMath::Atan2(FinalAcceleration.Y, FinalAcceleration.Z); - float CurrentRoll = -FMath::Atan2(FinalAcceleration.X, FinalAcceleration.Z); - - // if we want to calibrate, use the current values as center - if (bIsCalibrationRequested) - { - CenterPitch = CurrentPitch; - CenterRoll = CurrentRoll; - bIsCalibrationRequested = false; - } - - CurrentPitch -= CenterPitch; - CurrentRoll -= CenterRoll; - - Attitude = FVector(CurrentPitch, 0, CurrentRoll); - RotationRate = FVector(LastPitch - CurrentPitch, 0, LastRoll - CurrentRoll); - Gravity = FVector(0, 0, 0); - - // use the raw acceleration for acceleration - Acceleration = NewAcceleration; - - // remember for next time (for rotation rate) - LastPitch = CurrentPitch; - LastRoll = CurrentRoll; + [MotionManager startAccelerometerUpdates]; + CenterPitch = CenterPitch = 0; + bIsCalibrationRequested = false; } } + + // do we have full motion data? + if (MotionManager.deviceMotionActive) + { + // Grab the values + CMAttitude* CurrentAttitude = MotionManager.deviceMotion.attitude; + CMRotationRate CurrentRotationRate = MotionManager.deviceMotion.rotationRate; + CMAcceleration CurrentGravity = MotionManager.deviceMotion.gravity; + CMAcceleration CurrentUserAcceleration = MotionManager.deviceMotion.userAcceleration; + + // apply a reference attitude if we have been calibrated away from default + if (ReferenceAttitude) + { + [CurrentAttitude multiplyByInverseOfAttitude : ReferenceAttitude]; + } + + // convert to UE3 + Attitude = FVector(float(CurrentAttitude.pitch), float(CurrentAttitude.yaw), float(CurrentAttitude.roll)); + RotationRate = FVector(float(CurrentRotationRate.x), float(CurrentRotationRate.y), float(CurrentRotationRate.z)); + Gravity = FVector(float(CurrentGravity.x), float(CurrentGravity.y), float(CurrentGravity.z)); + Acceleration = FVector(float(CurrentUserAcceleration.x), float(CurrentUserAcceleration.y), float(CurrentUserAcceleration.z)); + } + else + { + // get the plain accleration + CMAcceleration RawAcceleration = [MotionManager accelerometerData].acceleration; + FVector NewAcceleration(RawAcceleration.x, RawAcceleration.y, RawAcceleration.z); + + // storage for keeping the accelerometer values over time (for filtering) + static bool bFirstAccel = true; + + // how much of the previous frame's acceleration to keep + const float VectorFilter = bFirstAccel ? 0.0f : 0.85f; + bFirstAccel = false; + + // apply new accelerometer values to last frames + FilteredAccelerometer = FilteredAccelerometer * VectorFilter + (1.0f - VectorFilter) * NewAcceleration; + + // create an normalized acceleration vector + FVector FinalAcceleration = -FilteredAccelerometer.GetSafeNormal(); + + // calculate Roll/Pitch + float CurrentPitch = FMath::Atan2(FinalAcceleration.Y, FinalAcceleration.Z); + float CurrentRoll = -FMath::Atan2(FinalAcceleration.X, FinalAcceleration.Z); + + // if we want to calibrate, use the current values as center + if (bIsCalibrationRequested) + { + CenterPitch = CurrentPitch; + CenterRoll = CurrentRoll; + bIsCalibrationRequested = false; + } + + CurrentPitch -= CenterPitch; + CurrentRoll -= CenterRoll; + + Attitude = FVector(CurrentPitch, 0, CurrentRoll); + RotationRate = FVector(LastPitch - CurrentPitch, 0, LastRoll - CurrentRoll); + Gravity = FVector(0, 0, 0); + + // use the raw acceleration for acceleration + Acceleration = NewAcceleration; + + // remember for next time (for rotation rate) + LastPitch = CurrentPitch; + LastRoll = CurrentRoll; + } #endif } @@ -680,7 +699,7 @@ void FIOSInputInterface::CalibrateMotion(uint32 PlayerIndex) #if !PLATFORM_TVOS // If we are using the motion manager, grab a reference frame. Note, once you set the Attitude Reference frame // all additional reference information will come from it - if (MotionManager.deviceMotionActive) + if (MotionManager && MotionManager.deviceMotionActive) { ReferenceAttitude = [MotionManager.deviceMotion.attitude retain]; } diff --git a/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSPlatformApplicationMisc.cpp b/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSPlatformApplicationMisc.cpp index 0ae70c901937..b3d08fcb6187 100644 --- a/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSPlatformApplicationMisc.cpp +++ b/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSPlatformApplicationMisc.cpp @@ -208,6 +208,18 @@ bool FIOSPlatformApplicationMisc::IsControllerAssignedToGamepad(int32 Controller return InputInterface->IsControllerAssignedToGamepad(ControllerId); } +void FIOSPlatformApplicationMisc::EnableMotionData(bool bEnable) +{ + FIOSInputInterface* InputInterface = (FIOSInputInterface*)CachedApplication->GetInputInterface(); + return InputInterface->EnableMotionData(bEnable); +} + +bool FIOSPlatformApplicationMisc::IsMotionDataEnabled() +{ + const FIOSInputInterface* InputInterface = (const FIOSInputInterface*)CachedApplication->GetInputInterface(); + return InputInterface->IsMotionDataEnabled(); +} + void FIOSPlatformApplicationMisc::ClipboardCopy(const TCHAR* Str) { #if !PLATFORM_TVOS diff --git a/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSView.cpp b/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSView.cpp index d3d289465430..4fd285b7442e 100644 --- a/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSView.cpp +++ b/Engine/Source/Runtime/ApplicationCore/Private/IOS/IOSView.cpp @@ -232,16 +232,11 @@ id GMetalDevice = nil; // Initialize some variables SwapCount = 0; -#if PLATFORM_TVOS - // @todo tvos: This may need to be exposed to the game so that when you click Menu it will background the app - // this is basically the same way Android handles the Back button (maybe we should pass Menu button as back... maybe) - // @todo embedded: this will not work, because this view hasn't been added to a hierarchy yet, so the IOSController is guaranteed to be null. DELAY THIS - AppDelegate.IOSController.controllerUserInteractionEnabled = NO; -#endif - // self.userInteractionEnabled = YES; // self.clearsContextBeforeDrawing = NO; +#if !PLATFORM_TVOS self.multipleTouchEnabled = YES; +#endif FMemory::Memzero(AllTouches, sizeof(AllTouches)); [self setAutoresizingMask: UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; diff --git a/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxWindow.cpp b/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxWindow.cpp index 134fb771fe0b..38b99382149e 100644 --- a/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxWindow.cpp +++ b/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxWindow.cpp @@ -420,14 +420,17 @@ void FLinuxWindow::BringToFront( bool bForce ) /** Native windows should implement this function by asking the OS to destroy OS-specific resource associated with the window (e.g. Win32 window handle) */ void FLinuxWindow::Destroy() { - OwningApplication->RemoveRevertFocusWindow( HWnd ); - OwningApplication->RemoveEventWindow( HWnd ); - OwningApplication->RemoveNotificationWindow( HWnd ); + if (HWnd) + { + OwningApplication->RemoveRevertFocusWindow( HWnd ); + OwningApplication->RemoveEventWindow( HWnd ); + OwningApplication->RemoveNotificationWindow( HWnd ); - UE_LOG(LogLinuxWindow, Verbose, TEXT("Destroying SDL Window '%p'\n"), HWnd); + UE_LOG(LogLinuxWindow, Verbose, TEXT("Destroying SDL Window '%p'\n"), HWnd); - SDL_DestroyWindow( HWnd ); - HWnd = nullptr; + SDL_DestroyWindow( HWnd ); + HWnd = nullptr; + } } /** Native window should implement this function by performing the equivalent of the Win32 minimize-to-taskbar operation */ diff --git a/Engine/Source/Runtime/ApplicationCore/Private/Mac/MacApplication.cpp b/Engine/Source/Runtime/ApplicationCore/Private/Mac/MacApplication.cpp index a81972825356..65da7081efd5 100644 --- a/Engine/Source/Runtime/ApplicationCore/Private/Mac/MacApplication.cpp +++ b/Engine/Source/Runtime/ApplicationCore/Private/Mac/MacApplication.cpp @@ -104,7 +104,7 @@ FMacApplication::FMacApplication() CacheKeyboardInputSource(); - WindowUnderCursor = FindSlateWindowUnderCursor(); + WindowUnderCursor = [FindSlateWindowUnderCursor() retain]; }, NSDefaultRunLoopMode, true); #if WITH_EDITOR @@ -170,6 +170,8 @@ FMacApplication::~FMacApplication() [KeyBoardLayoutData release]; KeyBoardLayoutData = nil; } + + [WindowUnderCursor release]; }, NSDefaultRunLoopMode, true); if (TextInputMethodSystem.IsValid()) @@ -366,7 +368,12 @@ void FMacApplication::DeferEvent(NSObject* Object) { FDeferredMacEvent DeferredEvent; - WindowUnderCursor = FindSlateWindowUnderCursor(); + FCocoaWindow* CursorWindow = FindSlateWindowUnderCursor(); + if (WindowUnderCursor != CursorWindow) + { + [WindowUnderCursor release]; + WindowUnderCursor = [CursorWindow retain]; + } if (Object && [Object isKindOfClass:[NSEvent class]]) { diff --git a/Engine/Source/Runtime/ApplicationCore/Public/GenericPlatform/GenericPlatformApplicationMisc.h b/Engine/Source/Runtime/ApplicationCore/Public/GenericPlatform/GenericPlatformApplicationMisc.h index 17f14a014d27..6a273e816997 100644 --- a/Engine/Source/Runtime/ApplicationCore/Public/GenericPlatform/GenericPlatformApplicationMisc.h +++ b/Engine/Source/Runtime/ApplicationCore/Public/GenericPlatform/GenericPlatformApplicationMisc.h @@ -201,6 +201,21 @@ struct APPLICATIONCORE_API FGenericPlatformApplicationMisc return FString(TEXT("None")); } + /* + * Whether to enable controller motion data polling (by default motion data is enabled) + * Some platforms may want to disable it to reduce battery drain + */ + static void EnableMotionData(bool bEnable) + {} + + /* + * Whether controller motion data polling is enabled (by default motion data is enabled) + */ + static bool IsMotionDataEnabled() + { + return true; + } + /** Copies text to the operating system clipboard. */ static void ClipboardCopy(const TCHAR* Str); diff --git a/Engine/Source/Runtime/ApplicationCore/Public/IOS/IOSBackgroundURLSessionHandler.h b/Engine/Source/Runtime/ApplicationCore/Public/IOS/IOSBackgroundURLSessionHandler.h index 858c05a19890..80e9041ab7b9 100644 --- a/Engine/Source/Runtime/ApplicationCore/Public/IOS/IOSBackgroundURLSessionHandler.h +++ b/Engine/Source/Runtime/ApplicationCore/Public/IOS/IOSBackgroundURLSessionHandler.h @@ -36,7 +36,7 @@ public: static NSURLSession* GetBackgroundSession(); //Gets a path stored in an FString of the working directory any completed temp files will be downloaded - static FString GetBackgroundSessionWorkingDirectoryPath(); + static const FString& GetBackgroundSessionWorkingDirectoryPath(); /** * Function that takes in a URL and figures out the location we should use as the temp storage URL diff --git a/Engine/Source/Runtime/ApplicationCore/Public/IOS/IOSFeedbackContext.h b/Engine/Source/Runtime/ApplicationCore/Public/IOS/IOSFeedbackContext.h index 9c7f0d76348a..b05eebcaacb2 100644 --- a/Engine/Source/Runtime/ApplicationCore/Public/IOS/IOSFeedbackContext.h +++ b/Engine/Source/Runtime/ApplicationCore/Public/IOS/IOSFeedbackContext.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once #include "Misc/App.h" diff --git a/Engine/Source/Runtime/ApplicationCore/Public/IOS/IOSInputInterface.h b/Engine/Source/Runtime/ApplicationCore/Public/IOS/IOSInputInterface.h index 2e935263471c..d4179676fe46 100644 --- a/Engine/Source/Runtime/ApplicationCore/Public/IOS/IOSInputInterface.h +++ b/Engine/Source/Runtime/ApplicationCore/Public/IOS/IOSInputInterface.h @@ -82,6 +82,9 @@ public: bool IsControllerAssignedToGamepad(int32 ControllerId) const; bool IsGamepadAttached() const; + void EnableMotionData(bool bEnable); + bool IsMotionDataEnabled() const; + private: FIOSInputInterface( const TSharedRef< FGenericApplicationMessageHandler >& InMessageHandler ); @@ -151,17 +154,16 @@ private: // should we allow controllers to send input bool bAllowControllers; + /** Is motion paused or not? */ + bool bPauseMotion; + #if !PLATFORM_TVOS /** Access to the ios devices motion */ CMMotionManager* MotionManager; - - /** Is motion paused or not? */ - bool bPauseMotion; /** Access to the ios devices tilt information */ CMAttitude* ReferenceAttitude; #endif - /** Last frames roll, for calculating rate */ float LastRoll; diff --git a/Engine/Source/Runtime/ApplicationCore/Public/IOS/IOSPlatformApplicationMisc.h b/Engine/Source/Runtime/ApplicationCore/Public/IOS/IOSPlatformApplicationMisc.h index 13e4edec2046..9b7c0112936e 100644 --- a/Engine/Source/Runtime/ApplicationCore/Public/IOS/IOSPlatformApplicationMisc.h +++ b/Engine/Source/Runtime/ApplicationCore/Public/IOS/IOSPlatformApplicationMisc.h @@ -19,6 +19,9 @@ struct APPLICATIONCORE_API FIOSPlatformApplicationMisc : public FGenericPlatform static void ResetGamepadAssignmentToController(int32 ControllerId); static bool IsControllerAssignedToGamepad(int32 ControllerId); + static void EnableMotionData(bool bEnable); + static bool IsMotionDataEnabled(); + static void ClipboardCopy(const TCHAR* Str); static void ClipboardPaste(class FString& Dest); diff --git a/Engine/Source/Runtime/AudioMixer/Classes/Generators/AudioGenerator.h b/Engine/Source/Runtime/AudioMixer/Classes/Generators/AudioGenerator.h index d437b111e508..4fb33c54a899 100644 --- a/Engine/Source/Runtime/AudioMixer/Classes/Generators/AudioGenerator.h +++ b/Engine/Source/Runtime/AudioMixer/Classes/Generators/AudioGenerator.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/AudioMixer/Private/AudioMixerSource.cpp b/Engine/Source/Runtime/AudioMixer/Private/AudioMixerSource.cpp index bf898e22a6a8..c1c3a7064312 100644 --- a/Engine/Source/Runtime/AudioMixer/Private/AudioMixerSource.cpp +++ b/Engine/Source/Runtime/AudioMixer/Private/AudioMixerSource.cpp @@ -66,7 +66,10 @@ namespace Audio { check(!InWaveInstance->WaveData->RawPCMData || InWaveInstance->WaveData->RawPCMDataSize); const int32 NumBytes = WaveInstance->WaveData->RawPCMDataSize; - NumFrames = NumBytes / (WaveInstance->WaveData->NumChannels * sizeof(int16)); + if (WaveInstance->WaveData->NumChannels > 0) + { + NumFrames = NumBytes / (WaveInstance->WaveData->NumChannels * sizeof(int16)); + } } // Unfortunately, we need to know if this is a vorbis source since channel maps are different for 5.1 vorbis files diff --git a/Engine/Source/Runtime/AudioMixer/Private/AudioMixerSourceManager.cpp b/Engine/Source/Runtime/AudioMixer/Private/AudioMixerSourceManager.cpp index 06900b2181a5..9e8ca6c79592 100644 --- a/Engine/Source/Runtime/AudioMixer/Private/AudioMixerSourceManager.cpp +++ b/Engine/Source/Runtime/AudioMixer/Private/AudioMixerSourceManager.cpp @@ -106,7 +106,6 @@ namespace Audio , NumSourceWorkers(4) , bInitialized(false) , bUsingSpatializationPlugin(false) - , MaxChannelsSupportedBySpatializationPlugin(1) { // Get a manual resetable event const bool bIsManualReset = true; @@ -262,10 +261,10 @@ namespace Audio if (SpatializationPlugin.IsValid()) { bUsingSpatializationPlugin = true; - MaxChannelsSupportedBySpatializationPlugin = MixerDevice->MaxChannelsSupportedBySpatializationPlugin; } bInitialized = true; + bPumpQueue = false; } void FMixerSourceManager::Update() @@ -283,22 +282,46 @@ namespace Audio } #endif - // If the command was triggered, then we want to do a swap of command buffers - if (CommandsProcessedEvent->Wait(0)) + if (FPlatformProcess::SupportsMultithreading()) { - int32 CurrentGameIndex = AudioThreadCommandBufferIndex.GetValue(); + // If the command was triggered, then we want to do a swap of command buffers + if (CommandsProcessedEvent->Wait(0)) + { + int32 CurrentGameIndex = AudioThreadCommandBufferIndex.GetValue(); - // This flags the audio render thread to be able to pump the next batch of commands - // And will allow the audio thread to write to a new command slot - const int32 NextIndex = (CurrentGameIndex + 1) & 1; + // This flags the audio render thread to be able to pump the next batch of commands + // And will allow the audio thread to write to a new command slot + const int32 NextIndex = (CurrentGameIndex + 1) & 1; - // Make sure we've actually emptied the command queue from the render thread before writing to it - check(CommandBuffers[NextIndex].SourceCommandQueue.Num() == 0); - AudioThreadCommandBufferIndex.Set(NextIndex); - RenderThreadCommandBufferIndex.Set(CurrentGameIndex); + // Make sure we've actually emptied the command queue from the render thread before writing to it + check(CommandBuffers[NextIndex].SourceCommandQueue.Num() == 0); + AudioThreadCommandBufferIndex.Set(NextIndex); + RenderThreadCommandBufferIndex.Set(CurrentGameIndex); - CommandsProcessedEvent->Reset(); + CommandsProcessedEvent->Reset(); + } } + else + { + int32 CurrentRenderIndex = RenderThreadCommandBufferIndex.GetValue(); + int32 CurrentGameIndex = AudioThreadCommandBufferIndex.GetValue(); + check(CurrentGameIndex == 0 || CurrentGameIndex == 1); + check(CurrentRenderIndex == 0 || CurrentRenderIndex == 1); + + // If these values are the same, that means the audio render thread has finished the last buffer queue so is ready for the next block + if (CurrentRenderIndex == CurrentGameIndex) + { + // This flags the audio render thread to be able to pump the next batch of commands + // And will allow the audio thread to write to a new command slot + const int32 NextIndex = !CurrentGameIndex; + + // Make sure we've actually emptied the command queue from the render thread before writing to it + check(CommandBuffers[NextIndex].SourceCommandQueue.Num() == 0); + AudioThreadCommandBufferIndex.Set(NextIndex); + bPumpQueue = true; + } + } + } void FMixerSourceManager::ReleaseSource(const int32 SourceId) @@ -1175,7 +1198,7 @@ namespace Audio } // If we have audio in our queue, we're still playing - if (SourceInfo.MixerSourceBuffer->GetNumBuffersQueued() > 0) + if (SourceInfo.MixerSourceBuffer->GetNumBuffersQueued() > 0 && NumChannels > 0) { SourceInfo.CurrentPCMBuffer = SourceInfo.MixerSourceBuffer->GetNextBuffer(); SourceInfo.CurrentAudioChunkNumFrames = SourceInfo.CurrentPCMBuffer->AudioData.Num() / NumChannels; @@ -1517,7 +1540,7 @@ namespace Audio SCOPE_CYCLE_COUNTER(STAT_AudioMixerHRTF); AUDIO_MIXER_CHECK(SpatializationPlugin.IsValid()); - AUDIO_MIXER_CHECK(SourceInfo.NumInputChannels <= MaxChannelsSupportedBySpatializationPlugin); + AUDIO_MIXER_CHECK(SourceInfo.NumInputChannels == 1); FAudioPluginSourceInputData AudioPluginInputData; AudioPluginInputData.AudioBuffer = &SourceInfo.SourceBuffer; @@ -2316,8 +2339,16 @@ namespace Audio SCOPE_CYCLE_COUNTER(STAT_AudioMixerSourceManagerUpdate); - // Get the this blocks commands before rendering audio - PumpCommandQueue(); + if (FPlatformProcess::SupportsMultithreading()) + { + // Get the this blocks commands before rendering audio + PumpCommandQueue(); + } + else if (bPumpQueue) + { + bPumpQueue = false; + PumpCommandQueue(); + } // Update pending tasks and release them if they're finished UpdatePendingReleaseData(); @@ -2386,9 +2417,12 @@ namespace Audio void FMixerSourceManager::PumpCommandQueue() { // If we're already triggered, we need to wait for the audio thread to reset it before pumping - if (CommandsProcessedEvent->Wait(0)) + if (FPlatformProcess::SupportsMultithreading()) { - return; + if (CommandsProcessedEvent->Wait(0)) + { + return; + } } int32 CurrentRenderThreadIndex = RenderThreadCommandBufferIndex.GetValue(); @@ -2404,8 +2438,16 @@ namespace Audio Commands.SourceCommandQueue.Reset(); - check(CommandsProcessedEvent != nullptr); - CommandsProcessedEvent->Trigger(); + if (FPlatformProcess::SupportsMultithreading()) + { + check(CommandsProcessedEvent != nullptr); + CommandsProcessedEvent->Trigger(); + } + else + { + RenderThreadCommandBufferIndex.Set(!CurrentRenderThreadIndex); + } + } void FMixerSourceManager::FlushCommandQueue() diff --git a/Engine/Source/Runtime/AudioMixer/Private/AudioMixerSourceManager.h b/Engine/Source/Runtime/AudioMixer/Private/AudioMixerSourceManager.h index 7e5c263532c2..e28980e1c37f 100644 --- a/Engine/Source/Runtime/AudioMixer/Private/AudioMixerSourceManager.h +++ b/Engine/Source/Runtime/AudioMixer/Private/AudioMixerSourceManager.h @@ -152,6 +152,7 @@ namespace Audio { checkSlow(InNumInChannels <= AUDIO_MIXER_MAX_OUTPUT_CHANNELS); checkSlow(InNumOutChannels <= AUDIO_MIXER_MAX_OUTPUT_CHANNELS); + FMemory::Memzero(ChannelStartGains, CopySize); } FORCEINLINE void Reset(int32 InNumInChannels, int32 InNumOutChannels) @@ -160,6 +161,7 @@ namespace Audio checkSlow(InNumOutChannels <= AUDIO_MIXER_MAX_OUTPUT_CHANNELS); CopySize = InNumInChannels * InNumOutChannels * sizeof(float); + FMemory::Memzero(ChannelStartGains, CopySize); FMemory::Memzero(ChannelDestinationGains, CopySize); } @@ -170,8 +172,6 @@ namespace Audio FORCEINLINE void SetChannelMap(const float* RESTRICT InChannelGains) { - // TODO: See if you can find a way to assign this without going back to memory. - FMemory::Memcpy(ChannelStartGains, ChannelDestinationGains, CopySize); FMemory::Memcpy(ChannelDestinationGains, InChannelGains, CopySize); } @@ -550,7 +550,9 @@ namespace Audio uint8 bInitialized : 1; uint8 bUsingSpatializationPlugin : 1; - int32 MaxChannelsSupportedBySpatializationPlugin; + + // Set to true when the audio source manager should pump the command queue + FThreadSafeBool bPumpQueue; friend class FMixerSourceVoice; }; diff --git a/Engine/Source/Runtime/AudioMixer/Private/DSP/BufferVectorOperations.cpp b/Engine/Source/Runtime/AudioMixer/Private/DSP/BufferVectorOperations.cpp index 8dd65220a326..25b5238f446d 100644 --- a/Engine/Source/Runtime/AudioMixer/Private/DSP/BufferVectorOperations.cpp +++ b/Engine/Source/Runtime/AudioMixer/Private/DSP/BufferVectorOperations.cpp @@ -462,7 +462,7 @@ namespace Audio * +------------+---------+---------+---------+---------+ * | Gain | g0 | g1 | g0 | g1 | * | | * | * | * | * | - * | Input | i0 | i0 | i0 | i0 | + * | Input | i0 | i0 | i1 | i1 | * | | = | = | = | = | * | Output | o0 | o1 | o2 | o3 | * +------------+---------+---------+---------+---------+ @@ -476,7 +476,7 @@ namespace Audio const VectorRegister GainDeltasVector = VectorDivide(VectorSubtract(DestinationVector, GainVector), NumFramesVector); // To help with stair stepping, we initialize the second frame in GainVector to be half a GainDeltas vector higher than the first frame. - const VectorRegister VectorOfHalf = VectorSet(0.0f, 0.0f, 0.5f, 0.5f); + const VectorRegister VectorOfHalf = VectorSet(0.5f, 0.5f, 1.0f, 1.0f); const VectorRegister HalfOfDeltaVector = VectorMultiply(GainDeltasVector, VectorOfHalf); GainVector = VectorAdd(GainVector, HalfOfDeltaVector); @@ -562,7 +562,7 @@ namespace Audio const VectorRegister GainDeltasVector1 = VectorDivide(VectorSubtract(DestinationVector1, GainVector1), NumFramesVector); // To help with stair stepping, we initialize the second frame in GainVector to be half a GainDeltas vector higher than the first frame. - const VectorRegister VectorOfHalf = VectorSet(0.0f, 0.0f, 0.5f, 0.5f); + const VectorRegister VectorOfHalf = VectorSet(0.5f, 0.5f, 1.0f, 1.0f); const VectorRegister HalfOfDeltaVector1 = VectorMultiply(GainDeltasVector1, VectorOfHalf); GainVector1 = VectorAdd(GainVector1, HalfOfDeltaVector1); @@ -1410,7 +1410,7 @@ namespace Audio { for (int32 InputChannelIndex = 0; InputChannelIndex < NumSourceChannels; InputChannelIndex++) { - const int32 GainMatrixIndex = OutputChannelIndex * NumDestinationChannels + InputChannelIndex; + const int32 GainMatrixIndex = InputChannelIndex * NumDestinationChannels + OutputChannelIndex; GainDeltas[GainMatrixIndex] = (EndGains[GainMatrixIndex] - StartGains[GainMatrixIndex]) / NumFrames; } } diff --git a/Engine/Source/Runtime/AudioMixer/Private/Generators/AudioGenerator.cpp b/Engine/Source/Runtime/AudioMixer/Private/Generators/AudioGenerator.cpp index 59cb3eae1a00..e76a238307b2 100644 --- a/Engine/Source/Runtime/AudioMixer/Private/Generators/AudioGenerator.cpp +++ b/Engine/Source/Runtime/AudioMixer/Private/Generators/AudioGenerator.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "Generators/AudioGenerator.h" diff --git a/Engine/Source/Runtime/Core/Private/Async/TaskGraph.cpp b/Engine/Source/Runtime/Core/Private/Async/TaskGraph.cpp index 2f183e905760..edb74cff5f35 100644 --- a/Engine/Source/Runtime/Core/Private/Async/TaskGraph.cpp +++ b/Engine/Source/Runtime/Core/Private/Async/TaskGraph.cpp @@ -609,11 +609,9 @@ public: } #endif + TStatId StatName; TStatId StallStatId; bool bCountAsStall = false; -#if STATS - TStatId StatName; - FCycleCounter ProcessingTasks; if (ThreadId == ENamedThreads::GameThread) { StatName = GET_STATID(STAT_TaskGraph_GameTasks); @@ -629,12 +627,19 @@ public: } // else StatName = none, we need to let the scope empty so that the render thread submits tasks in a timely manner. } +#if STATS else if (ThreadId != ENamedThreads::StatsThread) +#else + else +#endif { StatName = GET_STATID(STAT_TaskGraph_OtherTasks); StallStatId = GET_STATID(STAT_TaskGraph_OtherStalls); bCountAsStall = true; } + +#if STATS + FCycleCounter ProcessingTasks; bool bTasksOpen = false; if (FThreadStats::IsCollectingData(StatName)) { diff --git a/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformMisc.cpp b/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformMisc.cpp index d119faa6a1d7..054baf9a5844 100644 --- a/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformMisc.cpp +++ b/Engine/Source/Runtime/Core/Private/GenericPlatform/GenericPlatformMisc.cpp @@ -1322,3 +1322,40 @@ FString FGenericPlatformMisc::LoadTextFileFromPlatformPackage(const FString& Rel FFileHelper::LoadFileToString(Result, *Path); return Result; } + +void FGenericPlatformMisc::ParseChunkIdPakchunkIndexMapping(TArray ChunkIndexMappingData, TMap& OutMapping) +{ + OutMapping.Empty(); + + const TCHAR* PropertyOldChunkIndex = TEXT("Old"); + const TCHAR* PropertyNewChunkIndex = TEXT("New"); + for (const FString& Entry : ChunkIndexMappingData) + { + // Remove parentheses + FString EntryContent = Entry.Mid(1, Entry.Len() - 2); + TArray EntryProperties; + EntryContent.ParseIntoArray(EntryProperties, TEXT(",")); + + int32 ChunkId = -1; + int32 PakchunkIndex = -1; + for (const FString EntryProperty : EntryProperties) + { + TArray MappingKeyValue; + EntryProperty.ParseIntoArray(MappingKeyValue, TEXT("=")); + + if (MappingKeyValue[0].Equals(PropertyOldChunkIndex, ESearchCase::IgnoreCase)) + { + LexTryParseString(ChunkId, *MappingKeyValue[1]); + } + else if (MappingKeyValue[0].Equals(PropertyNewChunkIndex, ESearchCase::IgnoreCase)) + { + LexTryParseString(PakchunkIndex, *MappingKeyValue[1]); + } + } + + if (ChunkId != -1 && PakchunkIndex != -1 && ChunkId != PakchunkIndex && !OutMapping.Contains(ChunkId)) + { + OutMapping.Add(ChunkId, PakchunkIndex); + } + } +} \ No newline at end of file diff --git a/Engine/Source/Runtime/Core/Private/HAL/Allocators/PooledVirtualMemoryAllocator.cpp b/Engine/Source/Runtime/Core/Private/HAL/Allocators/PooledVirtualMemoryAllocator.cpp index f9beaeeb092a..1e4db86de3b3 100644 --- a/Engine/Source/Runtime/Core/Private/HAL/Allocators/PooledVirtualMemoryAllocator.cpp +++ b/Engine/Source/Runtime/Core/Private/HAL/Allocators/PooledVirtualMemoryAllocator.cpp @@ -43,7 +43,10 @@ void* FPooledVirtualMemoryAllocator::Allocate(SIZE_T Size) { if (Size > Limits::MaxAllocationSizeToPool) { - return FPlatformMemory::BinnedAllocFromOS(Size); + // do not report to LLM here, the platform functions will do that + FScopeLock Lock(&OsAllocatorCacheLock); + void* Ptr = OsAllocatorCache.Allocate(Size); + return Ptr; } else { @@ -97,7 +100,9 @@ void FPooledVirtualMemoryAllocator::Free(void* Ptr, SIZE_T Size) { if (Size > Limits::MaxAllocationSizeToPool) { - return FPlatformMemory::BinnedFreeToOS(Ptr, Size); + // do not report to LLM here, the platform functions will do that + FScopeLock Lock(&OsAllocatorCacheLock); + OsAllocatorCache.Free(Ptr, Size); } else { @@ -219,7 +224,10 @@ void FPooledVirtualMemoryAllocator::DestroyPool(FPoolDescriptorBase* Pool) void FPooledVirtualMemoryAllocator::FreeAll() { - // Currently, there's nothing to trim. + FScopeLock Lock(&OsAllocatorCacheLock); + OsAllocatorCache.FreeAll(); + + // Currently, there's nothing else to trim. // We could avoid deleting pools on Free() and instead keep them in a separate list to delete on FreeAll() (unless they're reused before that). // That would be a speed optimization and not a size optimization so I'm not going for this at this point, this method is speedy enough. }; diff --git a/Engine/Source/Runtime/Core/Private/HAL/LowLevelMemTracker.cpp b/Engine/Source/Runtime/Core/Private/HAL/LowLevelMemTracker.cpp index 7e2a5d24d274..55997382f28f 100644 --- a/Engine/Source/Runtime/Core/Private/HAL/LowLevelMemTracker.cpp +++ b/Engine/Source/Runtime/Core/Private/HAL/LowLevelMemTracker.cpp @@ -10,23 +10,12 @@ #include "HAL/IConsoleManager.h" #if ENABLE_LOW_LEVEL_MEM_TRACKER - -// uncomment this to use MemPro (note: MemPro.cpp/need to be added to the project for this to work) -//#define ENABLE_MEMPRO +#include "MemPro/MemProProfiler.h" // There is a little memory and cpu overhead in tracking peak memory but it is generally more useful than current memory. // Disable if you need a little more memory or speed #define LLM_TRACK_PEAK_MEMORY 0 // currently disabled because there was a problem with tracking peaks from multiple threads. -#ifdef ENABLE_MEMPRO -namespace MemProProfiler -{ - ELLMTag TrackTag = ELLMTag::RHIMisc; // GenericTagCount to track all allocs - bool bStart = true; -} -#include "MemPro.hpp" -#endif //ENABLE_MEMPRO - TAutoConsoleVariable CVarLLMWriteInterval( TEXT("LLM.LLMWriteInterval"), 5, @@ -310,7 +299,7 @@ public: #endif void TrackAllocation(const void* Ptr, uint64 Size, ELLMTag DefaultTag, ELLMTracker Tracker, ELLMAllocType AllocType); void TrackFree(const void* Ptr, ELLMTracker Tracker, ELLMAllocType AllocType); - void OnAllocMoved(const void* Dest, const void* Source); + void OnAllocMoved(const void* Dest, const void* Source, uint64& OutSize, int64& OutTag); void TrackMemory(int64 Tag, int64 Amount); @@ -538,6 +527,9 @@ FLowLevelMemTracker::FLowLevelMemTracker() FLowLevelMemTracker::~FLowLevelMemTracker() { + // Ensure that we skip any further tracking since it will fail after this destructor + bIsDisabled = true; + for (int32 TrackerIndex = 0; TrackerIndex < (int32)ELLMTracker::Max; ++TrackerIndex) { Trackers[TrackerIndex]->~FLLMTracker(); @@ -582,6 +574,11 @@ void FLowLevelMemTracker::UpdateStatsPerFrame(const TCHAR* LogName) GetTracker(ELLMTracker::Default)->SetTotalTags(ELLMTag::UntaggedTotal, ELLMTag::TrackedTotal); bFirstTimeUpdating = false; + +#if MEMPRO_ENABLED + FMemProProfiler::PostInit(); +#endif + } // update the trackers @@ -780,12 +777,31 @@ FLLMTracker* FLowLevelMemTracker::GetTracker(ELLMTracker Tracker) void FLowLevelMemTracker::OnLowLevelAllocMoved(ELLMTracker Tracker, const void* Dest, const void* Source) { - if (bIsDisabled) + if (bIsDisabled || GIsRequestingExit) { return; } - GetTracker(Tracker)->OnAllocMoved(Dest, Source); + //update the allocation map + uint64 Size; + int64 Tag; + GetTracker(Tracker)->OnAllocMoved(Dest, Source, Size, Tag); + + // update external memory trackers (ideally would want a proper 'move' option on these) + if (Tracker == ELLMTracker::Default) + { + FPlatformMemory::OnLowLevelMemory_Free(Source, Size, Tag); + FPlatformMemory::OnLowLevelMemory_Alloc(Dest, Size, Tag); + } + +#if MEMPRO_ENABLED + if (FMemProProfiler::IsTrackingTag( (ELLMTag)Tag) ) + { + MEMPRO_TRACK_FREE((void*)Source); + MEMPRO_TRACK_ALLOC((void*)Dest, (size_t)Size); + } +#endif + } bool FLowLevelMemTracker::Exec(const TCHAR* Cmd, FOutputDevice& Ar) @@ -912,6 +928,16 @@ int64 FLowLevelMemTracker::GetActiveTag(ELLMTracker Tracker) return GetTracker(Tracker)->GetActiveTag(); } +void FLowLevelMemTracker::DumpTag( ELLMTracker Tracker, const char* FileName, int LineNumber ) +{ + int64 Tag = GetActiveTag(Tracker); + const TCHAR* TagName = FindTagName(Tag); + + FPlatformMisc::LowLevelOutputDebugStringf( TEXT("LLM TAG: %s (%lld) @ %s:%d\n"), TagName ? TagName : TEXT(""), Tag, FileName ? ANSI_TO_TCHAR(FileName) : TEXT("?"), LineNumber ); +} + + + FLLMScope::FLLMScope(FName StatIDName, ELLMTagSet Set, ELLMTracker Tracker) { @@ -1254,10 +1280,20 @@ void FLLMTracker::TrackFree(const void* Ptr, ELLMTracker Tracker, ELLMAllocType #endif } -void FLLMTracker::OnAllocMoved(const void* Dest, const void* Source) +void FLLMTracker::OnAllocMoved(const void* Dest, const void* Source, uint64& OutSize, int64& OutTag) { LLMMap::Values Values = GetAllocationMap().Remove(Source); GetAllocationMap().Add(Dest, Values.Value1, Values.Value2); + + + const FLLMTracker::FLowLevelAllocInfo& AllocInfo = Values.Value2; +#if LLM_USE_ALLOC_INFO_STRUCT + OutTag = AllocInfo.Tag; +#else + OutTag = (int64)AllocInfo; +#endif + + OutSize = Values.Value1; } void FLLMTracker::TrackMemory(int64 Tag, int64 Amount) @@ -1579,8 +1615,8 @@ void FLLMTracker::FLLMThreadState::TrackAllocation(const void* Ptr, uint64 Size, FPlatformMemory::OnLowLevelMemory_Alloc(Ptr, Size, Tag); } -#ifdef ENABLE_MEMPRO - if (MemProProfiler::bStart && Tracker == ELLMTracker::Default && (MemProProfiler::TrackTag == ELLMTag::GenericTagCount || MemProProfiler::TrackTag == (ELLMTag)Tag)) +#if MEMPRO_ENABLED + if (FMemProProfiler::IsTrackingTag( (ELLMTag)Tag) ) { MEMPRO_TRACK_ALLOC((void*)Ptr, (size_t)Size); } @@ -1600,8 +1636,8 @@ void FLLMTracker::FLLMThreadState::TrackFree(const void* Ptr, int64 Tag, uint64 FPlatformMemory::OnLowLevelMemory_Free(Ptr, Size, Tag); } -#ifdef ENABLE_MEMPRO - if (MemProProfiler::bStart && Tracker == ELLMTracker::Default && (MemProProfiler::TrackTag == ELLMTag::GenericTagCount || MemProProfiler::TrackTag == (ELLMTag)Tag)) +#if MEMPRO_ENABLED + if (FMemProProfiler::IsTrackingTag( (ELLMTag)Tag) ) { MEMPRO_TRACK_FREE((void*)Ptr); } diff --git a/Engine/Source/Runtime/Core/Private/HAL/ThreadHeartBeat.cpp b/Engine/Source/Runtime/Core/Private/HAL/ThreadHeartBeat.cpp index 2a468b405a10..126f19b614ec 100644 --- a/Engine/Source/Runtime/Core/Private/HAL/ThreadHeartBeat.cpp +++ b/Engine/Source/Runtime/Core/Private/HAL/ThreadHeartBeat.cpp @@ -200,6 +200,7 @@ void FORCENOINLINE FThreadHeartBeat::OnHang(double HangDuration, uint32 ThreadTh for (int32 Idx = 0; Idx < NumStackFrames; Idx++) { ANSICHAR Buffer[1024]; + Buffer[0] = '\0'; FPlatformStackWalk::ProgramCounterToHumanReadableString(Idx, StackFrames[Idx], Buffer, sizeof(Buffer)); StackLines.Add(Buffer); } @@ -389,12 +390,14 @@ uint32 FThreadHeartBeat::CheckHeartBeat(double& OutHangDuration) if (ConfigHangDuration > 0.0) { // Check heartbeat for all threads and return thread ID of the thread that hung. + // Note: We only return a thread id for a thread that has updated since the last hang, i.e. is still alive + // This avoids the case where a user may be in a deep and minorly varying callstack and flood us with reports for (TPair& LastHeartBeat : ThreadHeartBeat) { FHeartBeatInfo& HeartBeatInfo = LastHeartBeat.Value; - if (HeartBeatInfo.SuspendedCount == 0 && (CurrentTime - HeartBeatInfo.LastHeartBeatTime) > HeartBeatInfo.HangDuration) + if (HeartBeatInfo.SuspendedCount == 0 && (CurrentTime - HeartBeatInfo.LastHeartBeatTime) > HeartBeatInfo.HangDuration && HeartBeatInfo.LastHeartBeatTime >= HeartBeatInfo.LastHangTime) { - HeartBeatInfo.LastHeartBeatTime = CurrentTime; + HeartBeatInfo.LastHangTime = CurrentTime; OutHangDuration = HeartBeatInfo.HangDuration; return LastHeartBeat.Key; } diff --git a/Engine/Source/Runtime/Core/Private/IOS/IOSPlatformMisc.cpp b/Engine/Source/Runtime/Core/Private/IOS/IOSPlatformMisc.cpp index 1b5b2d6e2f0b..55e7f4a90e82 100644 --- a/Engine/Source/Runtime/Core/Private/IOS/IOSPlatformMisc.cpp +++ b/Engine/Source/Runtime/Core/Private/IOS/IOSPlatformMisc.cpp @@ -312,7 +312,9 @@ EDeviceScreenOrientation ConvertFromUIInterfaceOrientation(UIInterfaceOrientatio } #endif +#if !PLATFORM_TVOS UIInterfaceOrientation GInterfaceOrientation = UIInterfaceOrientationUnknown; +#endif EDeviceScreenOrientation FIOSPlatformMisc::GetDeviceOrientation() { diff --git a/Engine/Source/Runtime/Core/Private/MemPro/MemProProfiler.cpp b/Engine/Source/Runtime/Core/Private/MemPro/MemProProfiler.cpp new file mode 100644 index 000000000000..9118ce7ab2bc --- /dev/null +++ b/Engine/Source/Runtime/Core/Private/MemPro/MemProProfiler.cpp @@ -0,0 +1,133 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +#include "MemPro/MemProProfiler.h" + +#if MEMPRO_ENABLED +#include "Misc/CString.h" +#include "Misc/CoreDelegates.h" +#include "HAL/IConsoleManager.h" + +DEFINE_LOG_CATEGORY_STATIC(LogMemPro, Log, All); + +/* NB. you can enable MemPro tracking after engine init by adding something like this to the command line: + * -execcmds="MemPro.LLMTag RHIMisc, MemPro.Enabled 1" + */ + + + +/* + * Main runtime switch for MemPro support + */ +int32 GMemProEnabled = 0; //edit this and set to 1 to track from startup. You probably want to edit GMemProTrackTag too +static FAutoConsoleVariableRef CVarMemProEnable( + TEXT("MemPro.Enabled"), + GMemProEnabled, + TEXT("Enable MemPro memory tracking.\n"), + ECVF_Default +); + + +/* + * the LLM tag to track in MemPro, or ELLMTag::GenericTagCount to track all + */ +#if ENABLE_LOW_LEVEL_MEM_TRACKER + + ELLMTag GMemProTrackTag = ELLMTag::EngineMisc; + +#endif //ENABLE_LOW_LEVEL_MEM_TRACKER + + + +/* + * helper function to track a tag + */ +#if ENABLE_LOW_LEVEL_MEM_TRACKER +void FMemProProfiler::TrackTag( ELLMTag Tag ) +{ + GMemProTrackTag = Tag; +} +#endif //ENABLE_LOW_LEVEL_MEM_TRACKER + + +/* + * helper function to track a tag given its name + */ +#if ENABLE_LOW_LEVEL_MEM_TRACKER +void FMemProProfiler::TrackTagByName( const TCHAR* TagName ) +{ + //sanity check + if( TagName == nullptr || FCString::Strlen(TagName) == 0 ) + { + UE_LOG( LogMemPro, Display, TEXT("please specify an LLM tag or * to track all") ); + return; + } + + //check whether they want to track all tags + if( FCString::Strcmp(TagName, TEXT("*") ) == 0 ) + { + TrackTag( ELLMTag::GenericTagCount ); + UE_LOG( LogMemPro, Display, TEXT("tracking all LLM tags" ) ); + } + else + { + //find the specific tag to track + uint64 TagIndex = (uint64)ELLMTag::Paused; + if ( FLowLevelMemTracker::Get().FindTagByName(TagName, TagIndex) && TagIndex < LLM_TAG_COUNT ) + { + TrackTag( (ELLMTag)TagIndex ); + UE_LOG( LogMemPro, Display, TEXT("tracking LLM tag \'%s\'" ), TagName ); + } + else + { + UE_LOG( LogMemPro, Display, TEXT("Unknown LLM tag \'%s\'" ), TagName ); + } + } +} +#endif //ENABLE_LOW_LEVEL_MEM_TRACKER + + +/* + * console command to get mempro to track a specific LLM tag + */ +#if ENABLE_LOW_LEVEL_MEM_TRACKER +static FAutoConsoleCommand MemProTrackLLMTag( + TEXT("MemPro.LLMTag"), + TEXT("Capture a specific LLM tag with MemPro"), + FConsoleCommandWithArgsDelegate::CreateLambda( [](const TArray& Args ) + { + FMemProProfiler::TrackTagByName( (Args.Num() == 0) ? nullptr : *Args[0] ); + }) +); +#endif //ENABLE_LOW_LEVEL_MEM_TRACKER + + +/* + * query the port that MemPro might be using so other development tools can steer clear if necessary + */ +bool FMemProProfiler::IsUsingPort( uint32 Port ) +{ +#if defined(MEMPRO_WRITE_DUMP) + return false; +#else + return Port == FCStringAnsi::Atoi(MEMPRO_PORT); +#endif +} + + +/* + * initialisation for MemPro. IT may have + */ + +void FMemProProfiler::PostInit() +{ + //shutdown MemPro when the engine is shutting down so that the send thread terminates cleanly + FCoreDelegates::OnPreExit.AddLambda( []() + { + GMemProEnabled = 0; + MemPro::Disconnect(); + MemPro::Shutdown(); + }); + +} + +#endif //MEMPRO_ENABLED diff --git a/Engine/Source/Runtime/Core/Private/Misc/ConfigCacheIni.cpp b/Engine/Source/Runtime/Core/Private/Misc/ConfigCacheIni.cpp index b7ef69ce58dc..9291746ccbe9 100644 --- a/Engine/Source/Runtime/Core/Private/Misc/ConfigCacheIni.cpp +++ b/Engine/Source/Runtime/Core/Private/Misc/ConfigCacheIni.cpp @@ -3228,16 +3228,7 @@ static void GetSourceIniHierarchyFilenames(const TCHAR* InBaseIniName, const TCH OutHierarchy.KeySort([](const EConfigFileHierarchy& A, const EConfigFileHierarchy& B) { return (A < B); }); } -/** - * Calculates the name of a dest (generated) .ini file for a given base (ie Engine, Game, etc) - * - * @param IniBaseName Base name of the .ini (Engine, Game) - * @param PlatformName Name of the platform to get the .ini path for (nullptr means to use the current platform) - * @param GeneratedConfigDir The base folder that will contain the generated config files. - * - * @return Standardized .ini filename - */ -static FString GetDestIniFilename(const TCHAR* BaseIniName, const TCHAR* PlatformName, const TCHAR* GeneratedConfigDir) +FString FConfigCacheIni::GetDestIniFilename(const TCHAR* BaseIniName, const TCHAR* PlatformName, const TCHAR* GeneratedConfigDir) { // figure out what to look for on the commandline for an override FString CommandLineSwitch = FString::Printf(TEXT("%sINI="), BaseIniName); @@ -3248,14 +3239,13 @@ static FString GetDestIniFilename(const TCHAR* BaseIniName, const TCHAR* Platfor { FString Name(PlatformName ? PlatformName : ANSI_TO_TCHAR(FPlatformProperties::PlatformName())); - FString BaseIniNameString = BaseIniName; - if (BaseIniNameString.Contains(GeneratedConfigDir)) + // if the BaseIniName doens't contain the config dir, put it all together + if (FCString::Stristr(BaseIniName, GeneratedConfigDir) != nullptr) { - IniFilename = BaseIniNameString; + IniFilename = BaseIniName; } else { - // put it all together IniFilename = FString::Printf(TEXT("%s%s/%s.ini"), GeneratedConfigDir, *Name, BaseIniName); } } diff --git a/Engine/Source/Runtime/Core/Private/Misc/CoreDelegates.cpp b/Engine/Source/Runtime/Core/Private/Misc/CoreDelegates.cpp index 0a2194871bc3..3bec705ae356 100644 --- a/Engine/Source/Runtime/Core/Private/Misc/CoreDelegates.cpp +++ b/Engine/Source/Runtime/Core/Private/Misc/CoreDelegates.cpp @@ -182,12 +182,12 @@ FSimpleMulticastDelegate& FCoreDelegates::GetOutOfMemoryDelegate() FCoreDelegates::FGetOnScreenMessagesDelegate FCoreDelegates::OnGetOnScreenMessages; -typedef void(*TSigningKeyFunc)(uint8[64], uint8[64]); +typedef void(*TSigningKeyFunc)(TArray&, TArray&); typedef void(*TEncryptionKeyFunc)(unsigned char[32]); void RegisterSigningKeyCallback(TSigningKeyFunc InCallback) { - FCoreDelegates::GetPakSigningKeysDelegate().BindLambda([InCallback](uint8 OutExponent[64], uint8 OutModulus[64]) + FCoreDelegates::GetPakSigningKeysDelegate().BindLambda([InCallback](TArray& OutExponent, TArray& OutModulus) { InCallback(OutExponent, OutModulus); }); diff --git a/Engine/Source/Runtime/Core/Private/Misc/SecureHash.cpp b/Engine/Source/Runtime/Core/Private/Misc/SecureHash.cpp index 298bd8d60121..e067f45b0530 100644 --- a/Engine/Source/Runtime/Core/Private/Misc/SecureHash.cpp +++ b/Engine/Source/Runtime/Core/Private/Misc/SecureHash.cpp @@ -610,6 +610,16 @@ uint32 GetTypeHash(FSHAHash const& InKey) return FCrc::MemCrc32(InKey.Hash, sizeof(InKey.Hash)); } +FString LexToString(const FSHAHash& InHash) +{ + return InHash.ToString(); +} + +void LexFromString(FSHAHash& InHash, const TCHAR* InString) +{ + InHash.FromString(InString); +} + FSHA1::FSHA1() { m_block = (SHA1_WORKSPACE_BLOCK *)m_workspace; diff --git a/Engine/Source/Runtime/Core/Private/Modules/ModuleManager.cpp b/Engine/Source/Runtime/Core/Private/Modules/ModuleManager.cpp index d62ed0b88e2b..d3fef626f920 100644 --- a/Engine/Source/Runtime/Core/Private/Modules/ModuleManager.cpp +++ b/Engine/Source/Runtime/Core/Private/Modules/ModuleManager.cpp @@ -934,6 +934,20 @@ void FModuleManager::SetModuleFilename(FName ModuleName, const FString& Filename Module->OriginalFilename = Filename; } } + +bool FModuleManager::HasAnyOverridenModuleFilename() const +{ + FScopeLock Lock(&ModulesCriticalSection); + for (const TPair& ModuleIt : Modules) + { + const FModuleInfo& CurModule = *ModuleIt.Value; + if(CurModule.Filename != CurModule.OriginalFilename) + { + return true; + } + } + return false; +} #endif void FModuleManager::ResetModulePathsCache() diff --git a/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformCrashContext.cpp b/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformCrashContext.cpp index a4da7488f73d..90b441913e16 100644 --- a/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformCrashContext.cpp +++ b/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformCrashContext.cpp @@ -139,6 +139,8 @@ void FWindowsPlatformCrashContext::AddPlatformSpecificProperties() const AddCrashProperty(TEXT("PlatformIsRunningWindows"), 1); // On windows track the crash type AddCrashProperty(TEXT("PlatformCallbackResult"), GetCrashType()); + + AddCrashProperty(TEXT("IsRunningOnBattery"), FPlatformMisc::IsRunningOnBattery()); } bool FWindowsPlatformCrashContext::GetPlatformAllThreadContextsString(FString& OutStr) const @@ -318,11 +320,13 @@ int32 ReportCrashUsingCrashReportClient(FWindowsPlatformCrashContext& InContext, GConfig->GetBool(TEXT("/Script/UnrealEd.CrashReportsPrivacySettings"), TEXT("bSendUnattendedBugReports"), bSendUnattendedBugReports, GEditorSettingsIni); } - if (BuildSettings::IsLicenseeVersion() && !UE_EDITOR) +#if !UE_EDITOR + if (BuildSettings::IsLicenseeVersion()) { // do not send unattended reports in licensees' builds except for the editor, where it is governed by the above setting bSendUnattendedBugReports = false; } +#endif if (bNoDialog && !bSendUnattendedBugReports) { diff --git a/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformMisc.cpp b/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformMisc.cpp index 3413e315cb1e..0edfd87f54c1 100644 --- a/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformMisc.cpp +++ b/Engine/Source/Runtime/Core/Private/Windows/WindowsPlatformMisc.cpp @@ -584,14 +584,24 @@ void FWindowsPlatformMisc::PlatformInit() */ static BOOL WINAPI ConsoleCtrlHandler(DWORD CtrlType) { + // Broadcast the termination the first time through. + bool IsRequestingExit = GIsRequestingExit; + static bool AppTermDelegateBroadcast = false; + if (!AppTermDelegateBroadcast) + { + GIsRequestingExit = true; + FCoreDelegates::ApplicationWillTerminateDelegate.Broadcast(); + AppTermDelegateBroadcast = true; + } + // Only "two-step Ctrl-C" if the termination event is Ctrl-C and the process // is considered interactive. Hard-terminate on all other cases. if (CtrlType != CTRL_C_EVENT || FApp::IsUnattended()) { - GIsRequestingExit = true; + IsRequestingExit = true; } - if (!GIsRequestingExit) + if (!IsRequestingExit) { UE_LOG(LogCore, Warning, TEXT("*** INTERRUPTED *** : SHUTTING DOWN")); UE_LOG(LogCore, Warning, TEXT("*** INTERRUPTED *** : CTRL-C TO FORCE QUIT")); @@ -611,11 +621,10 @@ static BOOL WINAPI ConsoleCtrlHandler(DWORD CtrlType) GError->Flush(); } - if (!GIsRequestingExit) + if (!IsRequestingExit) { - // Notify anyone listening that we're about to terminate - GIsRequestingExit = true; - FCoreDelegates::ApplicationWillTerminateDelegate.Broadcast(); + // We'll two-step Ctrl-C events to give processes like servers time to + // correctly terminate. Note the GIsRequestingExit is true now. return true; } diff --git a/Engine/Source/Runtime/Core/Public/Algo/FindSequence.h b/Engine/Source/Runtime/Core/Public/Algo/FindSequence.h index dbfed6c926a0..7c6921716345 100644 --- a/Engine/Source/Runtime/Core/Public/Algo/FindSequence.h +++ b/Engine/Source/Runtime/Core/Public/Algo/FindSequence.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/Core/Public/GenericPlatform/GenericPlatformInstallBundleManager.h b/Engine/Source/Runtime/Core/Public/GenericPlatform/GenericPlatformInstallBundleManager.h index ae8e57b77d46..36faaec34e75 100644 --- a/Engine/Source/Runtime/Core/Public/GenericPlatform/GenericPlatformInstallBundleManager.h +++ b/Engine/Source/Runtime/Core/Public/GenericPlatform/GenericPlatformInstallBundleManager.h @@ -50,6 +50,7 @@ enum class EInstallBundleModuleInitResult : int DistributionRootDownloadError, ManifestCreationError, ManifestDownloadError, + BackgroundDownloadsIniDownloadError, NoInternetConnectionError, Count }; @@ -66,6 +67,7 @@ inline const TCHAR* GetInstallBundleModuleInitResultString(EInstallBundleModuleI TEXT("DistributionRootDownloadError"), TEXT("ManifestCreationError"), TEXT("ManifestDownloadError"), + TEXT("BackgroundDownloadsIniDownloadError"), TEXT("NoInternetConnectionError"), }; static_assert(static_cast(EInstallBundleModuleInitResult::Count) == ARRAY_COUNT(Strings), ""); @@ -143,9 +145,9 @@ enum class EInstallBundleRequestInfoFlags : int32 EnqueuedBundlesForRemoval = (1 << 1), SkippedAlreadyMountedBundles = (1 << 2), SkippedBundlesQueuedForRemoval = (1 << 3), - SkippedBundlesQueuedForInstall = (1 << 5), // Only valid for removal requests - SkippedUnknownBundles = (1 << 6), - InitializationError = (1 << 7), // Can't enqueue because the bundle manager failed to initialize + SkippedBundlesQueuedForInstall = (1 << 4), // Only valid for removal requests + SkippedUnknownBundles = (1 << 5), + InitializationError = (1 << 6), // Can't enqueue because the bundle manager failed to initialize }; ENUM_CLASS_FLAGS(EInstallBundleRequestInfoFlags); @@ -156,6 +158,13 @@ struct FInstallBundleRequestInfo TArray BundlesQueuedForRemoval; }; +enum class EInstallBundleCancelFlags : int32 +{ + None = 0, + Resumable = (1 << 0), +}; +ENUM_CLASS_FLAGS(EInstallBundleCancelFlags); + enum class EInstallBundleManagerInitErrorHandlerResult { NotHandled, // Defer to the next handler @@ -195,9 +204,15 @@ public: virtual void RequestRemoveBundleOnNextInit(FName BundleName) = 0; - virtual void CancelBundle(FName BundleName) = 0; + virtual void CancelBundle(FName BundleName, EInstallBundleCancelFlags Flags) = 0; + + virtual void CancelAllBundles(EInstallBundleCancelFlags Flags) = 0; virtual TOptional GetBundleProgress(FName BundleName) const = 0; + + virtual bool IsNullInterface() const = 0; + + virtual void SetErrorSimulationCommands(const FString& CommandLine) {} }; class IPlatformInstallBundleManagerModule : public IModuleInterface diff --git a/Engine/Source/Runtime/Core/Public/GenericPlatform/GenericPlatformMisc.h b/Engine/Source/Runtime/Core/Public/GenericPlatform/GenericPlatformMisc.h index 188cf117b270..db4eb0f5dcea 100644 --- a/Engine/Source/Runtime/Core/Public/GenericPlatform/GenericPlatformMisc.h +++ b/Engine/Source/Runtime/Core/Public/GenericPlatform/GenericPlatformMisc.h @@ -1279,6 +1279,8 @@ public: */ static FString LoadTextFileFromPlatformPackage(const FString& RelativePath); + static void ParseChunkIdPakchunkIndexMapping(TArray ChunkIndexRedirects, TMap& OutMapping); + #if !UE_BUILD_SHIPPING /** * Returns any platform specific warning messages we want printed on screen diff --git a/Engine/Source/Runtime/Core/Public/HAL/Allocators/PooledVirtualMemoryAllocator.h b/Engine/Source/Runtime/Core/Public/HAL/Allocators/PooledVirtualMemoryAllocator.h index 92ba80d98ef4..66521e8b7aa9 100644 --- a/Engine/Source/Runtime/Core/Public/HAL/Allocators/PooledVirtualMemoryAllocator.h +++ b/Engine/Source/Runtime/Core/Public/HAL/Allocators/PooledVirtualMemoryAllocator.h @@ -4,6 +4,7 @@ #include "CoreTypes.h" #include "HAL/CriticalSection.h" +#include "HAL/Allocators/CachedOSPageAllocator.h" /** * This class pools OS allocations made from FMallocBinned2. @@ -17,7 +18,7 @@ * of pools that hold varied number of same-sized allocations (called blocks). Each bucket starts empty (linked list is empty). * * Whenever an allocation request arrives, it is first bucketed based on its size (if it is larger than the largest bucket, - * it is passed through to a platform function BinnedAllocFromOS()). Then, the linked list of that bucket is walked, and + * it is passed through to a caching OS allocator). Then, the linked list of that bucket is walked, and * the allocation is fulfilled by the first pool that has empty "blocks". If there are no such pool, a new pool is created * (with number of blocks being possibly larger than in the existing list, if any), this pool becomes the new head of the * list and the allocation happens there. @@ -54,6 +55,7 @@ * and we cannot "trim" anything here. Also, it does not make sense to put the global cap on the pooled memory * since BinnedAllocFromOS() can support only a limited number of allocations on some platforms. * + * CachedOSPageAllocator sits "below" this and is used for allocs larger than the largest bucketed. */ struct FPooledVirtualMemoryAllocator { @@ -81,7 +83,10 @@ private: enum Limits { NumAllocationSizeClasses = 64, - MaxAllocationSizeToPool = NumAllocationSizeClasses * 65536 + MaxAllocationSizeToPool = NumAllocationSizeClasses * 65536, + + MaxOSAllocCacheSize = 64 * 1024 * 1024, + MaxOSAllocsCached = 64 }; /** @@ -134,4 +139,10 @@ private: /** Destroys a pool */ void DestroyPool(FPoolDescriptorBase* Pool); + + /** Lock for accessing the caching allocator for larger allocs */ + FCriticalSection OsAllocatorCacheLock; + + /** Caching allocator for larger allocs */ + TCachedOSPageAllocator OsAllocatorCache; }; diff --git a/Engine/Source/Runtime/Core/Public/HAL/LowLevelMemStats.h b/Engine/Source/Runtime/Core/Public/HAL/LowLevelMemStats.h index fb4f244d8ec9..be2ce618201b 100644 --- a/Engine/Source/Runtime/Core/Public/HAL/LowLevelMemStats.h +++ b/Engine/Source/Runtime/Core/Public/HAL/LowLevelMemStats.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "HAL/LowLevelMemTracker.h" #include "Stats/Stats.h" diff --git a/Engine/Source/Runtime/Core/Public/HAL/LowLevelMemTracker.h b/Engine/Source/Runtime/Core/Public/HAL/LowLevelMemTracker.h index 23b334110968..ad51163cc396 100644 --- a/Engine/Source/Runtime/Core/Public/HAL/LowLevelMemTracker.h +++ b/Engine/Source/Runtime/Core/Public/HAL/LowLevelMemTracker.h @@ -321,6 +321,13 @@ extern FName LLMGetTagStat(ELLMTag Tag); #define LLM_PUSH_STATS_FOR_ASSET_TAGS() #endif +/** + * LLM tag dumping, to help with identifying mis-tagged items. Probably don't want to check in with these in use! + */ +#define LLM_DUMP_TAG() FLowLevelMemTracker::Get().DumpTag(ELLMTracker::Default,__FILE__,__LINE__); +#define LLM_DUMP_PLATFORM_TAG() FLowLevelMemTracker::Get().DumpTag(ELLMTracker::Platform,__FILE__,__LINE__); + + typedef void*(*LLMAllocFunction)(size_t); typedef void(*LLMFreeFunction)(void*, size_t); @@ -449,6 +456,9 @@ public: // Get the amount of memory for a tag from the given tracker int64 GetTagAmountForTracker(ELLMTracker Tracker, ELLMTag Tag); + // Dump the current tag for the given tracker to the output + void DumpTag( ELLMTracker Tracker, const char* FileName, int LineNumber ); + private: FLowLevelMemTracker(); @@ -556,4 +566,7 @@ protected: #define LLM_SCOPED_PAUSE_TRACKING_WITH_ENUM_AND_AMOUNT(...) #define LLM_SCOPED_PAUSE_TRACKING_WITH_STAT_AND_AMOUNT(...) #define LLM_PUSH_STATS_FOR_ASSET_TAGS() + #define LLM_DUMP_TAG() + #define LLM_DUMP_PLATFORM_TAG() + #endif // #if ENABLE_LOW_LEVEL_MEM_TRACKER diff --git a/Engine/Source/Runtime/Core/Public/HAL/ThreadHeartBeat.h b/Engine/Source/Runtime/Core/Public/HAL/ThreadHeartBeat.h index f2a426ab0d42..0acf12860b02 100644 --- a/Engine/Source/Runtime/Core/Public/HAL/ThreadHeartBeat.h +++ b/Engine/Source/Runtime/Core/Public/HAL/ThreadHeartBeat.h @@ -47,12 +47,15 @@ class CORE_API FThreadHeartBeat : public FRunnable { FHeartBeatInfo() : LastHeartBeatTime(0.0) + , LastHangTime(0.0) , SuspendedCount(0) , HangDuration(0) {} /** Time we last received a heartbeat for the current thread */ double LastHeartBeatTime; + /** Time we last detected a hang due to lack of heartbeats for the current thread */ + double LastHangTime; /** Suspended counter */ int32 SuspendedCount; /** The timeout for this thread */ diff --git a/Engine/Source/Runtime/Core/Public/Math/BigInt.h b/Engine/Source/Runtime/Core/Public/Math/BigInt.h index 058ba0a5570a..69e748c6b4f4 100644 --- a/Engine/Source/Runtime/Core/Public/Math/BigInt.h +++ b/Engine/Source/Runtime/Core/Public/Math/BigInt.h @@ -84,6 +84,11 @@ public: return Bits; } + FORCEINLINE const uint32* GetBits() const + { + return Bits; + } + /** Sets this integer to 0. */ FORCEINLINE void Zero() { @@ -126,6 +131,21 @@ public: FMemory::Memcpy(Bits, InBits, sizeof(Bits)); } + /** + * Constructor. Initializes this big int with an array of bytes. + */ + explicit TBigInt(const uint8* InData, uint32 InNumBytes) + { + // If we weren't given enough data to fill the int, zero first + // TODO: Only zero the bits we aren't going to copy over + if (InNumBytes < (ARRAY_COUNT(Bits) * sizeof(uint32))) + { + Zero(); + } + + FMemory::Memcpy(Bits, InData, InNumBytes); + } + /** * Constructor. Initializes this big int with a string representing a hex value. */ @@ -139,7 +159,7 @@ public: * * @param BitCount the number of bits to shift. */ - FORCEINLINE void ShiftLeftInternal(const int32 BitCount) + void ShiftLeftInternal(const int32 BitCount) { checkSlow(BitCount > 0); @@ -172,7 +192,7 @@ public: /** * Shift left by 1 bit. */ - FORCEINLINE void ShiftLeftByOneInternal() + void ShiftLeftByOneInternal() { const int32 HiWordShift = BitsPerWord - 1; Bits[NumWords - 1] = Bits[NumWords - 1] << 1; @@ -189,7 +209,7 @@ public: * * @param BitCount the number of bits to shift. */ - FORCEINLINE void ShiftRightInternal(const int32 BitCount) + void ShiftRightInternal(const int32 BitCount) { checkSlow(BitCount > 0); @@ -222,7 +242,7 @@ public: /** * Shift right by 1 bit. */ - FORCEINLINE void ShiftRightByOneInternal() + void ShiftRightByOneInternal() { const int32 LoWordShift = BitsPerWord - 1; Bits[0] = Bits[0] >> 1; @@ -237,7 +257,7 @@ public: /** * Adds two integers. */ - FORCEINLINE void Add(const BigInt& Other) + void Add(const BigInt& Other) { int64 CarryOver = 0; for (int32 WordIndex = 0; WordIndex < NumWords; ++WordIndex) @@ -252,7 +272,7 @@ public: /** * Subtracts two integers. */ - FORCEINLINE void Subtract(const BigInt& Other) + void Subtract(const BigInt& Other) { BigInt NegativeOther(Other); NegativeOther.Negate(); @@ -262,7 +282,7 @@ public: /** * Checks if this integer is negative. */ - FORCEINLINE bool IsNegative() const + bool IsNegative() const { if (bSigned) { @@ -277,7 +297,7 @@ public: /** * Returns the sign of this integer. */ - FORCEINLINE int32 Sign() const + int32 Sign() const { return IsNegative() ? -1 : 1; } @@ -297,7 +317,7 @@ public: /** * Returns the index of the highest word that is not zero. -1 if no such word exists. */ - FORCEINLINE int32 GetHighestNonZeroWord() const + int32 GetHighestNonZeroWord() const { int32 WordIndex; for (WordIndex = NumWords - 1; WordIndex >= 0 && Bits[WordIndex] == 0; --WordIndex); @@ -307,7 +327,7 @@ public: /** * Multiplies two positive integers. */ - FORCEINLINE void MultiplyFast(const BigInt& Factor) + void MultiplyFast(const BigInt& Factor) { const int64 Base = 2LL << BitsPerWord; TBigInt Result; // Twice as many bits for overflow protection @@ -340,7 +360,7 @@ public: /** * Multiplies two integers. */ - FORCEINLINE void Multiply(const BigInt& Factor) + void Multiply(const BigInt& Factor) { BigInt Result = *this; BigInt Other = Factor; @@ -443,7 +463,7 @@ public: /** * Performs modulo operation on this integer. */ - FORCEINLINE void Modulo(const BigInt& Modulus) + void Modulo(const BigInt& Modulus) { // a mod b = a - floor(a/b)*b check(!IsNegative()); @@ -497,7 +517,7 @@ public: /** * Assignment operator for int64 values. */ - FORCEINLINE TBigInt& operator=(int64 Other) + TBigInt& operator=(int64 Other) { Set(Other); return *this; @@ -506,7 +526,7 @@ public: /** * Returns the index of the highest non-zero bit. -1 if no such bit exists. */ - FORCEINLINE int32 GetHighestNonZeroBit() const + int32 GetHighestNonZeroBit() const { for (int32 WordIndex = NumWords - 1; WordIndex >= 0; --WordIndex) { @@ -523,7 +543,7 @@ public: /** * Returns a bit value as an integer value (0 or 1). */ - FORCEINLINE int32 GetBit(int32 BitIndex) const + int32 GetBit(int32 BitIndex) const { const int32 WordIndex = BitIndex / BitsPerWord; BitIndex -= WordIndex * BitsPerWord; @@ -533,7 +553,7 @@ public: /** * Sets a bit value. */ - FORCEINLINE void SetBit(int32 BitIndex, int32 Value) + void SetBit(int32 BitIndex, int32 Value) { const int32 WordIndex = BitIndex / BitsPerWord; BitIndex -= WordIndex * BitsPerWord; @@ -600,7 +620,7 @@ public: /** * Bitwise 'or' */ - FORCEINLINE void BitwiseOr(const BigInt& Other) + void BitwiseOr(const BigInt& Other) { for (int32 WordIndex = 0; WordIndex < NumWords; ++WordIndex) { @@ -611,7 +631,7 @@ public: /** * Bitwise 'and' */ - FORCEINLINE void BitwiseAnd(const BigInt& Other) + void BitwiseAnd(const BigInt& Other) { for (int32 WordIndex = 0; WordIndex < NumWords; ++WordIndex) { @@ -622,7 +642,7 @@ public: /** * Bitwise 'not' */ - FORCEINLINE void BitwiseNot() + void BitwiseNot() { for (int32 WordIndex = 0; WordIndex < NumWords; ++WordIndex) { @@ -633,7 +653,7 @@ public: /** * Checks if two integers are equal. */ - FORCEINLINE bool IsEqual(const BigInt& Other) const + bool IsEqual(const BigInt& Other) const { for (int32 WordIndex = 0; WordIndex < NumWords; ++WordIndex) { @@ -649,7 +669,7 @@ public: /** * this < Other */ - FORCEINLINE bool IsLess(const BigInt& Other) const + bool IsLess(const BigInt& Other) const { if (IsNegative()) { @@ -670,7 +690,7 @@ public: /** * this <= Other */ - FORCEINLINE bool IsLessOrEqual(const BigInt& Other) const + bool IsLessOrEqual(const BigInt& Other) const { if (IsNegative()) { @@ -691,7 +711,7 @@ public: /** * this > Other */ - FORCEINLINE bool IsGreater(const BigInt& Other) const + bool IsGreater(const BigInt& Other) const { if (IsNegative()) { @@ -712,7 +732,7 @@ public: /** * this >= Other */ - FORCEINLINE bool IsGreaterOrEqual(const BigInt& Other) const + bool IsGreaterOrEqual(const BigInt& Other) const { if (IsNegative()) { @@ -733,7 +753,7 @@ public: /** * this == 0 */ - FORCEINLINE bool IsZero() const + bool IsZero() const { int32 WordIndex; for (WordIndex = NumWords - 1; WordIndex >= 0 && !Bits[WordIndex]; --WordIndex); @@ -743,7 +763,7 @@ public: /** * this > 0 */ - FORCEINLINE bool IsGreaterThanZero() const + bool IsGreaterThanZero() const { return !IsNegative() && !IsZero(); } @@ -751,184 +771,184 @@ public: /** * this < 0 */ - FORCEINLINE bool IsLessThanZero() const + bool IsLessThanZero() const { return IsNegative() && !IsZero(); } - FORCEINLINE bool IsFirstBitSet() const + bool IsFirstBitSet() const { return !!(Bits[0] & 1); } /** * Bit indexing operator */ - FORCEINLINE bool operator[](int32 BitIndex) const + bool operator[](int32 BitIndex) const { return GetBit(BitIndex); } // Begin operator overloads - FORCEINLINE BigInt operator>>(int32 Count) const + BigInt operator>>(int32 Count) const { BigInt Result = *this; Result.ShiftRight(Count); return Result; } - FORCEINLINE BigInt& operator>>=(int32 Count) + BigInt& operator>>=(int32 Count) { ShiftRight(Count); return *this; } - FORCEINLINE BigInt operator<<(int32 Count) const + BigInt operator<<(int32 Count) const { BigInt Result = *this; Result.ShiftLeft(Count); return Result; } - FORCEINLINE BigInt& operator<<=(int32 Count) + BigInt& operator<<=(int32 Count) { ShiftLeft(Count); return *this; } - FORCEINLINE BigInt operator+(const BigInt& Other) const + BigInt operator+(const BigInt& Other) const { BigInt Result(*this); Result.Add(Other); return Result; } - FORCEINLINE BigInt& operator++() + BigInt& operator++() { Add(One); return *this; } - FORCEINLINE BigInt& operator+=(const BigInt& Other) + BigInt& operator+=(const BigInt& Other) { Add(Other); return *this; } - FORCEINLINE BigInt operator-(const BigInt& Other) const + BigInt operator-(const BigInt& Other) const { BigInt Result(*this); Result.Subtract(Other); return Result; } - FORCEINLINE BigInt& operator--() + BigInt& operator--() { Subtract(One); return *this; } - FORCEINLINE BigInt& operator-=(const BigInt& Other) + BigInt& operator-=(const BigInt& Other) { Subtract(Other); return *this; } - FORCEINLINE BigInt operator*(const BigInt& Other) const + BigInt operator*(const BigInt& Other) const { BigInt Result(*this); Result.Multiply(Other); return Result; } - FORCEINLINE BigInt& operator*=(const BigInt& Other) + BigInt& operator*=(const BigInt& Other) { Multiply(Other); return *this; } - FORCEINLINE BigInt operator/(const BigInt& Divider) const + BigInt operator/(const BigInt& Divider) const { BigInt Result(*this); Result.Divide(Divider); return Result; } - FORCEINLINE BigInt& operator/=(const BigInt& Divider) + BigInt& operator/=(const BigInt& Divider) { Divide(Divider); return *this; } - FORCEINLINE BigInt operator%(const BigInt& Modulus) const + BigInt operator%(const BigInt& Modulus) const { BigInt Result(*this); Result.Modulo(Modulus); return Result; } - FORCEINLINE BigInt& operator%=(const BigInt& Modulus) + BigInt& operator%=(const BigInt& Modulus) { Modulo(Modulus); return *this; } - FORCEINLINE bool operator<(const BigInt& Other) const + bool operator<(const BigInt& Other) const { return IsLess(Other); } - FORCEINLINE bool operator<=(const BigInt& Other) const + bool operator<=(const BigInt& Other) const { return IsLessOrEqual(Other); } - FORCEINLINE bool operator>(const BigInt& Other) const + bool operator>(const BigInt& Other) const { return IsGreater(Other); } - FORCEINLINE bool operator>=(const BigInt& Other) const + bool operator>=(const BigInt& Other) const { return IsGreaterOrEqual(Other); } - FORCEINLINE bool operator==(const BigInt& Other) const + bool operator==(const BigInt& Other) const { return IsEqual(Other); } - FORCEINLINE bool operator!=(const BigInt& Other) const + bool operator!=(const BigInt& Other) const { return !IsEqual(Other); } - FORCEINLINE BigInt operator&(const BigInt& Other) const + BigInt operator&(const BigInt& Other) const { BigInt Result(*this); Result.BitwiseAnd(Other); return Result; } - FORCEINLINE BigInt& operator&=(const BigInt& Other) + BigInt& operator&=(const BigInt& Other) { BitwiseAnd(Other); return *this; } - FORCEINLINE BigInt operator|(const BigInt& Other) const + BigInt operator|(const BigInt& Other) const { BigInt Result(*this); Result.BitwiseOr(Other); return Result; } - FORCEINLINE BigInt& operator|=(const BigInt& Other) + BigInt& operator|=(const BigInt& Other) { BitwiseOr(Other); return *this; } - FORCEINLINE BigInt operator~() const + BigInt operator~() const { BigInt Result(*this); Result.BitwiseNot(); @@ -1022,7 +1042,7 @@ typedef TBigInt<512> int512; typedef TBigInt<512> TEncryptionInt; /** - * Encyption key - exponent and modulus pair + * Encryption key - exponent and modulus pair */ template struct TEncryptionKey @@ -1159,7 +1179,7 @@ namespace FEncryption * Raise Base to power of Exponent in mod Modulus. */ template - FORCEINLINE static IntType ModularPow(IntType Base, IntType Exponent, IntType Modulus) + static IntType ModularPow(IntType Base, IntType Exponent, IntType Modulus) { const IntType One(1); IntType Result(1); @@ -1179,7 +1199,7 @@ namespace FEncryption * Encrypts a stream of bytes */ template - FORCEINLINE static void EncryptBytes(IntType* EncryptedData, const uint8* Data, const int64 DataLength, const FEncryptionKey& EncryptionKey) + static void EncryptBytes(IntType* EncryptedData, const uint8* Data, const int64 DataLength, const FEncryptionKey& EncryptionKey) { for (int Index = 0; Index < DataLength; ++Index) { @@ -1191,7 +1211,7 @@ namespace FEncryption * Decrypts a stream of bytes */ template - FORCEINLINE static void DecryptBytes(uint8* DecryptedData, const IntType* Data, const int64 DataLength, const FEncryptionKey& DecryptionKey) + static void DecryptBytes(uint8* DecryptedData, const IntType* Data, const int64 DataLength, const FEncryptionKey& DecryptionKey) { for (int64 Index = 0; Index < DataLength; ++Index) { @@ -1207,7 +1227,7 @@ namespace FEncryption * Specialization for int type used in encryption (performance). Avoids using temporary results and most of the operations are inplace. */ template <> -FORCEINLINE TEncryptionInt FEncryption::ModularPow(TEncryptionInt Base, TEncryptionInt Exponent, TEncryptionInt Modulus) +TEncryptionInt FEncryption::ModularPow(TEncryptionInt Base, TEncryptionInt Exponent, TEncryptionInt Modulus) { TEncryptionInt Result(1LL); while (Exponent.IsGreaterThanZero()) @@ -1283,13 +1303,14 @@ struct FDecryptedSignature : public FSignatureBase namespace FEncryption { - FORCEINLINE static void EncryptSignature(const FDecryptedSignature& InUnencryptedSignature, FEncryptedSignature& OutEncryptedSignature, const FEncryptionKey& EncryptionKey) + static void EncryptSignature(const FDecryptedSignature& InUnencryptedSignature, FEncryptedSignature& OutEncryptedSignature, const FEncryptionKey& EncryptionKey) { OutEncryptedSignature.Data = FEncryption::ModularPow(TEncryptionInt(InUnencryptedSignature.Data), EncryptionKey.Exponent, EncryptionKey.Modulus); } - FORCEINLINE static void DecryptSignature(const FEncryptedSignature& InEncryptedSignature, FDecryptedSignature& OutUnencryptedSignature, const FEncryptionKey& EncryptionKey) + static void DecryptSignature(const FEncryptedSignature& InEncryptedSignature, FDecryptedSignature& OutUnencryptedSignature, const FEncryptionKey& EncryptionKey) { OutUnencryptedSignature.Data = FEncryption::ModularPow(TEncryptionInt(InEncryptedSignature.Data), EncryptionKey.Exponent, EncryptionKey.Modulus).ToInt(); } } + diff --git a/Engine/Source/Runtime/Core/Public/Math/UnrealMathFPU.h b/Engine/Source/Runtime/Core/Public/Math/UnrealMathFPU.h index 1fdd46610af9..ffc1e8ca9657 100644 --- a/Engine/Source/Runtime/Core/Public/Math/UnrealMathFPU.h +++ b/Engine/Source/Runtime/Core/Public/Math/UnrealMathFPU.h @@ -1636,6 +1636,60 @@ FORCEINLINE VectorRegisterInt VectorIntLoad1(const void* Ptr) IntSplat); } +/** + * These functions return a vector mask to indicate which components pass the comparison. + * Each component is 0xffffffff if it passes, 0x00000000 if it fails. + * + * @param Vec1 1st source vector + * @param Vec2 2nd source vector + * @return Vector with a mask for each component. + */ +FORCEINLINE VectorRegister VectorMask_LT(const VectorRegister& Vec1, const VectorRegister& Vec2) +{ + return VectorCompareLT(Vec1, Vec2); +} + +FORCEINLINE VectorRegister VectorMask_LE(const VectorRegister& Vec1, const VectorRegister& Vec2) +{ + return VectorCompareLE(Vec1, Vec2); +} + +FORCEINLINE VectorRegister VectorMask_GT(const VectorRegister& Vec1, const VectorRegister& Vec2) +{ + return VectorCompareGT(Vec1, Vec2); +} + +FORCEINLINE VectorRegister VectorMask_GE(const VectorRegister& Vec1, const VectorRegister& Vec2) +{ + return VectorCompareGE(Vec1, Vec2); +} + +FORCEINLINE VectorRegister VectorMask_EQ(const VectorRegister& Vec1, const VectorRegister& Vec2) +{ + return VectorCompareEQ(Vec1, Vec2); +} + +FORCEINLINE VectorRegister VectorMask_NE(const VectorRegister& Vec1, const VectorRegister& Vec2) +{ + return VectorCompareNE(Vec1, Vec2); +} + +/** + * Returns an integer bit-mask (0x00 - 0x0f) based on the sign-bit for each component in a vector. + * + * @param VecMask Vector + * @return Bit 0 = sign(VecMask.x), Bit 1 = sign(VecMask.y), Bit 2 = sign(VecMask.z), Bit 3 = sign(VecMask.w) + */ +FORCEINLINE int32_t VectorMaskBits(const VectorRegister& Vec1) +{ + uint32* V1 = (uint32*)(&(Vec1.V[0])); + + return (V1[0] >> 31) | + ((V1[1] >> 30) & 2) | + ((V1[2] >> 29) & 4) | + ((V1[3] >> 28) & 8); +} + // To be continued... diff --git a/Engine/Source/Runtime/Core/Public/MemPro/MemProProfiler.h b/Engine/Source/Runtime/Core/Public/MemPro/MemProProfiler.h new file mode 100644 index 000000000000..ea80bf8f72bf --- /dev/null +++ b/Engine/Source/Runtime/Core/Public/MemPro/MemProProfiler.h @@ -0,0 +1,46 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +#pragma once + + +/* + * To enable MemPro support in the engine, add GlobalDefinitions.Add("MEMPRO_ENABLED=1"); to your game's .Target.cs build rules file for supported platforms. + * (note: MemPro.cpp/.h need to be added to the project for this to work) + */ +#if !defined(MEMPRO_ENABLED) + #define MEMPRO_ENABLED 0 +#endif + + + +#if MEMPRO_ENABLED +#include "CoreGlobals.h" +#include "HAL/LowLevelMemTracker.h" +#include "MemPro/MemPro.h" + +class CORE_API FMemProProfiler +{ +public: + static void PostInit(); + + static bool IsUsingPort( uint32 Port ); + + static inline bool IsStarted() + { + extern int32 GMemProEnabled; + return (GMemProEnabled != 0) && !GIsRequestingExit; + } + +#if ENABLE_LOW_LEVEL_MEM_TRACKER + static inline bool IsTrackingTag( ELLMTag Tag ) + { + extern ELLMTag GMemProTrackTag; + return IsStarted() && (GMemProTrackTag != ELLMTag::Paused) && ((Tag == GMemProTrackTag) || (GMemProTrackTag == ELLMTag::GenericTagCount)); + } + + static void TrackTag( ELLMTag Tag ); + static void TrackTagByName( const TCHAR* TagName ); +#endif //ENABLE_LOW_LEVEL_MEM_TRACKER +}; + +#endif //MEMPRO_ENABLED diff --git a/Engine/Source/Runtime/Core/Public/Misc/Build.h b/Engine/Source/Runtime/Core/Public/Misc/Build.h index f941ee15baa7..c6a16da19e57 100644 --- a/Engine/Source/Runtime/Core/Public/Misc/Build.h +++ b/Engine/Source/Runtime/Core/Public/Misc/Build.h @@ -311,7 +311,7 @@ #ifndef ALLOW_HANG_DETECTION #define ALLOW_HANG_DETECTION 1 #endif -#define USE_HANG_DETECTION (ALLOW_HANG_DETECTION && !WITH_EDITORONLY_DATA && !IS_PROGRAM && !UE_BUILD_DEBUG) +#define USE_HANG_DETECTION (ALLOW_HANG_DETECTION && !WITH_EDITORONLY_DATA && !IS_PROGRAM && !UE_BUILD_DEBUG && !ENABLE_PGO_PROFILE) // Controls the creation of a thread for detecting hitches (FGameThreadHitchHeartBeat). This is subject to other criteria, USE_HITCH_DETECTION #ifndef ALLOW_HITCH_DETECTION diff --git a/Engine/Source/Runtime/Core/Public/Misc/ConfigCacheIni.h b/Engine/Source/Runtime/Core/Public/Misc/ConfigCacheIni.h index b60abbe98d50..e4ec3e92d9a7 100644 --- a/Engine/Source/Runtime/Core/Public/Misc/ConfigCacheIni.h +++ b/Engine/Source/Runtime/Core/Public/Misc/ConfigCacheIni.h @@ -821,6 +821,17 @@ public: */ static void InitializeConfigSystem(); + /** + * Calculates the name of a dest (generated) .ini file for a given base (ie Engine, Game, etc) + * + * @param IniBaseName Base name of the .ini (Engine, Game) + * @param PlatformName Name of the platform to get the .ini path for (nullptr means to use the current platform) + * @param GeneratedConfigDir The base folder that will contain the generated config files. + * + * @return Standardized .ini filename + */ + static FString GetDestIniFilename(const TCHAR* BaseIniName, const TCHAR* PlatformName, const TCHAR* GeneratedConfigDir); + /** * Loads and generates a destination ini file and adds it to GConfig: * - Looking on commandline for override source/dest .ini filenames @@ -844,7 +855,7 @@ public: /** * Load an ini file directly into an FConfigFile, and nothing is written to GConfig or disk. * The passed in .ini name can be a "base" (Engine, Game) which will be modified by platform and/or commandline override, - * or it can be a full ini filenname (ie WrangleContent) loaded from the Source config directory + * or it can be a full ini filename (ie WrangleContent) loaded from the Source config directory * * @param ConfigFile The output object to fill * @param IniName Either a Base ini name (Engine) or a full ini name (WrangleContent). NO PATH OR EXTENSION SHOULD BE USED! @@ -858,7 +869,7 @@ public: /** * Load an ini file directly into an FConfigFile from the specified config folders, optionally writing to disk. * The passed in .ini name can be a "base" (Engine, Game) which will be modified by platform and/or commandline override, - * or it can be a full ini filenname (ie WrangleContent) loaded from the Source config directory + * or it can be a full ini filename (ie WrangleContent) loaded from the Source config directory * * @param ConfigFile The output object to fill * @param IniName Either a Base ini name (Engine) or a full ini name (WrangleContent). NO PATH OR EXTENSION SHOULD BE USED! diff --git a/Engine/Source/Runtime/Core/Public/Misc/CoreDelegates.h b/Engine/Source/Runtime/Core/Public/Misc/CoreDelegates.h index 3fcd35557d42..96e165a00075 100644 --- a/Engine/Source/Runtime/Core/Public/Misc/CoreDelegates.h +++ b/Engine/Source/Runtime/Core/Public/Misc/CoreDelegates.h @@ -101,7 +101,7 @@ public: DECLARE_DELEGATE_OneParam(FPakEncryptionKeyDelegate, uint8[32]); // Callback for gathering pak signing keys, if they exist - DECLARE_DELEGATE_TwoParams(FPakSigningKeysDelegate, uint8[64], uint8[64]); + DECLARE_DELEGATE_TwoParams(FPakSigningKeysDelegate, TArray&, TArray&); // Callback for handling the Controller connection / disconnection // first param is true for a connection, false for a disconnection. diff --git a/Engine/Source/Runtime/Core/Public/Misc/SecureHash.h b/Engine/Source/Runtime/Core/Public/Misc/SecureHash.h index 6456271738bb..92df6aedc9c5 100644 --- a/Engine/Source/Runtime/Core/Public/Misc/SecureHash.h +++ b/Engine/Source/Runtime/Core/Public/Misc/SecureHash.h @@ -223,6 +223,9 @@ public: friend CORE_API FArchive& operator<<( FArchive& Ar, FSHAHash& G ); friend CORE_API uint32 GetTypeHash(FSHAHash const& InKey); + + friend CORE_API FString LexToString(const FSHAHash&); + friend CORE_API void LexFromString(FSHAHash& Hash, const TCHAR*); }; class CORE_API FSHA1 diff --git a/Engine/Source/Runtime/Core/Public/Modules/ModuleManager.h b/Engine/Source/Runtime/Core/Public/Modules/ModuleManager.h index b67d753922bd..4d3a70cb0d5a 100644 --- a/Engine/Source/Runtime/Core/Public/Modules/ModuleManager.h +++ b/Engine/Source/Runtime/Core/Public/Modules/ModuleManager.h @@ -415,6 +415,9 @@ public: /** Sets the filename for a module. The module is not reloaded immediately, but the new name will be used for subsequent unload/load events. */ void SetModuleFilename(FName ModuleName, const FString& Filename); + + /** Determines if any non-default module instances are loaded (eg. hot reloaded modules) */ + bool HasAnyOverridenModuleFilename() const; #endif public: @@ -765,16 +768,21 @@ class FDefaultGameModuleImpl { \ FSigningKeyRegistration() \ { \ - extern void RegisterSigningKeyCallback(void (*)(unsigned char OutExponent[64], unsigned char OutModulus[64])); \ + extern void RegisterSigningKeyCallback(void (*)(TArray&, TArray&)); \ RegisterSigningKeyCallback(&Callback); \ } \ - static void Callback(unsigned char OutExponent[64], unsigned char OutModulus[64]) \ + static void Callback(TArray& OutExponent, TArray& OutModulus) \ { \ - const unsigned char Exponent[64] = { ExponentValue }; \ - const unsigned char Modulus[64] = { ModulusValue }; \ - for(int ByteIdx = 0; ByteIdx < 64; ByteIdx++) \ + const uint8 Exponent[] = { ExponentValue }; \ + const uint8 Modulus[] = { ModulusValue }; \ + OutExponent.SetNum(ARRAY_COUNT(Exponent)); \ + OutModulus.SetNum(ARRAY_COUNT(Modulus)); \ + for(int ByteIdx = 0; ByteIdx < ARRAY_COUNT(Exponent); ByteIdx++) \ { \ OutExponent[ByteIdx] = Exponent[ByteIdx]; \ + } \ + for(int ByteIdx = 0; ByteIdx < ARRAY_COUNT(Modulus); ByteIdx++) \ + { \ OutModulus[ByteIdx] = Modulus[ByteIdx]; \ } \ } \ diff --git a/Engine/Source/Runtime/Core/Public/ProfilingDebugging/ScopedTimers.h b/Engine/Source/Runtime/Core/Public/ProfilingDebugging/ScopedTimers.h index c8b4d74b1be7..14e7e25f11b3 100644 --- a/Engine/Source/Runtime/Core/Public/ProfilingDebugging/ScopedTimers.h +++ b/Engine/Source/Runtime/Core/Public/ProfilingDebugging/ScopedTimers.h @@ -72,6 +72,7 @@ class FAutoScopedDurationTimer : public FScopedDurationTimer public: FAutoScopedDurationTimer() : FScopedDurationTimer(AccumulatorValue) + , AccumulatorValue(0) { } diff --git a/Engine/Source/Runtime/Core/Public/Windows/WindowsPlatformCompilerSetup.h b/Engine/Source/Runtime/Core/Public/Windows/WindowsPlatformCompilerSetup.h index 19f84cdec663..93e19c96bbfe 100644 --- a/Engine/Source/Runtime/Core/Public/Windows/WindowsPlatformCompilerSetup.h +++ b/Engine/Source/Runtime/Core/Public/Windows/WindowsPlatformCompilerSetup.h @@ -166,10 +166,7 @@ static_assert(_MSC_VER != 1914 && _MSC_VER != 1915, "Visual Studio 2017 versions #pragma warning(disable: 4511) // 'class' : copy constructor could not be generated https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-3-c4511 #pragma warning(disable: 4512) // 'class' : assignment operator could not be generated https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4512 #pragma warning(disable: 4514) // 'function' : unreferenced inline function has been removed https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4514 -#if UE_BUILD_DEBUG -// xstring.h causes this warning in debug builds -#pragma warning(disable: 4548) // expression before comma has no effect; expected expression with side-effect https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-1-c4548 -#endif + #pragma warning(disable: 4592) // 'function': 'constexpr' call evaluation failed; function will be called at run-time https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warnings-c4400-through-c4599 #pragma warning(disable: 4599) // 'flag path': command line argument number number does not match precompiled header https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warnings-c4400-through-c4599 diff --git a/Engine/Source/Runtime/CoreUObject/Private/Serialization/ArchiveStackTrace.cpp b/Engine/Source/Runtime/CoreUObject/Private/Serialization/ArchiveStackTrace.cpp index 786a72e9af02..294f73a3cfe6 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/Serialization/ArchiveStackTrace.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/Serialization/ArchiveStackTrace.cpp @@ -1304,14 +1304,14 @@ void FArchiveStackTrace::DumpPackageHeaderDiffs(const FPackageData& SourcePackag FLinkerLoad* DestLinker = nullptr; // Create linkers. Note there's no need to clean them up here since they will be removed by the package associated with them { - TRefCountPtr LinkerLoadContext(new FUObjectSerializeContext()); + TRefCountPtr LinkerLoadContext(FUObjectThreadContext::Get().GetSerializeContext()); BeginLoad(LinkerLoadContext); SourceLinker = CreateLinkerForPackage(LinkerLoadContext, SourceAssetPackageName, AssetFilename, SourcePackage); EndLoad(SourceLinker ? SourceLinker->GetSerializeContext() : LinkerLoadContext.GetReference()); } { - TRefCountPtr LinkerLoadContext(new FUObjectSerializeContext()); + TRefCountPtr LinkerLoadContext(FUObjectThreadContext::Get().GetSerializeContext()); BeginLoad(LinkerLoadContext); DestLinker = CreateLinkerForPackage(LinkerLoadContext, DestAssetPackageName, AssetFilename, DestPackage); EndLoad(DestLinker ? DestLinker->GetSerializeContext() : LinkerLoadContext.GetReference()); @@ -1387,4 +1387,4 @@ FArchiveStackTraceReader* FArchiveStackTraceReader::CreateFromFile(const TCHAR* Reader = new FArchiveStackTraceReader(InFilename, PackageData.Data, PackageData.Size); } return Reader; -} +} \ No newline at end of file diff --git a/Engine/Source/Runtime/CoreUObject/Private/Serialization/AsyncLoading.cpp b/Engine/Source/Runtime/CoreUObject/Private/Serialization/AsyncLoading.cpp index cd1c64aed7fd..458ac166f05b 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/Serialization/AsyncLoading.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/Serialization/AsyncLoading.cpp @@ -485,6 +485,11 @@ void FAsyncPackage::PopulateFlushTree(struct FFlushTree* FlushTree) } } +FUObjectSerializeContext* FAsyncPackage::GetSerializeContext() +{ + return FUObjectThreadContext::Get().GetSerializeContext(); +} + FAsyncPackage* FAsyncLoadingThread::FindExistingPackageAndAddCompletionCallback(FAsyncPackageDesc* PackageRequest, TMap& PackageList, FFlushTree* FlushTree) { checkSlow(IsInAsyncLoadThread()); @@ -3149,6 +3154,7 @@ UObject* FAsyncPackage::EventDrivenIndexToObject(FPackageIndex Index, bool bChec MyDependentNode.Phase = EEventLoadNode::ImportOrExport_Create; if (EventNodeArray.GetNode(MyDependentNode, false).bAddedToGraph || !EventNodeArray.GetNode(MyDependentNode, false).bFired) { + FUObjectSerializeContext* LoadContext = GetSerializeContext(); UClass* SerClass = Cast(LoadContext->SerializedObject); if (!SerClass || Linker->ImpExp(Index).ObjectName != SerClass->GetDefaultObjectName()) { @@ -3166,6 +3172,7 @@ UObject* FAsyncPackage::EventDrivenIndexToObject(FPackageIndex Index, bool bChec if (DumpIndex.IsNull()) { + FUObjectSerializeContext* LoadContext = GetSerializeContext(); DumpDependencies(TEXT("Dependencies"), LoadContext->SerializedObject); } else @@ -3210,6 +3217,7 @@ void FAsyncPackage::EventDrivenCreateExport(int32 LocalExportIndex) check(!Export.Object); // we should not have this yet if (!Export.Object && !Export.bExportLoadFailed) { + FUObjectSerializeContext* LoadContext = GetSerializeContext(); FScopedAddObjectreference OnExitAddReference(*this, Export.Object); if (!Linker->FilterExport(Export)) // for some acceptable position, it was not "not for" @@ -3581,6 +3589,7 @@ void FAsyncPackage::EventDrivenSerializeExport(int32 LocalExportIndex) Object->ClearFlags(RF_NeedLoad); + FUObjectSerializeContext* LoadContext = GetSerializeContext(); UObject* PrevSerializedObject = LoadContext->SerializedObject; LoadContext->SerializedObject = Object; Linker->bForceSimpleIndexToObject = true; @@ -4122,6 +4131,7 @@ void FAsyncPackage::Event_StartPostload() AsyncPackageLoadingState = EAsyncPackageLoadingState::PostLoad_Etc; EventDrivenLoadingComplete(); { + FUObjectSerializeContext* LoadContext = GetSerializeContext(); LoadContext->ReserveObjectsLoaded(LoadContext->GetNumObjectsLoaded() + Linker->ExportMap.Num()); for (int32 LocalExportIndex = 0; LocalExportIndex < Linker->ExportMap.Num(); LocalExportIndex++) { @@ -5485,7 +5495,6 @@ FAsyncPackage::FAsyncPackage(const FAsyncPackageDesc& InDesc) , FinishObjectsTime(0.0) #endif // PERF_TRACK_DETAILED_ASYNC_STATS { - LoadContext = new FUObjectSerializeContext(); AddRequestID(InDesc.RequestID); } @@ -5675,6 +5684,7 @@ void FAsyncPackage::BeginAsyncLoad() } // this won't do much during async loading except increase the load count which causes IsLoading to return true + FUObjectSerializeContext* LoadContext = GetSerializeContext(); BeginLoad(LoadContext); } @@ -5687,6 +5697,7 @@ void FAsyncPackage::EndAsyncLoad() check(IsAsyncLoading()); // this won't do much during async loading except decrease the load count which causes IsLoading to return false + FUObjectSerializeContext* LoadContext = GetSerializeContext(); EndLoad(LoadContext); if (IsInGameThread()) @@ -5958,19 +5969,6 @@ EAsyncPackageState::Type FAsyncPackage::CreateLinker() Linker = FLinkerLoad::FindExistingLinkerForPackage(Package); if (Linker) { - // Swap the load context to the currently associated with the existing linker - if (Linker->GetSerializeContext()) - { - LoadContext->DecrementBeginLoadCount(); - LoadContext = Linker->GetSerializeContext(); - LoadContext->IncrementBeginLoadCount(); - } - else - { - check(!GEventDrivenLoaderEnabled); - Linker->SetSerializeContext(LoadContext); - } - if (GEventDrivenLoaderEnabled) { // this almost works, but the EDL does not tolerate redoing steps it already did @@ -6064,6 +6062,7 @@ EAsyncPackageState::Type FAsyncPackage::CreateLinker() LinkerFlags |= LOAD_PackageForPIE; } #endif + FUObjectSerializeContext* LoadContext = GetSerializeContext(); if (GEventDrivenLoaderEnabled) { FWeakAsyncPackagePtr WeakPtr(this); @@ -6530,6 +6529,7 @@ EAsyncPackageState::Type FAsyncPackage::PreLoadObjects() // GC can't run in here FGCScopeGuard GCGuard; + FUObjectSerializeContext* LoadContext = GetSerializeContext(); TArray& ThreadObjLoaded = LoadContext->PRIVATE_GetObjectsLoadedInternalUseOnly(); PackageObjLoaded.Append(ThreadObjLoaded); ThreadObjLoaded.Reset(); @@ -6629,6 +6629,7 @@ EAsyncPackageState::Type FAsyncPackage::PostLoadObjects() FUObjectThreadContext& ThreadContext = FUObjectThreadContext::Get(); TGuardValue GuardIsRoutingPostLoad(ThreadContext.IsRoutingPostLoad, true); + FUObjectSerializeContext* LoadContext = GetSerializeContext(); TArray& ThreadObjLoaded = LoadContext->PRIVATE_GetObjectsLoadedInternalUseOnly(); if (ThreadObjLoaded.Num()) { @@ -6709,6 +6710,7 @@ EAsyncPackageState::Type FAsyncPackage::PostLoadDeferredObjects(double InTickSta TGuardValue GuardIsRoutingPostLoad(PackageScope.ThreadContext.IsRoutingPostLoad, true); FAsyncLoadingTickScope InAsyncLoadingTick; + FUObjectSerializeContext* LoadContext = GetSerializeContext(); TArray& ObjLoadedInPostLoad = LoadContext->PRIVATE_GetObjectsLoadedInternalUseOnly(); TArray ObjLoadedInPostLoadLocal; @@ -6898,6 +6900,7 @@ EAsyncPackageState::Type FAsyncPackage::FinishObjects() LastObjectWorkWasPerformedOn = nullptr; LastTypeOfWorkPerformed = TEXT("finishing all objects"); + FUObjectSerializeContext* LoadContext = GetSerializeContext(); check(!Linker || LoadContext == Linker->GetSerializeContext()); TArray& ThreadObjLoaded = LoadContext->PRIVATE_GetObjectsLoadedInternalUseOnly(); diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/Class.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/Class.cpp index ccc37309ce02..f4fc8c423630 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/Class.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/Class.cpp @@ -4381,7 +4381,7 @@ void UClass::AssembleReferenceTokenStreams() Class->GetDefaultObject(); // Force the default object to be constructed if it isn't already } // Assemble reference token stream for garbage collection/ RTGC. - if (!Class->HasAnyClassFlags(CLASS_TokenStreamAssembled)) + if (!Class->HasAnyFlags(RF_ClassDefaultObject) && !Class->HasAnyClassFlags(CLASS_TokenStreamAssembled)) { Class->AssembleReferenceTokenStream(); } diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/Linker.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/Linker.cpp index 4f0d1012878d..b48ae797d84a 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/Linker.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/Linker.cpp @@ -715,12 +715,9 @@ FLinkerLoad* GetPackageLinker // Create new linker. if( !Result ) { - // @todo: RM so never create the context below? - //check(InExistingContext && InExistingContext->HasStartedLoading()); - // we will already have found the filename above check(NewFilename.Len() > 0); - TRefCountPtr LoadContext(InExistingContext ? InExistingContext : new FUObjectSerializeContext()); + TRefCountPtr LoadContext(FUObjectThreadContext::Get().GetSerializeContext()); Result = FLinkerLoad::CreateLinker(LoadContext, InOuter, *NewFilename, LoadFlags, InReaderOverride); } else if (InExistingContext) @@ -772,7 +769,7 @@ FLinkerLoad* GetPackageLinker FLinkerLoad* LoadPackageLinker(UPackage* InOuter, const TCHAR* InLongPackageName, uint32 LoadFlags, UPackageMap* Sandbox, FGuid* CompatibleGuid, FArchive* InReaderOverride, TFunctionRef LinkerLoadedCallback) { FLinkerLoad* Linker = nullptr; - TRefCountPtr LoadContext(new FUObjectSerializeContext()); + TRefCountPtr LoadContext(FUObjectThreadContext::Get().GetSerializeContext()); BeginLoad(LoadContext); { FUObjectSerializeContext* InOutLoadContext = LoadContext; diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerLoad.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerLoad.cpp index fe524adc1cba..8545e9bd6d05 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerLoad.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/LinkerLoad.cpp @@ -642,19 +642,11 @@ FLinkerLoad* FLinkerLoad::CreateLinkerAsync(FUObjectSerializeContext* LoadContex void FLinkerLoad::SetSerializeContext(FUObjectSerializeContext* InLoadContext) { - if (!GEventDrivenLoaderEnabled && CurrentLoadContext) - { - CurrentLoadContext->DetachLinker(this); - } - CurrentLoadContext = InLoadContext; - if (!GEventDrivenLoaderEnabled && CurrentLoadContext) - { - CurrentLoadContext->AttachLinker(this); - } } + FUObjectSerializeContext* FLinkerLoad::GetSerializeContext() { - return CurrentLoadContext; + return FUObjectThreadContext::Get().GetSerializeContext(); } /** @@ -2429,6 +2421,8 @@ FLinkerLoad::EVerifyResult FLinkerLoad::VerifyImport(int32 ImportIndex) // these checks find out if the VerifyImportInner was successful or not if (Import.SourceLinker && Import.SourceIndex == INDEX_NONE && Import.XObject == NULL && !Import.OuterIndex.IsNull() && Import.ObjectName != NAME_ObjectRedirector) { + FUObjectSerializeContext* CurrentLoadContext = GetSerializeContext(); + // if we found the package, but not the object, look for a redirector FObjectImport OriginalImport = Import; Import.ClassName = NAME_ObjectRedirector; @@ -2949,6 +2943,7 @@ bool FLinkerLoad::VerifyImportInner(const int32 ImportIndex, FString& WarningSuf { // except if we are looking for _the_ package...in which case we are looking for TmpPkg, so we are done Import.XObject = TmpPkg; + FUObjectSerializeContext* CurrentLoadContext = GetSerializeContext(); check(CurrentLoadContext); CurrentLoadContext->IncrementImportCount(); FLinkerManager::Get().AddLoaderWithNewImports(this); @@ -3003,6 +2998,7 @@ bool FLinkerLoad::VerifyImportInner(const int32 ImportIndex, FString& WarningSuf if (FindObject != NULL && ((LoadFlags & LOAD_FindIfFail) || bIsInMemoryOnlyOrNativeTransient)) { Import.XObject = FindObject; + FUObjectSerializeContext* CurrentLoadContext = GetSerializeContext(); check(CurrentLoadContext); CurrentLoadContext->IncrementImportCount(); FLinkerManager::Get().AddLoaderWithNewImports(this); @@ -3424,6 +3420,8 @@ void FLinkerLoad::Preload( UObject* Object ) // Preload the object if necessary. if (Object->HasAnyFlags(RF_NeedLoad)) { + FUObjectSerializeContext* CurrentLoadContext = GetSerializeContext(); + if (Object->GetLinker() == this) { check(!GEventDrivenLoaderEnabled || !bLockoutLegacyOperations || !EVENT_DRIVEN_ASYNC_LOAD_ACTIVE_AT_RUNTIME); @@ -3556,7 +3554,7 @@ void FLinkerLoad::Preload( UObject* Object ) return; } #endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING - + check(CurrentLoadContext); // Maintain the current SerializedObjects. UObject* PrevSerializedObject = CurrentLoadContext->SerializedObject; @@ -3853,6 +3851,7 @@ UObject* FLinkerLoad::CreateExport( int32 Index ) // Check whether we already loaded the object and if not whether the context flags allow loading it. if( !Export.Object && !FilterExport(Export) ) // for some acceptable position, it was not "not for" { + FUObjectSerializeContext* CurrentLoadContext = GetSerializeContext(); check(!GEventDrivenLoaderEnabled || !bLockoutLegacyOperations || !EVENT_DRIVEN_ASYNC_LOAD_ACTIVE_AT_RUNTIME); check(Export.ObjectName!=NAME_None || !(Export.ObjectFlags&RF_Public)); check(IsLoading()); @@ -4453,6 +4452,7 @@ UObject* FLinkerLoad::CreateImport( int32 Index ) // Imports can have no name if they were filtered out due to package redirects, skip in that case if (Import.XObject == nullptr && Import.ObjectName != NAME_None) { + FUObjectSerializeContext* CurrentLoadContext = GetSerializeContext(); if (!GIsEditor && !IsRunningCommandlet()) { // Try to find existing version in memory first. @@ -4735,8 +4735,10 @@ void FLinkerLoad::Detach() // Remove from object manager, if it has been added. FLinkerManager::Get().RemoveLoaderFromObjectLoadersAndLoadersWithNewImports(this); - if (!FPlatformProperties::HasEditorOnlyData() && CurrentLoadContext) + if (!FPlatformProperties::HasEditorOnlyData()) { + FUObjectSerializeContext* CurrentLoadContext = GetSerializeContext(); + check(CurrentLoadContext); CurrentLoadContext->RemoveDelayedLinkerClosePackage(this); } @@ -4834,7 +4836,6 @@ FArchive& FLinkerLoad::operator<<( UObject*& Object ) return *this; } - check(CurrentLoadContext != nullptr); UObject* Temporary = NULL; Temporary = IndexToObject( Index ); @@ -4946,6 +4947,7 @@ UObject* FLinkerLoad::GetArchetypeFromLoader(const UObject* Obj) { if (GEventDrivenLoaderEnabled) { + FUObjectSerializeContext* CurrentLoadContext = GetSerializeContext(); check(CurrentLoadContext); check(!TemplateForGetArchetypeFromLoader || CurrentLoadContext->SerializedObject == Obj); return TemplateForGetArchetypeFromLoader; diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/Obj.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/Obj.cpp index c390ea93114b..1b3ef8b0b3c8 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/Obj.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/Obj.cpp @@ -753,9 +753,30 @@ void UObject::BeginDestroy() } LowLevelRename(NAME_None); + +#if WITH_EDITORONLY_DATA + // Make sure the linker entry stays as 'bExportLoadFailed' if the entry was marked as such, + // doing this prevents the object from being reloaded by subsequent load calls: + FLinkerLoad* Linker = GetLinker(); + const int32 CachedLinkerIndex = GetLinkerIndex(); + bool bLinkerEntryWasInvalid = false; + if(Linker && Linker->ExportMap.IsValidIndex(CachedLinkerIndex)) + { + FObjectExport& ObjExport = Linker->ExportMap[CachedLinkerIndex]; + bLinkerEntryWasInvalid = ObjExport.bExportLoadFailed; + } +#endif // WITH_EDITORONLY_DATA // Remove from linker's export table. SetLinker( NULL, INDEX_NONE ); + +#if WITH_EDITORONLY_DATA + if(bLinkerEntryWasInvalid) + { + FObjectExport& ObjExport = Linker->ExportMap[CachedLinkerIndex]; + ObjExport.bExportLoadFailed = true; + } +#endif // WITH_EDITORONLY_DATA // ensure BeginDestroy has been routed back to UObject::BeginDestroy. #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/PropertyMulticastDelegate.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/PropertyMulticastDelegate.cpp index 1360a7c50015..89317518f102 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/PropertyMulticastDelegate.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/PropertyMulticastDelegate.cpp @@ -194,7 +194,7 @@ void UMulticastDelegateProperty::ExportTextItem( FString& ValueStr, const void* ValueStr += TEXT( ")" ); } -const TCHAR* UMulticastDelegateProperty::ImportDelegateFromText( FMulticastScriptDelegate& MulticastDelegate, const TCHAR* Buffer, void* PropertyValue, int32 PortFlags, UObject* Parent, FOutputDevice* ErrorText ) const +const TCHAR* UMulticastDelegateProperty::ImportDelegateFromText( FMulticastScriptDelegate& MulticastDelegate, const TCHAR* Buffer, UObject* Parent, FOutputDevice* ErrorText ) const { // Multi-cast delegates always expect an opening parenthesis when using assignment syntax, so that // users don't accidentally blow away already-bound delegates in DefaultProperties. This also helps @@ -371,7 +371,7 @@ void UMulticastInlineDelegateProperty::SerializeItem(FStructuredArchive::FSlot S const TCHAR* UMulticastInlineDelegateProperty::ImportText_Internal(const TCHAR* Buffer, void* PropertyValue, int32 PortFlags, UObject* Parent, FOutputDevice* ErrorText) const { FMulticastScriptDelegate& MulticastDelegate = (*(FMulticastScriptDelegate*)PropertyValue); - return ImportDelegateFromText(MulticastDelegate, Buffer, PropertyValue, PortFlags, Parent, ErrorText); + return ImportDelegateFromText(MulticastDelegate, Buffer, Parent, ErrorText); } void ResolveDelegateReference(const UMulticastInlineDelegateProperty* InlineProperty, UObject*& Parent, void*& PropertyValue) @@ -436,14 +436,16 @@ void UMulticastSparseDelegateProperty::SetMulticastDelegate(void* PropertyValue, USparseDelegateFunction* SparseDelegateFunc = CastChecked(SignatureFunction); UObject* OwningObject = FSparseDelegateStorage::ResolveSparseOwner(SparseDelegate, SparseDelegateFunc->OwningClassName, SparseDelegateFunc->DelegateName); - FMulticastScriptDelegate& Delegate = FSparseDelegateStorage::GetOrCreateMulticastDelegate(OwningObject, SparseDelegateFunc->DelegateName); - Delegate = MoveTemp(ScriptDelegate); - - SparseDelegate.bIsBound = Delegate.IsBound(); - if (!SparseDelegate.bIsBound) + if (ScriptDelegate.IsBound()) + { + FSparseDelegateStorage::SetMulticastDelegate(OwningObject, SparseDelegateFunc->DelegateName, MoveTemp(ScriptDelegate)); + SparseDelegate.bIsBound = true; + } + else if (SparseDelegate.bIsBound) { FSparseDelegateStorage::Clear(OwningObject, SparseDelegateFunc->DelegateName); + SparseDelegate.bIsBound = false; } } @@ -480,8 +482,7 @@ void UMulticastSparseDelegateProperty::SerializeItem(FStructuredArchive::FSlot S { USparseDelegateFunction* SparseDelegateFunc = CastChecked(SignatureFunction); UObject* OwningObject = FSparseDelegateStorage::ResolveSparseOwner(SparseDelegate, SparseDelegateFunc->OwningClassName, SparseDelegateFunc->DelegateName); - FMulticastScriptDelegate& BoundDelegate = FSparseDelegateStorage::GetOrCreateMulticastDelegate(OwningObject, SparseDelegateFunc->DelegateName); - BoundDelegate = MoveTemp(Delegate); + FSparseDelegateStorage::SetMulticastDelegate(OwningObject, SparseDelegateFunc->DelegateName, MoveTemp(Delegate)); SparseDelegate.bIsBound = true; } else if (SparseDelegate.bIsBound) @@ -516,19 +517,23 @@ void UMulticastSparseDelegateProperty::SerializeItem(FStructuredArchive::FSlot S const TCHAR* UMulticastSparseDelegateProperty::ImportText_Internal(const TCHAR* Buffer, void* PropertyValue, int32 PortFlags, UObject* Parent, FOutputDevice* ErrorText) const { - FSparseDelegate& SparseDelegate = *(FSparseDelegate*)PropertyValue; - - USparseDelegateFunction* SparseDelegateFunc = CastChecked(SignatureFunction); - FMulticastScriptDelegate& Delegate = FSparseDelegateStorage::GetOrCreateMulticastDelegate(Parent, SparseDelegateFunc->DelegateName); - - const TCHAR* Result = ImportDelegateFromText(Delegate, Buffer, PropertyValue, PortFlags, Parent, ErrorText); + FMulticastScriptDelegate Delegate; + const TCHAR* Result = ImportDelegateFromText(Delegate, Buffer, Parent, ErrorText); if (Result) { - SparseDelegate.bIsBound = Delegate.IsBound(); - if (!SparseDelegate.bIsBound) + FSparseDelegate& SparseDelegate = *(FSparseDelegate*)PropertyValue; + USparseDelegateFunction* SparseDelegateFunc = CastChecked(SignatureFunction); + + if (Delegate.IsBound()) + { + FSparseDelegateStorage::SetMulticastDelegate(Parent, SparseDelegateFunc->DelegateName, MoveTemp(Delegate)); + SparseDelegate.bIsBound = true; + } + else { FSparseDelegateStorage::Clear(Parent, SparseDelegateFunc->DelegateName); + SparseDelegate.bIsBound = false; } } diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/SavePackage.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/SavePackage.cpp index 438e0788520c..048714336698 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/SavePackage.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/SavePackage.cpp @@ -3553,7 +3553,7 @@ FSavePackageResultStruct UPackage::Save(UPackage* InOuter, UObject* Base, EObjec if (FPlatformProperties::HasEditorOnlyData()) { - TRefCountPtr SaveContext(new FUObjectSerializeContext()); + TRefCountPtr SaveContext(FUObjectThreadContext::Get().GetSerializeContext()); #if WITH_EDITOR struct FDiffSettings diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/SparseDelegate.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/SparseDelegate.cpp index cb98745ba371..d1e0acc9f495 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/SparseDelegate.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/SparseDelegate.cpp @@ -5,9 +5,40 @@ #include "UObject/Package.h" #include "HAL/IConsoleManager.h" -FUObjectAnnotationSparse FSparseDelegateStorage::SparseDelegates; +FSparseDelegateStorage::FObjectListener FSparseDelegateStorage::SparseDelegateObjectListener; +FCriticalSection FSparseDelegateStorage::SparseDelegateMapCritical; +TMap FSparseDelegateStorage::SparseDelegates; TMap, size_t> FSparseDelegateStorage::SparseDelegateObjectOffsets; +FSparseDelegateStorage::FObjectListener::~FObjectListener() +{ + // Destroy order might result in GUObjectArray or its critical section being invalid so don't disable since we're shutting down anyways + if (!GIsRequestingExit) + { + DisableListener(); + } +} + +void FSparseDelegateStorage::FObjectListener::NotifyUObjectDeleted(const UObjectBase* Object, int32 Index) +{ + FScopeLock SparseDelegateMapLock(&FSparseDelegateStorage::SparseDelegateMapCritical); + FSparseDelegateStorage::SparseDelegates.Remove(Object); + if (FSparseDelegateStorage::SparseDelegates.Num() == 0) + { + DisableListener(); + } +} + +void FSparseDelegateStorage::FObjectListener::EnableListener() +{ + GUObjectArray.AddUObjectDeleteListener(this); +} + +void FSparseDelegateStorage::FObjectListener::DisableListener() +{ + GUObjectArray.RemoveUObjectDeleteListener(this); +} + void FSparseDelegateStorage::RegisterDelegateOffset(const UObject* OwningObject, const FName DelegateName, const size_t DelegateOffsetToOwner) { check(OwningObject); @@ -45,27 +76,36 @@ UObject* FSparseDelegateStorage::ResolveSparseOwner(const FSparseDelegate& Spars FMulticastScriptDelegate* FSparseDelegateStorage::GetMulticastDelegate(const UObject* DelegateOwner, const FName DelegateName) { - FSparseDelegateMap DelegateMap = SparseDelegates.GetAnnotation(DelegateOwner); - if (TSharedPtr* MulticastDelegatePtr = DelegateMap.Find(DelegateName)) - { - return MulticastDelegatePtr->Get(); - } + FScopeLock SparseDelegateMapLock(&SparseDelegateMapCritical); + if (FSparseDelegateMap* DelegateMap = SparseDelegates.Find(DelegateOwner)) + { + if (TSharedPtr* MulticastDelegatePtr = DelegateMap->Find(DelegateName)) + { + return MulticastDelegatePtr->Get(); + } + } return nullptr; } -FMulticastScriptDelegate& FSparseDelegateStorage::GetOrCreateMulticastDelegate(const UObject* DelegateOwner, const FName DelegateName) +void FSparseDelegateStorage::SetMulticastDelegate(const UObject* DelegateOwner, const FName DelegateName, FMulticastScriptDelegate Delegate) { - FSparseDelegateMap DelegateMap = SparseDelegates.GetAnnotation(DelegateOwner); + FScopeLock SparseDelegateMapLock(&SparseDelegateMapCritical); + + if (SparseDelegates.Num() == 0) + { + SparseDelegateObjectListener.EnableListener(); + } + + FSparseDelegateMap& DelegateMap = SparseDelegates.FindOrAdd(DelegateOwner); TSharedPtr& MulticastDelegate = DelegateMap.FindOrAdd(DelegateName); if (!MulticastDelegate.IsValid()) { MulticastDelegate = MakeShared(); - SparseDelegates.AddAnnotation(DelegateOwner, MoveTemp(DelegateMap)); } - return *MulticastDelegate; + *MulticastDelegate = MoveTemp(Delegate); } bool FSparseDelegateStorage::Add(const UObject* DelegateOwner, const FName DelegateName, FScriptDelegate Delegate) @@ -73,7 +113,14 @@ bool FSparseDelegateStorage::Add(const UObject* DelegateOwner, const FName Deleg bool bDelegateWasBound = false; if (Delegate.IsBound()) { - FSparseDelegateMap DelegateMap = SparseDelegates.GetAnnotation(DelegateOwner); + FScopeLock SparseDelegateMapLock(&SparseDelegateMapCritical); + + if (SparseDelegates.Num() == 0) + { + SparseDelegateObjectListener.EnableListener(); + } + + FSparseDelegateMap& DelegateMap = SparseDelegates.FindOrAdd(DelegateOwner); TSharedPtr& MulticastDelegate = DelegateMap.FindOrAdd(DelegateName); if (!MulticastDelegate.IsValid()) @@ -82,7 +129,6 @@ bool FSparseDelegateStorage::Add(const UObject* DelegateOwner, const FName Deleg } MulticastDelegate->Add(MoveTemp(Delegate)); - SparseDelegates.AddAnnotation(DelegateOwner, MoveTemp(DelegateMap)); bDelegateWasBound = true; } return bDelegateWasBound; @@ -93,7 +139,14 @@ bool FSparseDelegateStorage::AddUnique(const UObject* DelegateOwner, const FName bool bDelegateWasBound = false; if (Delegate.IsBound()) { - FSparseDelegateMap DelegateMap = SparseDelegates.GetAnnotation(DelegateOwner); + FScopeLock SparseDelegateMapLock(&SparseDelegateMapCritical); + + if (SparseDelegates.Num() == 0) + { + SparseDelegateObjectListener.EnableListener(); + } + + FSparseDelegateMap& DelegateMap = SparseDelegates.FindOrAdd(DelegateOwner); TSharedPtr& MulticastDelegate = DelegateMap.FindOrAdd(DelegateName); if (!MulticastDelegate.IsValid()) @@ -102,7 +155,6 @@ bool FSparseDelegateStorage::AddUnique(const UObject* DelegateOwner, const FName } MulticastDelegate->AddUnique(MoveTemp(Delegate)); - SparseDelegates.AddAnnotation(DelegateOwner, MoveTemp(DelegateMap)); bDelegateWasBound = true; } return bDelegateWasBound; @@ -111,12 +163,16 @@ bool FSparseDelegateStorage::AddUnique(const UObject* DelegateOwner, const FName bool FSparseDelegateStorage::Contains(const UObject* DelegateOwner, const FName DelegateName, const FScriptDelegate& Delegate) { bool bContainsDelegate = false; - FSparseDelegateMap DelegateMap = SparseDelegates.GetAnnotation(DelegateOwner); - if (TSharedPtr* MulticastDelegatePtr = DelegateMap.Find(DelegateName)) + FScopeLock SparseDelegateMapLock(&SparseDelegateMapCritical); + + if (FSparseDelegateMap* DelegateMap = SparseDelegates.Find(DelegateOwner)) { - if (FMulticastScriptDelegate* MulticastDelegate = MulticastDelegatePtr->Get()) + if (TSharedPtr* MulticastDelegatePtr = DelegateMap->Find(DelegateName)) { - bContainsDelegate = MulticastDelegate->Contains(Delegate); + if (FMulticastScriptDelegate* MulticastDelegate = MulticastDelegatePtr->Get()) + { + bContainsDelegate = MulticastDelegate->Contains(Delegate); + } } } @@ -126,12 +182,16 @@ bool FSparseDelegateStorage::Contains(const UObject* DelegateOwner, const FName bool FSparseDelegateStorage::Contains(const UObject* DelegateOwner, const FName DelegateName, const UObject* InObject, FName InFunctionName) { bool bContainsDelegate = false; - FSparseDelegateMap DelegateMap = SparseDelegates.GetAnnotation(DelegateOwner); - if (TSharedPtr* MulticastDelegatePtr = DelegateMap.Find(DelegateName)) + FScopeLock SparseDelegateMapLock(&SparseDelegateMapCritical); + + if (FSparseDelegateMap* DelegateMap = SparseDelegates.Find(DelegateOwner)) { - if (FMulticastScriptDelegate* MulticastDelegate = MulticastDelegatePtr->Get()) + if (TSharedPtr* MulticastDelegatePtr = DelegateMap->Find(DelegateName)) { - bContainsDelegate = MulticastDelegate->Contains(InObject, InFunctionName); + if (FMulticastScriptDelegate* MulticastDelegate = MulticastDelegatePtr->Get()) + { + bContainsDelegate = MulticastDelegate->Contains(InObject, InFunctionName); + } } } return bContainsDelegate; @@ -140,31 +200,35 @@ bool FSparseDelegateStorage::Contains(const UObject* DelegateOwner, const FName bool FSparseDelegateStorage::Remove(const UObject* DelegateOwner, const FName DelegateName, const FScriptDelegate& Delegate) { bool bSparseDelegateBound = false; - FSparseDelegateMap DelegateMap = SparseDelegates.GetAnnotation(DelegateOwner); - if (TSharedPtr* MulticastDelegatePtr = DelegateMap.Find(DelegateName)) + FScopeLock SparseDelegateMapLock(&SparseDelegateMapCritical); + + if (FSparseDelegateMap* DelegateMap = SparseDelegates.Find(DelegateOwner)) { - if (FMulticastScriptDelegate* MulticastDelegate = MulticastDelegatePtr->Get()) + if (TSharedPtr* MulticastDelegatePtr = DelegateMap->Find(DelegateName)) { - MulticastDelegate->Remove(Delegate); - bSparseDelegateBound = MulticastDelegate->IsBound(); - if (!bSparseDelegateBound) + if (FMulticastScriptDelegate* MulticastDelegate = MulticastDelegatePtr->Get()) { - DelegateMap.Remove(DelegateName); + MulticastDelegate->Remove(Delegate); + bSparseDelegateBound = MulticastDelegate->IsBound(); + if (!bSparseDelegateBound) + { + DelegateMap->Remove(DelegateName); + } + } + else + { + DelegateMap->Remove(DelegateName); } } - else + if (DelegateMap->Num() == 0) { - DelegateMap.Remove(DelegateName); + SparseDelegates.Remove(DelegateOwner); + if (SparseDelegates.Num() == 0) + { + SparseDelegateObjectListener.DisableListener(); + } } } - if (DelegateMap.IsDefault()) - { - SparseDelegates.RemoveAnnotation(DelegateOwner); - } - else - { - SparseDelegates.AddAnnotation(DelegateOwner, MoveTemp(DelegateMap)); - } return bSparseDelegateBound; } @@ -172,31 +236,35 @@ bool FSparseDelegateStorage::Remove(const UObject* DelegateOwner, const FName De bool FSparseDelegateStorage::Remove(const UObject* DelegateOwner, const FName DelegateName, const UObject* InObject, FName InFunctionName) { bool bSparseDelegateBound = false; - FSparseDelegateMap DelegateMap = SparseDelegates.GetAnnotation(DelegateOwner); - if (TSharedPtr* MulticastDelegatePtr = DelegateMap.Find(DelegateName)) + FScopeLock SparseDelegateMapLock(&SparseDelegateMapCritical); + + if (FSparseDelegateMap* DelegateMap = SparseDelegates.Find(DelegateOwner)) { - if (FMulticastScriptDelegate* MulticastDelegate = MulticastDelegatePtr->Get()) + if (TSharedPtr* MulticastDelegatePtr = DelegateMap->Find(DelegateName)) { - MulticastDelegate->Remove(InObject, InFunctionName); - bSparseDelegateBound = MulticastDelegate->IsBound(); - if (!bSparseDelegateBound) + if (FMulticastScriptDelegate* MulticastDelegate = MulticastDelegatePtr->Get()) { - DelegateMap.Remove(DelegateName); + MulticastDelegate->Remove(InObject, InFunctionName); + bSparseDelegateBound = MulticastDelegate->IsBound(); + if (!bSparseDelegateBound) + { + DelegateMap->Remove(DelegateName); + } + } + else + { + DelegateMap->Remove(DelegateName); } } - else + if (DelegateMap->Num() == 0) { - DelegateMap.Remove(DelegateName); + SparseDelegates.Remove(DelegateOwner); + if (SparseDelegates.Num() == 0) + { + SparseDelegateObjectListener.DisableListener(); + } } } - if (DelegateMap.IsDefault()) - { - SparseDelegates.RemoveAnnotation(DelegateOwner); - } - else - { - SparseDelegates.AddAnnotation(DelegateOwner, MoveTemp(DelegateMap)); - } return bSparseDelegateBound; } @@ -204,52 +272,59 @@ bool FSparseDelegateStorage::Remove(const UObject* DelegateOwner, const FName De bool FSparseDelegateStorage::RemoveAll(const UObject* DelegateOwner, const FName DelegateName, const UObject* UserObject) { bool bSparseDelegateBound = false; - FSparseDelegateMap DelegateMap = SparseDelegates.GetAnnotation(DelegateOwner); - if (TSharedPtr* MulticastDelegatePtr = DelegateMap.Find(DelegateName)) + FScopeLock SparseDelegateMapLock(&SparseDelegateMapCritical); + + if (FSparseDelegateMap* DelegateMap = SparseDelegates.Find(DelegateOwner)) { - if (FMulticastScriptDelegate* MulticastDelegate = MulticastDelegatePtr->Get()) + if (TSharedPtr* MulticastDelegatePtr = DelegateMap->Find(DelegateName)) { - MulticastDelegate->RemoveAll(UserObject); - bSparseDelegateBound = MulticastDelegate->IsBound(); - if (!bSparseDelegateBound) + if (FMulticastScriptDelegate* MulticastDelegate = MulticastDelegatePtr->Get()) { - DelegateMap.Remove(DelegateName); + MulticastDelegate->RemoveAll(UserObject); + bSparseDelegateBound = MulticastDelegate->IsBound(); + if (!bSparseDelegateBound) + { + DelegateMap->Remove(DelegateName); + } + } + else + { + DelegateMap->Remove(DelegateName); } } - else + if (DelegateMap->Num() == 0) { - DelegateMap.Remove(DelegateName); + SparseDelegates.Remove(DelegateOwner); + if (SparseDelegates.Num() == 0) + { + SparseDelegateObjectListener.DisableListener(); + } } } - if (DelegateMap.IsDefault()) - { - SparseDelegates.RemoveAnnotation(DelegateOwner); - } - else - { - SparseDelegates.AddAnnotation(DelegateOwner, MoveTemp(DelegateMap)); - } return bSparseDelegateBound; } void FSparseDelegateStorage::Clear(const UObject* DelegateOwner, const FName DelegateName) { - FSparseDelegateMap DelegateMap = SparseDelegates.GetAnnotation(DelegateOwner); - if (TSharedPtr* MulticastDelegatePtr = DelegateMap.Find(DelegateName)) + FScopeLock SparseDelegateMapLock(&SparseDelegateMapCritical); + if (FSparseDelegateMap* DelegateMap = SparseDelegates.Find(DelegateOwner)) { - if (FMulticastScriptDelegate* MulticastDelegate = MulticastDelegatePtr->Get()) + if (TSharedPtr* MulticastDelegatePtr = DelegateMap->Find(DelegateName)) { - MulticastDelegate->Clear(); + if (FMulticastScriptDelegate* MulticastDelegate = MulticastDelegatePtr->Get()) + { + MulticastDelegate->Clear(); + } + DelegateMap->Remove(DelegateName); + } + if (DelegateMap->Num() == 0) + { + SparseDelegates.Remove(DelegateOwner); + if (SparseDelegates.Num() == 0) + { + SparseDelegateObjectListener.DisableListener(); + } } - DelegateMap.Remove(DelegateName); - } - if (DelegateMap.IsDefault()) - { - SparseDelegates.RemoveAnnotation(DelegateOwner); - } - else - { - SparseDelegates.AddAnnotation(DelegateOwner, MoveTemp(DelegateMap)); } } @@ -316,32 +391,36 @@ void FSparseDelegateStorage::SparseDelegateReport(const TArray& Args, U TArray Details; uint32 BoundObjects = 0; uint32 BoundDelegates = 0; - for (const TPair& ObjectAnnotation : SparseDelegates.GetAnnotationMap()) + { - const UObject* Object = static_cast(ObjectAnnotation.Key); - if (ObjectName.IsNone() || Object->GetFName() == ObjectName) + FScopeLock SparseDelegateMapLock(&SparseDelegateMapCritical); + for (const TPair& ObjectAnnotation : SparseDelegates) { - if (ObjectType == nullptr || Object->IsA(ObjectType)) + const UObject* Object = static_cast(ObjectAnnotation.Key); + if (ObjectName.IsNone() || Object->GetFName() == ObjectName) { - if (!bSummary) + if (ObjectType == nullptr || Object->IsA(ObjectType)) { - Details.Add(FString::Printf(TEXT("%s"), *Object->GetPathName())); - } - ++BoundObjects; - if (DelegateName.IsNone()) - { - BoundDelegates += ObjectAnnotation.Value.Num(); if (!bSummary) { - for (const TPair>& BoundDelegate : ObjectAnnotation.Value) + Details.Add(FString::Printf(TEXT("%s"), *Object->GetPathName())); + } + ++BoundObjects; + if (DelegateName.IsNone()) + { + BoundDelegates += ObjectAnnotation.Value.Num(); + if (!bSummary) { - Details.Add(FString::Printf(TEXT(" %s"), *BoundDelegate.Key.ToString())); + for (const TPair>& BoundDelegate : ObjectAnnotation.Value) + { + Details.Add(FString::Printf(TEXT(" %s"), *BoundDelegate.Key.ToString())); + } } } - } - else if (ObjectAnnotation.Value.Contains(DelegateName)) - { - ++BoundDelegates; + else if (ObjectAnnotation.Value.Contains(DelegateName)) + { + ++BoundDelegates; + } } } } diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectBase.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectBase.cpp index 350b788fd271..142947745c15 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectBase.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectBase.cpp @@ -852,7 +852,7 @@ void UClassReplaceHotReloadClasses() RegisteredClass = Class->Register(); } - FCoreUObjectDelegates::RegisterClassForHotReloadReinstancingDelegate.Broadcast(Class->OldClass, RegisteredClass); + FCoreUObjectDelegates::RegisterClassForHotReloadReinstancingDelegate.Broadcast(Class->OldClass, RegisteredClass, Class->bHasChanged ? EHotReloadedClassFlags::Changed : EHotReloadedClassFlags::None); } } diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectGlobals.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectGlobals.cpp index 6ed28e108b6c..b74b1415d98d 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectGlobals.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectGlobals.cpp @@ -1155,30 +1155,7 @@ UPackage* LoadPackageInternal(UPackage* InOuter, const TCHAR* InLongPackageNameO } // Set up a load context - TRefCountPtr LoadContext; - if (InLoadContext) - { - // Use the privided context - LoadContext = InLoadContext; - } - else - { - // Try to get the context from the callstack - FUObjectThreadContext& ThreadContext = FUObjectThreadContext::Get(); - if (ThreadContext.SerializeContext) - { - LoadContext = ThreadContext.SerializeContext; - } - else - { - // Create a new context - LoadContext = new FUObjectSerializeContext(); - if (!IsInAsyncLoadingThread()) - { - ThreadContext.SerializeContext = LoadContext; - } - } - } + TRefCountPtr LoadContext = FUObjectThreadContext::Get().GetSerializeContext(); // Try to load. BeginLoad(LoadContext, InLongPackageNameOrFilename); @@ -1758,16 +1735,6 @@ void EndLoad(FUObjectSerializeContext* LoadContext) { LoadContext->DetachFromLinkers(); } - - FUObjectThreadContext& ThreadContext = FUObjectThreadContext::Get(); - if (ThreadContext.SerializeContext == LoadContext) - { - ThreadContext.SerializeContext = nullptr; - } - else - { - check(!ThreadContext.SerializeContext); - } } } @@ -2073,7 +2040,7 @@ UObject* StaticDuplicateObjectEx( FObjectDuplicationParameters& Parameters ) SerializedObjects.Add(Object); }; - TRefCountPtr LoadContext(new FUObjectSerializeContext()); + TRefCountPtr LoadContext(FUObjectThreadContext::Get().GetSerializeContext()); FDuplicateDataReader Reader(DuplicatedObjectAnnotation, ObjectData, Parameters.PortFlags, Parameters.DestOuter); Reader.SetSerializeContext(LoadContext); for(int32 ObjectIndex = 0;ObjectIndex < SerializedObjects.Num();ObjectIndex++) diff --git a/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectThreadContext.cpp b/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectThreadContext.cpp index 11754d07f6c6..9d4507148991 100644 --- a/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectThreadContext.cpp +++ b/Engine/Source/Runtime/CoreUObject/Private/UObject/UObjectThreadContext.cpp @@ -17,9 +17,12 @@ FUObjectThreadContext::FUObjectThreadContext() , IsInConstructor(0) , ConstructedObject(nullptr) , AsyncPackage(nullptr) -, SerializeContext(nullptr) +, SerializeContext(new FUObjectSerializeContext()) {} +FUObjectThreadContext::~FUObjectThreadContext() +{ +} FUObjectSerializeContext::FUObjectSerializeContext() : RefCount(0) @@ -59,6 +62,11 @@ void FUObjectSerializeContext::AddUniqueLoadedObjects(const TArray& In } +void FUObjectSerializeContext::AddLoadedObject(UObject* InObject) +{ + ObjectsLoaded.Add(InObject); +} + bool FUObjectSerializeContext::PRIVATE_PatchNewObjectIntoExport(UObject* OldObject, UObject* NewObject) { const int32 ObjLoadedIdx = ObjectsLoaded.Find(OldObject); @@ -78,10 +86,15 @@ void FUObjectSerializeContext::AttachLinker(FLinkerLoad* InLinker) AttachedLinkers.Add(InLinker); } +void FUObjectSerializeContext::DetachLinker(FLinkerLoad* InLinker) +{ + AttachedLinkers.Remove(InLinker); +} + void FUObjectSerializeContext::DetachFromLinkers() { check(!GEventDrivenLoaderEnabled); - check(ObjectsLoaded.Num() == 0); + check(ObjectsLoaded.Num() == 0 || AttachedLinkers.Num() == 0); TArray LinkersToDetach = AttachedLinkers.Array(); for (FLinkerLoad* Linker : LinkersToDetach) { diff --git a/Engine/Source/Runtime/CoreUObject/Public/Serialization/AsyncLoading.h b/Engine/Source/Runtime/CoreUObject/Public/Serialization/AsyncLoading.h index b48acb2615f3..ef43290feffa 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/Serialization/AsyncLoading.h +++ b/Engine/Source/Runtime/CoreUObject/Public/Serialization/AsyncLoading.h @@ -945,7 +945,7 @@ private: public: /** Serialization context for this package */ - TRefCountPtr LoadContext; + FUObjectSerializeContext* GetSerializeContext(); }; struct FScopedAsyncPackageEvent diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/LinkerLoad.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/LinkerLoad.h index 35425321f32d..3c4d381f6caf 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/LinkerLoad.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/LinkerLoad.h @@ -1185,11 +1185,6 @@ private: // FLinkerLoad creation helpers END // -private: - - /** Current UObject serialization context */ - TRefCountPtr CurrentLoadContext; - public: //~ FArchive interface diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/SparseDelegate.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/SparseDelegate.h index 065bac3ffeaf..0a53f659d63d 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/SparseDelegate.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/SparseDelegate.h @@ -4,9 +4,9 @@ #include "CoreMinimal.h" #include "UObject/Object.h" -#include "UObject/UObjectAnnotation.h" #include "UObject/WeakObjectPtr.h" #include "Delegates/Delegate.h" +#include "Misc/ScopeLock.h" struct FSparseDelegate; @@ -52,8 +52,8 @@ public: /** Acquires the actual Multicast Delegate from the annotation if any delegates are bound to it. Will be null if no entry exists in the annotation for this object/delegatename. */ static FMulticastScriptDelegate* GetMulticastDelegate(const UObject* DelegateOwner, const FName DelegateName); - /** Acquires the actual Multicast Delegate from the annotation if any delegates are bound to it. Will be created if no entry exists in the annotation for this object/delegatename. */ - static FMulticastScriptDelegate& GetOrCreateMulticastDelegate(const UObject* DelegateOwner, const FName DelegateName); + /** Directly sets the Multicast Delegate for this object/delegatename pair. If the delegate is unbound it will be assigned/inserted anyways. */ + static void SetMulticastDelegate(const UObject* DelegateOwner, const FName DelegateName, FMulticastScriptDelegate Delegate); /** Using the registry of sparse delegates recover the FSparseDelegate address from the UObject and name. */ static FSparseDelegate* ResolveSparseDelegate(const UObject* OwningObject, FName DelegateName); @@ -66,13 +66,29 @@ public: private: - struct FSparseDelegateMap : public TMap> + struct FObjectListener : public FUObjectArray::FUObjectDeleteListener { - bool IsDefault() const { return Num() == 0; } + virtual ~FObjectListener(); + virtual void NotifyUObjectDeleted(const UObjectBase* Object, int32 Index) override; + void EnableListener(); + void DisableListener(); }; - static FUObjectAnnotationSparse SparseDelegates; + /** Allow the object listener to use the critical section and remove objects from the map */ + friend struct FObjectListener; + /** A listener to get notified when objects have been deleted and remove them from the map */ + static FObjectListener SparseDelegateObjectListener; + + /** Critical Section for locking access to the sparse delegate map */ + static FCriticalSection SparseDelegateMapCritical; + + /** Delegate map is a map of Delegate names to a shared pointer of the multicast script delegate */ + typedef TMap> FSparseDelegateMap; + + /** Map of objects to the map of delegates that are bound to that object */ + static TMap SparseDelegates; + /** Sparse delegate offsets are indexed by ActorClass/DelegateName pair */ static TMap, size_t> SparseDelegateObjectOffsets; }; diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectGlobals.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectGlobals.h index fc6ce5398226..2226777f848b 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectGlobals.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectGlobals.h @@ -1958,6 +1958,16 @@ class FPackageReloadedEvent; class FGarbageCollectionTracer; +enum class EHotReloadedClassFlags +{ + None = 0, + + // Set when the hot reloaded class has been detected as changed + Changed = 0x01 +}; + +ENUM_CLASS_FLAGS(EHotReloadedClassFlags) + /** * Global CoreUObject delegates */ @@ -2023,7 +2033,7 @@ struct COREUOBJECT_API FCoreUObjectDelegates static FRegisterHotReloadAddedClassesDelegate RegisterHotReloadAddedClassesDelegate; /** Delegate for registering hot-reloaded classes that changed after hot-reload for reinstancing */ - DECLARE_MULTICAST_DELEGATE_TwoParams(FRegisterClassForHotReloadReinstancingDelegate, UClass*, UClass*); + DECLARE_MULTICAST_DELEGATE_ThreeParams(FRegisterClassForHotReloadReinstancingDelegate, UClass*, UClass*, EHotReloadedClassFlags); static FRegisterClassForHotReloadReinstancingDelegate RegisterClassForHotReloadReinstancingDelegate; /** Delegate for reinstancing hot-reloaded classes */ diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectThreadContext.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectThreadContext.h index 56e53486b220..44ac25aead9b 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectThreadContext.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/UObjectThreadContext.h @@ -9,6 +9,7 @@ #include "CoreMinimal.h" #include "HAL/ThreadSingleton.h" #include "HAL/ThreadSafeCounter.h" +#include "Templates/RefCounting.h" class FObjectInitializer; struct FUObjectSerializeContext; @@ -21,6 +22,7 @@ class COREUOBJECT_API FUObjectThreadContext : public TThreadSingleton; FUObjectThreadContext(); + virtual ~FUObjectThreadContext(); /** Stack of currently used FObjectInitializers for this thread */ TArray InitializerStack; @@ -76,8 +78,6 @@ public: UObject* ConstructedObject; /** Async Package currently processing objects */ struct FAsyncPackage* AsyncPackage; - /** Current serialization context (only valid for non-async loads) */ - FUObjectSerializeContext* SerializeContext; #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) /** Stack to ensure that PostInitProperties is routed through Super:: calls. **/ @@ -89,13 +89,31 @@ public: /** Maps a package name to all packages marked as editor-only due to the fact it was marked as editor-only */ TMap> PackagesMarkedEditorOnlyByOtherPackage; #endif + + /** Gets the current serialization context */ + FUObjectSerializeContext* GetSerializeContext() + { + return SerializeContext; + } + +private: + /** Current serialization context */ + TRefCountPtr SerializeContext; }; /** Structure that holds the current serialization state of UObjects */ struct COREUOBJECT_API FUObjectSerializeContext { + friend class FUObjectThreadContext; + private: + /** Constructor */ + FUObjectSerializeContext(); + + /** Destructor */ + ~FUObjectSerializeContext(); + /** Reference count of this context */ int32 RefCount; @@ -127,19 +145,8 @@ public: /** Points to the most recently used Linker for serialization by CreateExport() */ FLinkerLoad* SerializedExportLinker; - /** Constructor */ - FUObjectSerializeContext(); - - /** Destructor */ - ~FUObjectSerializeContext(); - /** Adds a new loaded object */ - void AddLoadedObject(UObject* InObject) - { - check(AttachedLinkers.Num() || GEventDrivenLoaderEnabled); - ObjectsLoaded.Add(InObject); - } - + void AddLoadedObject(UObject* InObject); void AddUniqueLoadedObjects(const TArray& InObjects); /** Checks if object loading has started */ @@ -226,10 +233,7 @@ public: void AttachLinker(FLinkerLoad* InLinker); /** Detaches a linker from this context */ - void DetachLinker(FLinkerLoad* InLinker) - { - AttachedLinkers.Remove(InLinker); - } + void DetachLinker(FLinkerLoad* InLinker); /** Detaches all linkers from this context */ void DetachFromLinkers(); diff --git a/Engine/Source/Runtime/CoreUObject/Public/UObject/UnrealType.h b/Engine/Source/Runtime/CoreUObject/Public/UObject/UnrealType.h index 72c3e7270c80..add969008916 100644 --- a/Engine/Source/Runtime/CoreUObject/Public/UObject/UnrealType.h +++ b/Engine/Source/Runtime/CoreUObject/Public/UObject/UnrealType.h @@ -4238,7 +4238,7 @@ protected: const TCHAR* ImportText_Add( const TCHAR* Buffer, void* PropertyValue, int32 PortFlags, UObject* Parent, FOutputDevice* ErrorText ) const; const TCHAR* ImportText_Remove( const TCHAR* Buffer, void* PropertyValue, int32 PortFlags, UObject* Parent, FOutputDevice* ErrorText ) const; - const TCHAR* ImportDelegateFromText(FMulticastScriptDelegate& MulticastDelegate, const TCHAR* Buffer, void* Data, int32 PortFlags, UObject* OwnerObject, FOutputDevice* ErrorText) const; + const TCHAR* ImportDelegateFromText(FMulticastScriptDelegate& MulticastDelegate, const TCHAR* Buffer, UObject* OwnerObject, FOutputDevice* ErrorText) const; }; template diff --git a/Engine/Source/Runtime/D3D12RHI/Private/D3D12Allocation.cpp b/Engine/Source/Runtime/D3D12RHI/Private/D3D12Allocation.cpp index 56f57f19a34b..44558f34e22e 100644 --- a/Engine/Source/Runtime/D3D12RHI/Private/D3D12Allocation.cpp +++ b/Engine/Source/Runtime/D3D12RHI/Private/D3D12Allocation.cpp @@ -303,8 +303,7 @@ void FD3D12BuddyAllocator::Allocate(uint32 SizeInBytes, uint32 Alignment, FD3D12 // track the allocation #if !PLATFORM_WINDOWS - LLM(uint64 Addr = (ResourceLocation.GetGPUVirtualAddress() != 0ull) ? (uint64)ResourceLocation.GetGPUVirtualAddress() : AlignedOffsetFromResourceBase); - LLM(FLowLevelMemTracker::Get().OnLowLevelAlloc(ELLMTracker::Default, (void*)Addr, SizeInBytes)); + LLM(FLowLevelMemTracker::Get().OnLowLevelAlloc(ELLMTracker::Default, &ResourceLocation, SizeInBytes)); // Note: Disabling this LLM hook for Windows is due to a work-around in the way that d3d12 buffers are tracked // by LLM. LLM tracks buffer data in the UpdateBufferStats function because that is the easiest place to ensure that LLM // can be updated whenever a buffer is created or released. Unfortunately, some buffers allocate from this allocator @@ -365,8 +364,7 @@ void FD3D12BuddyAllocator::Deallocate(FD3D12ResourceLocation& ResourceLocation) // which means that the memory would be counted twice. Because of this the tracking had to be disabled here. // This does mean that non-buffer memory that goes through this allocator won't be tracked, so this does need a better solution. // see UpdateBufferStats for a more detailed explanation. - LLM(uint64 Addr = (ResourceLocation.GetGPUVirtualAddress() != 0ull) ? (uint64)ResourceLocation.GetGPUVirtualAddress() : ResourceLocation.GetOffsetFromBaseOfResource()); - LLM(FLowLevelMemTracker::Get().OnLowLevelFree(ELLMTracker::Default, (void*)Addr)); + LLM(FLowLevelMemTracker::Get().OnLowLevelFree(ELLMTracker::Default, &ResourceLocation)); #endif } diff --git a/Engine/Source/Runtime/D3D12RHI/Private/D3D12Resources.cpp b/Engine/Source/Runtime/D3D12RHI/Private/D3D12Resources.cpp index f99eb50e60d9..e5fe64395714 100644 --- a/Engine/Source/Runtime/D3D12RHI/Private/D3D12Resources.cpp +++ b/Engine/Source/Runtime/D3D12RHI/Private/D3D12Resources.cpp @@ -468,6 +468,14 @@ void FD3D12ResourceLocation::TransferOwnership(FD3D12ResourceLocation& Destinati FMemory::Memmove(&Destination, &Source, sizeof(FD3D12ResourceLocation)); + // update tracked allocation +#if !PLATFORM_WINDOWS && ENABLE_LOW_LEVEL_MEM_TRACKER + if (Source.GetType() == ResourceLocationType::eSubAllocation) + { + FLowLevelMemTracker::Get().OnLowLevelAllocMoved( ELLMTracker::Default, &Destination, &Source ); + } +#endif + // Destroy the source but don't invoke any resource destruction Source.InternalClear(); } diff --git a/Engine/Source/Runtime/D3D12RHI/Private/Windows/WindowsD3D12Device.cpp b/Engine/Source/Runtime/D3D12RHI/Private/Windows/WindowsD3D12Device.cpp index e59bb7803ab3..a6fc0b654dec 100644 --- a/Engine/Source/Runtime/D3D12RHI/Private/Windows/WindowsD3D12Device.cpp +++ b/Engine/Source/Runtime/D3D12RHI/Private/Windows/WindowsD3D12Device.cpp @@ -15,6 +15,8 @@ #include "Runtime/HeadMountedDisplay/Public/IHeadMountedDisplayModule.h" #include "GenericPlatform/GenericPlatformDriver.h" // FGPUDriverInfo +#include "ShaderCompiler.h" + #pragma comment(lib, "d3d12.lib") IMPLEMENT_MODULE(FD3D12DynamicRHIModule, D3D12RHI); @@ -700,6 +702,17 @@ void FD3D12DynamicRHI::Init() void FD3D12DynamicRHI::PostInit() { + if (!FPlatformProperties::RequiresCookedData() && (GRHISupportsRayTracing || GRHISupportsRHIThread)) + { + // Make sure all global shaders are complete at this point + extern RENDERCORE_API const int32 GlobalShaderMapId; + + TArray ShaderMapIds; + ShaderMapIds.Add(GlobalShaderMapId); + + GShaderCompilingManager->FinishCompilation(TEXT("Global"), ShaderMapIds); + } + if (GRHISupportsRayTracing) { for (FD3D12Adapter*& Adapter : ChosenAdapters) diff --git a/Engine/Source/Runtime/Engine/Classes/Animation/AnimCurveCompressionCodec.h b/Engine/Source/Runtime/Engine/Classes/Animation/AnimCurveCompressionCodec.h index 2ab72d0da565..2c2f5b67d05d 100644 --- a/Engine/Source/Runtime/Engine/Classes/Animation/AnimCurveCompressionCodec.h +++ b/Engine/Source/Runtime/Engine/Classes/Animation/AnimCurveCompressionCodec.h @@ -38,6 +38,9 @@ class ENGINE_API UAnimCurveCompressionCodec : public UObject FGuid InstanceGuid; #endif + /** Allow us to convert DDC serialized path back into codec object */ + virtual UAnimCurveCompressionCodec* GetCodec(const FString& Path) { return this; } + ////////////////////////////////////////////////////////////////////////// #if WITH_EDITORONLY_DATA @@ -52,8 +55,6 @@ class ENGINE_API UAnimCurveCompressionCodec : public UObject /** Compresses the curve data from an animation sequence. */ virtual bool Compress(const UAnimSequence& AnimSeq, FAnimCurveCompressionResult& OutResult) PURE_VIRTUAL(UAnimCurveCompressionCodec::Compress, return false;); - /** Allow us to convert DDC serialized path back into codec object */ - virtual UAnimCurveCompressionCodec* GetCodec(const FString& Path) { return this; } /* * Called to generate a unique DDC key for this codec instance. * A suitable key should be generated from: the InstanceGuid, a codec version, and all relevant properties that drive the behavior. diff --git a/Engine/Source/Runtime/Engine/Classes/Animation/AnimCurveCompressionSettings.h b/Engine/Source/Runtime/Engine/Classes/Animation/AnimCurveCompressionSettings.h index 06eb5125a483..48da018de23a 100644 --- a/Engine/Source/Runtime/Engine/Classes/Animation/AnimCurveCompressionSettings.h +++ b/Engine/Source/Runtime/Engine/Classes/Animation/AnimCurveCompressionSettings.h @@ -17,14 +17,15 @@ class ENGINE_API UAnimCurveCompressionSettings : public UObject { GENERATED_UCLASS_BODY() -#if WITH_EDITORONLY_DATA /** An animation curve compression codec. */ UPROPERTY(Category = Compression, Instanced, EditAnywhere, NoClear) UAnimCurveCompressionCodec* Codec; -#endif ////////////////////////////////////////////////////////////////////////// + /** Allow us to convert DDC serialized path back into codec object */ + UAnimCurveCompressionCodec* GetCodec(const FString& Path); + #if WITH_EDITORONLY_DATA /** Returns whether or not we can use these settings to compress. */ bool AreSettingsValid() const; @@ -36,9 +37,6 @@ class ENGINE_API UAnimCurveCompressionSettings : public UObject */ bool Compress(UAnimSequence& AnimSeq) const; - /** Allow us to convert DDC serialized path back into codec object */ - UAnimCurveCompressionCodec* GetCodec(const FString& Path); - /** Generates a DDC key that takes into account the current settings and selected codec. */ FString MakeDDCKey() const; #endif diff --git a/Engine/Source/Runtime/Engine/Classes/Animation/AnimSequence.h b/Engine/Source/Runtime/Engine/Classes/Animation/AnimSequence.h index 5c57d7b530b7..94de086aa622 100644 --- a/Engine/Source/Runtime/Engine/Classes/Animation/AnimSequence.h +++ b/Engine/Source/Runtime/Engine/Classes/Animation/AnimSequence.h @@ -688,10 +688,11 @@ public: UPROPERTY(Category = Compression, EditAnywhere) bool bAllowFrameStripping; +#endif + /** The curve compression settings used to compress curves in this sequence. */ UPROPERTY(Category=Compression, EditAnywhere) class UAnimCurveCompressionSettings* CurveCompressionSettings; -#endif // WITH_EDITORONLY_DATA /** The codec used by the compressed data as determined by the compression settings. */ class UAnimCurveCompressionCodec* CurveCompressionCodec; @@ -1063,10 +1064,10 @@ public: * @return The approximate size of compressed animation data. */ int32 GetApproxCompressedSize() const; -#if WITH_EDITORONLY_DATA + // Initialize curve compression settings, does nothing if scheme already valid void InitCurveCompressionScheme(); -#endif + /** * Utility function for lossless compression of a FRawAnimSequenceTrack * @return true if keys were removed. diff --git a/Engine/Source/Runtime/Engine/Classes/Components/RectLightComponent.h b/Engine/Source/Runtime/Engine/Classes/Components/RectLightComponent.h index e574cdddd4ec..2f1bc1a6aabe 100644 --- a/Engine/Source/Runtime/Engine/Classes/Components/RectLightComponent.h +++ b/Engine/Source/Runtime/Engine/Classes/Components/RectLightComponent.h @@ -48,9 +48,12 @@ class ENGINE_API URectLightComponent : public ULocalLightComponent float BarnDoorLength; /** Texture mapped to the light source rectangle */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Light) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Light) class UTexture* SourceTexture; + UFUNCTION(BlueprintCallable, Category = "Rendering|Lighting") + void SetSourceTexture(UTexture* bNewValue); + UFUNCTION(BlueprintCallable, Category="Rendering|Lighting") void SetSourceWidth(float bNewValue); @@ -75,9 +78,15 @@ public: virtual float GetUniformPenumbraSize() const override; virtual FLightSceneProxy* CreateSceneProxy() const override; + virtual void BeginDestroy() override; //~ Begin UObject Interface #if WITH_EDITOR virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; #endif // WITH_EDITOR //~ End UObject Interface + +private: + void UpdateRayTracingData(); + friend class FRectLightSceneProxy; + struct FRectLightRayTracingData* RayTracingData; }; \ No newline at end of file diff --git a/Engine/Source/Runtime/Engine/Classes/Components/SkeletalMeshComponent.h b/Engine/Source/Runtime/Engine/Classes/Components/SkeletalMeshComponent.h index 3250c36bc57e..c5f0520fd650 100644 --- a/Engine/Source/Runtime/Engine/Classes/Components/SkeletalMeshComponent.h +++ b/Engine/Source/Runtime/Engine/Classes/Components/SkeletalMeshComponent.h @@ -1288,7 +1288,7 @@ public: * * @param LODIndex Index of LOD [0-(MaxLOD-1)] */ - bool RecalcRequiredBones(int32 LODIndex); + void RecalcRequiredBones(int32 LODIndex); /** Computes the required bones in this SkeletalMeshComponent based on current SkeletalMesh, LOD and PhysicsAsset * @param LODIndex Index of LOD [0-(MaxLOD-1)] @@ -1301,21 +1301,6 @@ public: */ void RecalcRequiredCurves(); -private: - /** - * Recalculates the RequiredBones array in this SkeletalMeshComponent based on current SkeletalMesh, LOD and PhysicsAsset. - * Only modifies members of the SMC (safe to call while parallel task is in flight - * - * @param LODIndex Index of LOD [0-(MaxLOD-1)] - */ - bool RecalcRequiredBonesInternalSMC(int32 LODIndex); - - /** - * Causes the anim instances this component owns to recalc their required bones. - * RecalcRequiredBonesInternalSMC must be called first - */ - void RecalcRequiredBonesInternalAnimInstances(); - public: //~ Begin UObject Interface. virtual void Serialize(FArchive& Ar) override; diff --git a/Engine/Source/Runtime/Engine/Classes/Components/SkinnedMeshComponent.h b/Engine/Source/Runtime/Engine/Classes/Components/SkinnedMeshComponent.h index 01600af74f43..3a6d8d83815a 100644 --- a/Engine/Source/Runtime/Engine/Classes/Components/SkinnedMeshComponent.h +++ b/Engine/Source/Runtime/Engine/Classes/Components/SkinnedMeshComponent.h @@ -430,6 +430,10 @@ public: uint8& MeshComponentUpdateFlag = *(uint8*)&VisibilityBasedAnimTickOption; #endif +protected: + /** Record of the tick rate we are using when externally controlled */ + uint8 ExternalTickRate; + protected: /** used to cache previous bone transform or not */ uint8 bHasValidBoneTransform:1; @@ -572,6 +576,15 @@ public: /** Set whether we have our tick rate externally controlled non-URO-based interpolation */ void EnableExternalTickRateControl(bool bInEnable) { bExternalTickRateControlled = bInEnable; } + /** Check whether we we have our tick rate externally controlled */ + bool IsUsingExternalTickRateControl() const { return bExternalTickRateControlled; } + + /** Set the external tick rate */ + void SetExternalTickRate(uint8 InTickRate) { ExternalTickRate = InTickRate; } + + /** Get the external tick rate */ + uint8 GetExternalTickRate() const { return ExternalTickRate; } + /** Enable non-URO-based interpolation */ void EnableExternalInterpolation(bool bInEnable) { bExternalInterpolate = bInEnable; } @@ -781,6 +794,9 @@ public: virtual int32 GetNumMaterials() const override; //~ End UPrimitiveComponent Interface + /** Get the pre-skinning local space bounds for this component. */ + FBoxSphereBounds GetPreSkinnedLocalBounds() const; + /** * Sets the value of the bForceWireframe flag and reattaches the component as necessary. * diff --git a/Engine/Source/Runtime/Engine/Classes/Engine/BlueprintGeneratedClass.h b/Engine/Source/Runtime/Engine/Classes/Engine/BlueprintGeneratedClass.h index 1e018d97e892..86c630e4477f 100644 --- a/Engine/Source/Runtime/Engine/Classes/Engine/BlueprintGeneratedClass.h +++ b/Engine/Source/Runtime/Engine/Classes/Engine/BlueprintGeneratedClass.h @@ -541,7 +541,7 @@ struct ENGINE_API FBlueprintCookedComponentInstancingData /** Flag indicating whether or not this contains valid cooked data. Note that an empty changed property list can also be a valid template data context. */ UPROPERTY() - bool bIsValid; + bool bHasValidCookedData; /** List of property info records with values that differ between the template and the component class CDO. This list will be generated at cook time. */ UPROPERTY() @@ -559,7 +559,7 @@ struct ENGINE_API FBlueprintCookedComponentInstancingData /** Default constructor. */ FBlueprintCookedComponentInstancingData() { - bIsValid = false; + bHasValidCookedData = false; ComponentTemplateClass = nullptr; ComponentTemplateFlags = RF_NoFlags; } diff --git a/Engine/Source/Runtime/Engine/Classes/Engine/DemoNetDriver.h b/Engine/Source/Runtime/Engine/Classes/Engine/DemoNetDriver.h index b93ed8d76e78..6df7b5868c7d 100644 --- a/Engine/Source/Runtime/Engine/Classes/Engine/DemoNetDriver.h +++ b/Engine/Source/Runtime/Engine/Classes/Engine/DemoNetDriver.h @@ -390,7 +390,7 @@ class ENGINE_API UDemoNetDriver : public UNetDriver /** Index of LevelNames that is currently loaded */ int32 CurrentLevelIndex; - /** This is our spectator controller that is used to view the demo world from */ + /** This is the main spectator controller that is used to view the demo world from */ APlayerController* SpectatorController; /** Our network replay streamer */ @@ -608,6 +608,8 @@ public: virtual void ProcessLocalServerPackets() override {} virtual void ProcessLocalClientPackets() override {} + virtual void InitDestroyedStartupActors() override; + protected: virtual UChannel* InternalCreateChannelByName(const FName& ChName) override; @@ -725,6 +727,69 @@ public: void FinalizeFastForward( const double StartTime ); void SpawnDemoRecSpectator( UNetConnection* Connection, const FURL& ListenURL ); + + /** + * Restores the given player controller so that it properly points to the given NetConnection + * after scrubbing when viewing a replay. + * + * @param PC The PlayerController to set up the given NetConnection for + * @param NetConnection The NetConnection to be assigned to the player controller. + */ + void RestoreConnectionPostScrub(APlayerController* PC, UNetConnection* NetConnection); + + /** + * Sets the main spectator controller to be used and adds them to the spectator control array + * + * @param PC The PlayerController to set the main controller param to. + */ + void SetSpectatorController(APlayerController* PC); + + // Splitscreen demo handling + + /** + * Creates a new splitscreen replay viewer. + * + * @param NewPlayer The LocalPlayer in control of this new viewer + * @param InWorld The world to spawn the new viewer in. + * + * @return If the viewer was able to be created or not. + */ + bool SpawnSplitscreenViewer(ULocalPlayer* NewPlayer, UWorld* InWorld); + + /** + * Removes a splitscreen demo viewer and cleans up its connection. + * + * @param RemovePlayer The PlayerController to remove from the replay system + * @param bMarkOwnerForDeletion If this function should handle deleting the given player as well. + * + * @return If the player was successfully removed from the replay. + */ + bool RemoveSplitscreenViewer(APlayerController* RemovePlayer, bool bMarkOwnerForDeletion=false); + +private: + + // Internal player spawning + APlayerController* CreateDemoPlayerController(UNetConnection* Connection, const FURL& ListenURL); + + // Internal splitscreen management + + /** An array of all the spectator controllers (the main one and all splitscreen ones) that currently exist */ + UPROPERTY(transient) + TArray SpectatorControllers; + + /** + * Removes all child connections for splitscreen viewers. + * This should be done before the ClientConnections or ServerConnection + * variables change or during most travel scenarios. + * + * @param bDeleteOwner If the connections should delete the owning actor to the connection + * + * @return The number of splitscreen connections cleaned up. + */ + int32 CleanUpSplitscreenConnections(bool bDeleteOwner); + +public: + void ResetDemoState(); void JumpToEndOfLiveReplay(); void AddEvent(const FString& Group, const FString& Meta, const TArray& Data); @@ -930,7 +995,7 @@ private: // Levels that are currently pending for fast forward. // Using raw pointers, because we manually keep when levels are added and removed. - TMap>> LevelsPendingFastForward; + TSet LevelsPendingFastForward; // Only used during recording. uint32 NumLevelsAddedThisFrame; @@ -976,6 +1041,9 @@ private: FArchivePos CheckpointOffset; uint32 GuidCacheSize; + TSet DeltaDeletedNetStartupActors; + TSet DeltaDeletedActorGuids; + void CountBytes(FArchive& Ar) const { CheckpointAckState.CountBytes(Ar); diff --git a/Engine/Source/Runtime/Engine/Classes/Engine/EngineTypes.h b/Engine/Source/Runtime/Engine/Classes/Engine/EngineTypes.h index 6921a75f9690..ff0e24f19e40 100644 --- a/Engine/Source/Runtime/Engine/Classes/Engine/EngineTypes.h +++ b/Engine/Source/Runtime/Engine/Classes/Engine/EngineTypes.h @@ -3336,6 +3336,8 @@ struct FReplicationFlags uint32 bIgnoreRPCs:1; /** True if we should not swap the role and remote role of this actor when receiving properties. */ uint32 bSkipRoleSwap:1; + /** True if we should only compare role properties in CompareProperties */ + uint32 bRolesOnly:1; }; uint32 Value; diff --git a/Engine/Source/Runtime/Engine/Classes/Engine/GameInstance.h b/Engine/Source/Runtime/Engine/Classes/Engine/GameInstance.h index 44f105f9ec09..3015d4e0e6bc 100644 --- a/Engine/Source/Runtime/Engine/Classes/Engine/GameInstance.h +++ b/Engine/Source/Runtime/Engine/Classes/Engine/GameInstance.h @@ -260,7 +260,7 @@ public: * @param NewPlayer the player to add * @param ControllerId id of the controller associated with the player */ - int32 AddLocalPlayer(ULocalPlayer * NewPlayer, int32 ControllerId); + virtual int32 AddLocalPlayer(ULocalPlayer* NewPlayer, int32 ControllerId); /** diff --git a/Engine/Source/Runtime/Engine/Classes/Engine/GameViewportClient.h b/Engine/Source/Runtime/Engine/Classes/Engine/GameViewportClient.h index 64e5fdcaaa05..6bf7ba6a3667 100644 --- a/Engine/Source/Runtime/Engine/Classes/Engine/GameViewportClient.h +++ b/Engine/Source/Runtime/Engine/Classes/Engine/GameViewportClient.h @@ -866,7 +866,7 @@ private: TMap> HardwareCursors; /** Map of Software Cursor Widgets*/ - TMap> CursorWidgets; + TMap> CursorWidgets; /** Controls if the Map of Software Cursor Widgets is used */ bool bUseSoftwareCursorWidgets; diff --git a/Engine/Source/Runtime/Engine/Classes/Engine/Level.h b/Engine/Source/Runtime/Engine/Classes/Engine/Level.h index a21ae75c993e..4c52fa1286c2 100644 --- a/Engine/Source/Runtime/Engine/Classes/Engine/Level.h +++ b/Engine/Source/Runtime/Engine/Classes/Engine/Level.h @@ -342,6 +342,7 @@ struct FReplicatedStaticActorDestructionInfo GENERATED_BODY() FName PathName; + FString FullName; FVector DestroyedPosition; TWeakObjectPtr ObjOuter; UPROPERTY() diff --git a/Engine/Source/Runtime/Engine/Classes/Engine/NetSerialization.h b/Engine/Source/Runtime/Engine/Classes/Engine/NetSerialization.h index 3d5d48bf7648..33faba9d5cc0 100644 --- a/Engine/Source/Runtime/Engine/Classes/Engine/NetSerialization.h +++ b/Engine/Source/Runtime/Engine/Classes/Engine/NetSerialization.h @@ -395,6 +395,18 @@ struct FFastArraySerializer_FastArrayDeltaSerialize_FIdxIDPair int32 ID; }; +UENUM() +enum class EFastArraySerializerDeltaFlags : uint8 +{ + None, //! No flags. + HasBeenSerialized = 1 << 0, //! Set when serialization at least once (i.e., this struct has been written or read). + HasDeltaBeenRequested = 1 << 1, //! Set if users requested Delta Serialization for this struct. + IsUsingDeltaSerialization = 1 << 2, //! This will remain unset until we've serialized at least once. + //! At that point, this will be set if delta serialization was requested and + //! we support it. +}; +ENUM_CLASS_FLAGS(EFastArraySerializerDeltaFlags); + /** Base struct for wrapping the array used in Fast TArray Replication */ USTRUCT() struct FFastArraySerializer @@ -406,9 +418,10 @@ struct FFastArraySerializer , ArrayReplicationKey(0) , CachedNumItems(INDEX_NONE) , CachedNumItemsToConsiderForWriting(INDEX_NONE) - , bUseDeltaStructSerialization(false) - , bCachedUseDeltaStructSerialization(false) - , bHasBeenSerialized(false) { } + , DeltaFlags(EFastArraySerializerDeltaFlags::None) + { + SetDeltaSerializationEnabled(true); + } ~FFastArraySerializer() {} @@ -419,6 +432,7 @@ struct FFastArraySerializer int32 IDCounter; /** Counter used to track array replication. */ + UPROPERTY(NotReplicated) int32 ArrayReplicationKey; /** List of items that need to be re-serialized when the referenced objects are mapped */ @@ -507,14 +521,28 @@ struct FFastArraySerializer return !bIsWritingOnClient || Item.ReplicationID != INDEX_NONE; } - const bool IsUsingStructDeltaSerialization() const + void SetDeltaSerializationEnabled(const bool bEnabled) { - return bHasBeenSerialized ? bCachedUseDeltaStructSerialization : bUseDeltaStructSerialization; + if (!EnumHasAnyFlags(DeltaFlags, EFastArraySerializerDeltaFlags::HasBeenSerialized)) + { + if (bEnabled) + { + DeltaFlags |= EFastArraySerializerDeltaFlags::HasDeltaBeenRequested; + } + else + { + DeltaFlags &= ~EFastArraySerializerDeltaFlags::HasDeltaBeenRequested; + } + } + else + { + UE_LOG(LogNetFastTArray, Log, TEXT("FFastArraySerializer::SetDeltaSerializationEnabled - Called after array has been serialized. Ignoring")); + } } - const bool HasBeenSerialized() const + const EFastArraySerializerDeltaFlags GetDeltaSerializationFlags() const { - return bHasBeenSerialized; + return DeltaFlags; } private: @@ -641,15 +669,8 @@ private: int32 CachedNumItems; int32 CachedNumItemsToConsiderForWriting; -protected: - - uint32 bUseDeltaStructSerialization : 1; - -private: - - uint32 bCachedUseDeltaStructSerialization : 1; - - uint32 bHasBeenSerialized : 1; + UPROPERTY(NotReplicated) + EFastArraySerializerDeltaFlags DeltaFlags; }; template @@ -1040,7 +1061,7 @@ bool FFastArraySerializer::FastArrayDeltaSerialize(TArray &Items, FNetDelt // anything from the server (Net Conditions, Static Actors, etc.) // That should be fine though, because none of the GUID Tracking work should actually do anything // until after we've received. - if (ArraySerializer.bHasBeenSerialized && ArraySerializer.bCachedUseDeltaStructSerialization) + if (EnumHasAllFlags(ArraySerializer.DeltaFlags, EFastArraySerializerDeltaFlags::IsUsingDeltaSerialization)) { return FastArrayDeltaSerialize_DeltaSerializeStructs(Items, Parms, ArraySerializer); } @@ -1202,13 +1223,12 @@ bool FFastArraySerializer::FastArrayDeltaSerialize(TArray &Items, FNetDelt // If we've made it this far, it means that we're going to be serializing something. // So, it should be safe for us to update our cached state. // Also, make sure that we hit the right path if we need to. - if (!ArraySerializer.bHasBeenSerialized) + if (!EnumHasAnyFlags(ArraySerializer.DeltaFlags, EFastArraySerializerDeltaFlags::HasBeenSerialized)) { - ArraySerializer.bHasBeenSerialized = true; - ArraySerializer.bCachedUseDeltaStructSerialization = ArraySerializer.bUseDeltaStructSerialization && Parms.bSupportsFastArrayDeltaStructSerialization; - - if (ArraySerializer.bCachedUseDeltaStructSerialization) + ArraySerializer.DeltaFlags |= EFastArraySerializerDeltaFlags::HasBeenSerialized; + if (Parms.bSupportsFastArrayDeltaStructSerialization && EnumHasAnyFlags(ArraySerializer.DeltaFlags, EFastArraySerializerDeltaFlags::HasDeltaBeenRequested)) { + ArraySerializer.DeltaFlags |= EFastArraySerializerDeltaFlags::IsUsingDeltaSerialization; return FastArrayDeltaSerialize_DeltaSerializeStructs(Items, Parms, ArraySerializer); } } @@ -1421,19 +1441,59 @@ struct FFastArrayDeltaSerializeParams { FNetDeltaSerializeInfo& DeltaSerializeInfo; FFastArraySerializer& ArraySerializer; - const PTRINT PatchedItemOffset; - const PTRINT PatchedArrayOffset; + + const TFunction PreReplicatedRemove; + const TFunction PostReplicatedAdd; + const TFunction PostReplicatedChange; + const TFunction ReceivedItem; TArray>* WriteChangedElements = nullptr; FNetFastTArrayBaseState* WriteBaseState = nullptr; TArray>* ReadChangedElements = nullptr; TArray>* ReadAddedElements = nullptr; int32 ReadNumChanged = INDEX_NONE; + int32 ReadArrayReplicationKey = INDEX_NONE; }; template bool FFastArraySerializer::FastArrayDeltaSerialize_DeltaSerializeStructs(TArray& Items, FNetDeltaSerializeInfo& Parms, SerializerType& ArraySerializer) { + /** + * These methods are exposed on FastArraySerializerItems, but they aren't virtual. + * Further, we may not know the exact type when we want to call them, and won't + * safely be able to cast to the type in non-templated code. + * + * Maybe this defeats the purpose of having them not virtual in the first place. + * However, for now ReceivedItem and PostReplicatedChanged are the only ones that will actually + * be called in this way, whereas PostReplicatedAdd and PreReplicatedRemove will still be called + * from templated code. + */ + struct FFastArrayItemCallbackHelper + { + static void PreReplicatedRemove(void* FastArrayItem, const struct FFastArrayDeltaSerializeParams& Params) + { + reinterpret_cast(FastArrayItem)->PreReplicatedRemove(static_cast(Params.ArraySerializer)); + } + + static void PostReplicatedAdd(void* FastArrayItem, const struct FFastArrayDeltaSerializeParams& Params) + { + reinterpret_cast(FastArrayItem)->PostReplicatedAdd(static_cast(Params.ArraySerializer)); + } + + static void PostReplicatedChange(void* FastArrayItem, const struct FFastArrayDeltaSerializeParams& Params) + { + reinterpret_cast(FastArrayItem)->PostReplicatedChange(static_cast(Params.ArraySerializer)); + } + + static void ReceivedItem(void* FastArrayItem, const FFastArrayDeltaSerializeParams& Params, const uint32 ReplicationID) + { + Type* Item = reinterpret_cast(FastArrayItem); + Item->ReplicationID = ReplicationID; + Item->MostRecentArrayReplicationKey = Params.ReadArrayReplicationKey; + Item->ReplicationKey++; + } + }; + SCOPE_CYCLE_COUNTER(STAT_NetSerializeFastArray_DeltaStruct); class UScriptStruct* InnerStruct = Type::StaticStruct(); @@ -1444,17 +1504,15 @@ bool FFastArraySerializer::FastArrayDeltaSerialize_DeltaSerializeStructs(TArray< Parms }; -#define CALC_PATCH_OFFSET(Base, Derived) ((PTRINT)((Base*)((Derived*)1)) - 1) - FFastArrayDeltaSerializeParams DeltaSerializeParams{ Parms, ArraySerializer, - /*PatchedItemOffset=*/ CALC_PATCH_OFFSET(FFastArraySerializerItem, Type), - /*PatchedArrayOffset*/ CALC_PATCH_OFFSET(FFastArraySerializer, SerializerType) + FFastArrayItemCallbackHelper::PreReplicatedRemove, + FFastArrayItemCallbackHelper::PostReplicatedAdd, + FFastArrayItemCallbackHelper::PostReplicatedChange, + FFastArrayItemCallbackHelper::ReceivedItem, }; -#undef CALC_PATCH_OFFSET - //--------------- // Build ItemMap if necessary. This maps ReplicationID to our local index into the Items array. //--------------- @@ -1566,6 +1624,7 @@ bool FFastArraySerializer::FastArrayDeltaSerialize_DeltaSerializeStructs(TArray< DeltaSerializeParams.ReadAddedElements = &AddedIndices; DeltaSerializeParams.ReadChangedElements = &ChangedIndices; DeltaSerializeParams.ReadNumChanged = Header.NumChanged; + DeltaSerializeParams.ReadArrayReplicationKey = Header.ArrayReplicationKey; if (!Parms.NetSerializeCB->NetDeltaSerializeForFastArray(DeltaSerializeParams)) { diff --git a/Engine/Source/Runtime/Engine/Classes/Engine/SkeletalMesh.h b/Engine/Source/Runtime/Engine/Classes/Engine/SkeletalMesh.h index 215b52a77ed1..b7016271dff2 100644 --- a/Engine/Source/Runtime/Engine/Classes/Engine/SkeletalMesh.h +++ b/Engine/Source/Runtime/Engine/Classes/Engine/SkeletalMesh.h @@ -417,6 +417,29 @@ struct FSkeletalMaterial DECLARE_MULTICAST_DELEGATE_OneParam(FOnPostMeshCache, class USkeletalMesh*); #endif +#if WITH_EDITORONLY_DATA +namespace NSSkeletalMeshSourceFileLabels +{ + static FText GeoAndSkinningText() + { + static FText GeoAndSkinningText = (NSLOCTEXT("FBXReimport", "ImportContentTypeAll", "Geometry and Skinning Weights")); + return GeoAndSkinningText; + } + + static FText GeometryText() + { + static FText GeometryText = (NSLOCTEXT("FBXReimport", "ImportContentTypeGeometry", "Geometry")); + return GeometryText; + } + static FText SkinningText() + { + static FText SkinningText = (NSLOCTEXT("FBXReimport", "ImportContentTypeSkinning", "Skinning Weights")); + return SkinningText; + } +} +#endif + + /** * SkeletalMesh is geometry bound to a hierarchical skeleton of bones which can be animated for the purpose of deforming the mesh. * Skeletal Meshes are built up of two parts; a set of polygons composed to make up the surface of the mesh, and a hierarchical skeleton which can be used to animate the polygons. @@ -489,11 +512,11 @@ public: /** Get the extended bounds of this mesh (imported bounds plus bounds extension) */ UFUNCTION(BlueprintCallable, Category = Mesh) - FBoxSphereBounds GetBounds(); + FBoxSphereBounds GetBounds() const; /** Get the original imported bounds of the skel mesh */ UFUNCTION(BlueprintCallable, Category = Mesh) - FBoxSphereBounds GetImportedBounds(); + FBoxSphereBounds GetImportedBounds() const; /** Set the original imported bounds of the skel mesh, will recalculate extended bounds */ void SetImportedBounds(const FBoxSphereBounds& InBounds); @@ -644,6 +667,8 @@ public: UPROPERTY(EditAnywhere, Instanced, Category=ImportSettings) class UAssetImportData* AssetImportData; + static FText GetSourceFileLabelFromIndex(int32 SourceFileIndex); + /** Path to the resource used to construct this skeletal mesh */ UPROPERTY() FString SourceFilePath_DEPRECATED; diff --git a/Engine/Source/Runtime/Engine/Classes/Engine/World.h b/Engine/Source/Runtime/Engine/Classes/Engine/World.h index dce71abe6029..9e12ac43e697 100644 --- a/Engine/Source/Runtime/Engine/Classes/Engine/World.h +++ b/Engine/Source/Runtime/Engine/Classes/Engine/World.h @@ -2763,6 +2763,9 @@ public: /** Handle Exec/Console Commands related to the World */ bool Exec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar=*GLog ); + /** Mark the world as being torn down */ + void BeginTearingDown(); + private: /** Utility function to handle Exec/Console Commands related to the Trace Tags */ bool HandleTraceTagCommand( const TCHAR* Cmd, FOutputDevice& Ar ); diff --git a/Engine/Source/Runtime/Engine/Classes/GameFramework/CharacterMovementComponent.h b/Engine/Source/Runtime/Engine/Classes/GameFramework/CharacterMovementComponent.h index c0273a780283..e46c3ad3ddb8 100644 --- a/Engine/Source/Runtime/Engine/Classes/GameFramework/CharacterMovementComponent.h +++ b/Engine/Source/Runtime/Engine/Classes/GameFramework/CharacterMovementComponent.h @@ -668,6 +668,12 @@ protected: /** Used when throttling "stuck in geometry" logging, to output the number of events we skipped if throttling. */ uint32 StuckWarningCountSinceNotify; + /** + * Used to limit number of jump apex attempts per tick. + * @see MaxJumpApexAttemptsPerSimulation + */ + int32 NumJumpApexAttempts; + public: /** Returns the location at the end of the last tick. */ @@ -721,6 +727,13 @@ public: UPROPERTY(Category="Character Movement (General Settings)", EditAnywhere, BlueprintReadWrite, AdvancedDisplay, meta=(ClampMin="1", ClampMax="25", UIMin="1", UIMax="25")) int32 MaxSimulationIterations; + /** + * Max number of attempts per simulation to attempt to exactly reach the jump apex when falling movement reaches the top of the arc. + * Limiting this prevents deep recursion when special cases cause collision or other conditions which reactivate the apex condition. + */ + UPROPERTY(Category="Character Movement (General Settings)", EditAnywhere, BlueprintReadWrite, AdvancedDisplay, meta=(ClampMin="1", ClampMax="4", UIMin="1", UIMax="4")) + int32 MaxJumpApexAttemptsPerSimulation; + /** * Max distance we allow simulated proxies to depenetrate when moving out of anything but Pawns. * This is generally more tolerant than with Pawns, because other geometry is either not moving, or is moving predictably with a bit of delay compared to on the server. @@ -908,14 +921,25 @@ public: uint8 bNetworkMovementModeChanged:1; /** - * True when we should ignore server location difference checks for client error on this movement component + * If true, we should ignore server location difference checks for client error on this movement component. * This can be useful when character is moving at extreme speeds for a duration and you need it to look - * smooth on clients. Make sure to disable when done, as this would break this character's server-client - * movement correction. + * smooth on clients without the server correcting the client. Make sure to disable when done, as this would + * break this character's server-client movement correction. + * @see bServerAcceptClientAuthoritativePosition, ServerCheckClientError() */ UPROPERTY(Transient, Category="Character Movement", EditAnywhere, BlueprintReadWrite) uint8 bIgnoreClientMovementErrorChecksAndCorrection:1; + /** + * If true, and server does not detect client position error, server will copy the client movement location/velocity/etc after simulating the move. + * This can be useful for short bursts of movement that are difficult to sync over the network. + * Note that if bIgnoreClientMovementErrorChecksAndCorrection is used, this means the server will not detect an error. + * Also see GameNetworkManager->ClientAuthorativePosition which permanently enables this behavior. + * @see bIgnoreClientMovementErrorChecksAndCorrection, ServerShouldUseAuthoritativePosition() + */ + UPROPERTY(Transient, Category="Character Movement", EditAnywhere, BlueprintReadWrite) + uint8 bServerAcceptClientAuthoritativePosition : 1; + /** * If true, event NotifyJumpApex() to CharacterOwner's controller when at apex of jump. Is cleared when event is triggered. * By default this is off, and if you want the event to fire you typically set it to true when movement mode changes to "Falling" from another mode (see OnMovementModeChanged). @@ -2151,6 +2175,11 @@ protected: */ virtual bool ServerCheckClientError(float ClientTimeStamp, float DeltaTime, const FVector& Accel, const FVector& ClientWorldLocation, const FVector& RelativeClientLocation, UPrimitiveComponent* ClientMovementBase, FName ClientBaseBoneName, uint8 ClientMovementMode); + /** + * If ServerCheckClientError() does not find an error, this determines if the server should also copy the client's movement params rather than keep the server sim result. + */ + virtual bool ServerShouldUseAuthoritativePosition(float ClientTimeStamp, float DeltaTime, const FVector& Accel, const FVector& ClientWorldLocation, const FVector& RelativeClientLocation, UPrimitiveComponent* ClientMovementBase, FName ClientBaseBoneName, uint8 ClientMovementMode); + /* Process a move at the given time stamp, given the compressed flags representing various events that occurred (ie jump). */ virtual void MoveAutonomous( float ClientTimeStamp, float DeltaTime, uint8 CompressedFlags, const FVector& NewAccel); diff --git a/Engine/Source/Runtime/Engine/Classes/GameFramework/GameNetworkManager.h b/Engine/Source/Runtime/Engine/Classes/GameFramework/GameNetworkManager.h index 4c5076ba0c22..bf348db34713 100644 --- a/Engine/Source/Runtime/Engine/Classes/GameFramework/GameNetworkManager.h +++ b/Engine/Source/Runtime/Engine/Classes/GameFramework/GameNetworkManager.h @@ -132,6 +132,14 @@ class ENGINE_API AGameNetworkManager : public AInfo /** MaxClientForcedUpdateDuration is the maximum time duration over which the server will force updates, after MAXCLIENTUPDATEINTERVAL is initially exceeded. */ UPROPERTY(GlobalConfig) float MaxClientForcedUpdateDuration; + + /** Ignore forced client movement updates when server hitches for longer than this duration. */ + UPROPERTY(GlobalConfig) + float ServerForcedUpdateHitchThreshold; + + /** Ignore forced client movement updates when server hitch was detected within this amount of time in the past. */ + UPROPERTY(GlobalConfig) + float ServerForcedUpdateHitchCooldown; /** MaxMoveDeltaTime is the default maximum time delta of CharacterMovement ServerMoves. Should be less than or equal to MAXCLIENTUPDATEINTERVAL, otherwise server will interfere by forcing position updates. */ UPROPERTY(GlobalConfig) diff --git a/Engine/Source/Runtime/Engine/Classes/GameFramework/PlayerController.h b/Engine/Source/Runtime/Engine/Classes/GameFramework/PlayerController.h index 8735386cc298..769c28266962 100644 --- a/Engine/Source/Runtime/Engine/Classes/GameFramework/PlayerController.h +++ b/Engine/Source/Runtime/Engine/Classes/GameFramework/PlayerController.h @@ -1887,6 +1887,12 @@ protected: /** Set the SpawnLocation for use when changing states or when there is no pawn or spectator. */ virtual void SetSpawnLocation(const FVector& NewLocation); + /** Last real time (undilated) recorded in TickActor() when checking for forced client movement updates. */ + float LastMovementUpdateTime; + + /** Last real time (undilated) a hitch was detected in TickActor() when checking for forced client movement updates. */ + float LastMovementHitch; + public: /** Get the location used when initially created, or when changing states when there is no pawn or spectator. */ FVector GetSpawnLocation() const { return SpawnLocation; } diff --git a/Engine/Source/Runtime/Engine/Classes/Kismet/KismetMathLibrary.h b/Engine/Source/Runtime/Engine/Classes/Kismet/KismetMathLibrary.h index 5b9824ee4921..e839dac8c117 100644 --- a/Engine/Source/Runtime/Engine/Classes/Kismet/KismetMathLibrary.h +++ b/Engine/Source/Runtime/Engine/Classes/Kismet/KismetMathLibrary.h @@ -901,8 +901,8 @@ class ENGINE_API UKismetMathLibrary : public UBlueprintFunctionLibrary * @param InX New X coordinate. * @param InY New Y coordinate. */ - UFUNCTION(BlueprintPure, meta = (ScriptMethod = "Set"), Category = "Math|Vector2D") - static void Set2D(UPARAM(ref) FVector2D& A, float InX, float InY); + UFUNCTION(BlueprintCallable, meta = (ScriptMethod = "Set"), Category = "Math|Vector2D") + static void Set2D(UPARAM(ref) FVector2D& A, float X, float Y); /** * Creates a copy of this vector with both axes clamped to the given range. @@ -1041,7 +1041,7 @@ class ENGINE_API UKismetMathLibrary : public UBlueprintFunctionLibrary * @param Tolerance Minimum squared length of vector for normalization. * @see NormalSafe2D() */ - UFUNCTION(BlueprintPure, meta = (DisplayName = "Normalize In Place (Vector2D)", Keywords = "Unit Vector", ScriptMethod = "Normalize"), Category = "Math|Vector2D") + UFUNCTION(BlueprintCallable, meta = (DisplayName = "Normalize In Place (Vector2D)", Keywords = "Unit Vector", ScriptMethod = "Normalize"), Category = "Math|Vector2D") static void Normalize2D(UPARAM(ref) FVector2D& A, float Tolerance = 1.e-8); /** Converts spherical coordinates on the unit sphere into a Cartesian unit length vector. */ @@ -1136,7 +1136,7 @@ class ENGINE_API UKismetMathLibrary : public UBlueprintFunctionLibrary * * @param InVector Vector to copy values from. */ - UFUNCTION(BlueprintPure, meta = (ScriptMethod = "Assign"), Category = "Math|Vector") + UFUNCTION(BlueprintCallable, meta = (ScriptMethod = "Assign"), Category = "Math|Vector") static void Vector_Assign(UPARAM(ref) FVector& A, const FVector& InVector); /** @@ -1146,8 +1146,8 @@ class ENGINE_API UKismetMathLibrary : public UBlueprintFunctionLibrary * @param InY New Y coordinate. * @param InZ New Z coordinate. */ - UFUNCTION(BlueprintPure, meta = (ScriptMethod = "Set"), Category = "Math|Vector") - static void Vector_Set(UPARAM(ref) FVector& A, float InX, float InY, float InZ); + UFUNCTION(BlueprintCallable, meta = (ScriptMethod = "Set"), Category = "Math|Vector") + static void Vector_Set(UPARAM(ref) FVector& A, float X, float Y, float Z); /** Breaks a vector apart into X, Y, Z */ UFUNCTION(BlueprintPure, Category="Math|Vector", meta=(NativeBreakFunc)) @@ -1279,7 +1279,7 @@ class ENGINE_API UKismetMathLibrary : public UBlueprintFunctionLibrary static FVector LessLess_VectorRotator(FVector A, FRotator B); /** When this vector contains Euler angles (degrees), ensure that angles are between +/-180 */ - UFUNCTION(BlueprintPure, meta = (ScriptMethod = "UnwindEuler"), Category = "Math|Vector") + UFUNCTION(BlueprintCallable, meta = (ScriptMethod = "UnwindEuler"), Category = "Math|Vector") static void Vector_UnwindEuler(UPARAM(ref) FVector& A); /** Create a copy of this vector, with its magnitude/size/length clamped between Min and Max. */ @@ -1547,7 +1547,7 @@ class ENGINE_API UKismetMathLibrary : public UBlueprintFunctionLibrary * * @param Tolerance Minimum squared length of vector for normalization. */ - UFUNCTION(BlueprintPure, meta = (DisplayName = "Normalize In Place (Vector)", ScriptMethod = "Normalize", Keywords = "Unit Vector"), Category = "Math|Vector") + UFUNCTION(BlueprintCallable, meta = (DisplayName = "Normalize In Place (Vector)", ScriptMethod = "Normalize", Keywords = "Unit Vector"), Category = "Math|Vector") static void Vector_Normalize(UPARAM(ref) FVector& A, float Tolerance = 1.e-8); /** Linearly interpolates between A and B based on Alpha (100% of A when Alpha=0 and 100% of B when Alpha=1) */ @@ -1661,7 +1661,7 @@ class ENGINE_API UKismetMathLibrary : public UBlueprintFunctionLibrary * @param InAddVect Vector to add. * @param InRadius Half size of the cube. */ - UFUNCTION(BlueprintPure, meta=(ScriptMethod = "AddBounded", Keywords = "Bounding"), Category="Math|Vector") + UFUNCTION(BlueprintCallable, meta=(ScriptMethod = "AddBounded", Keywords = "Bounding"), Category="Math|Vector") static void Vector_AddBounded(UPARAM(ref) FVector& A, FVector InAddVect, float InRadius); /** Get a copy of this vector, clamped inside of the specified axis aligned cube. */ @@ -1903,7 +1903,7 @@ class ENGINE_API UKismetMathLibrary : public UBlueprintFunctionLibrary * * @param InVector Vector to copy values from. */ - UFUNCTION(BlueprintPure, meta = (ScriptMethod = "Assign"), Category = "Math|Vector4") + UFUNCTION(BlueprintCallable, meta = (ScriptMethod = "Assign"), Category = "Math|Vector4") static void Vector4_Assign(UPARAM(ref) FVector4& A, const FVector4& InVector); /** @@ -1914,8 +1914,8 @@ class ENGINE_API UKismetMathLibrary : public UBlueprintFunctionLibrary * @param InZ New Z coordinate. * @param InW New W coordinate. */ - UFUNCTION(BlueprintPure, meta = (ScriptMethod = "Set"), Category = "Math|Vector4") - static void Vector4_Set(UPARAM(ref) FVector4& A, float InX, float InY, float InZ, float InW); + UFUNCTION(BlueprintCallable, meta = (ScriptMethod = "Set"), Category = "Math|Vector4") + static void Vector4_Set(UPARAM(ref) FVector4& A, float X, float Y, float Z, float W); /** Returns the cross product of two vectors - see http://mathworld.wolfram.com/CrossProduct.html */ UFUNCTION(BlueprintPure, meta = (DisplayName = "Cross Product XYZ (Vector4)", CompactNodeTitle = "cross3", ScriptMethod = "Cross3"), Category = "Math|Vector4") @@ -2009,7 +2009,7 @@ class ENGINE_API UKismetMathLibrary : public UBlueprintFunctionLibrary * * @param Tolerance Minimum squared length of vector for normalization. */ - UFUNCTION(BlueprintPure, meta = (DisplayName = "Normalize In Place XYZ (Vector4)", ScriptMethod = "Normalize3", Keywords = "Unit Vector"), Category = "Math|Vector4") + UFUNCTION(BlueprintCallable, meta = (DisplayName = "Normalize In Place XYZ (Vector4)", ScriptMethod = "Normalize3", Keywords = "Unit Vector"), Category = "Math|Vector4") static void Vector4_Normalize3(UPARAM(ref) FVector4& A, float Tolerance = 1.e-8); /** @@ -2269,7 +2269,7 @@ class ENGINE_API UKismetMathLibrary : public UBlueprintFunctionLibrary static float Quat_AngularDistance(const FQuat& A, const FQuat& B); /** Modify the quaternion to ensure that the delta between it and B represents the shortest possible rotation angle. */ - UFUNCTION(BlueprintPure, meta = (DisplayName = "Ensure shortest arc to (Quat)", ScriptMethod = "EnsureShortestArcTo"), Category = "Math|Quat") + UFUNCTION(BlueprintCallable, meta = (DisplayName = "Ensure shortest arc to (Quat)", ScriptMethod = "EnsureShortestArcTo"), Category = "Math|Quat") static void Quat_EnforceShortestArcWith(UPARAM(ref) FQuat& A, const FQuat& B); /** Convert a Quaternion into floating-point Euler angles (in degrees). */ @@ -2318,7 +2318,7 @@ class ENGINE_API UKismetMathLibrary : public UBlueprintFunctionLibrary * * @param Tolerance Minimum squared length of quaternion for normalization. */ - UFUNCTION(BlueprintPure, meta = (DisplayName = "Normalize (Quat)", ScriptMethod = "Normalize"), Category = "Math|Quat") + UFUNCTION(BlueprintCallable, meta = (DisplayName = "Normalize (Quat)", ScriptMethod = "Normalize"), Category = "Math|Quat") static void Quat_Normalize(UPARAM(ref) FQuat& Q, float Tolerance = 1.e-4f); /** @@ -2347,8 +2347,17 @@ class ENGINE_API UKismetMathLibrary : public UBlueprintFunctionLibrary static FQuat Quat_Log(const FQuat& Q); /** Set X, Y, Z, W components of Quaternion. */ - UFUNCTION(BlueprintPure, meta = (DisplayName = "Set Components (Quat)", ScriptMethod = "SetComponents"), Category = "Math|Quat") - static void Quat_SetComponents(UPARAM(ref) FQuat& Q, float InX, float InY, float InZ, float InW); + UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set Components (Quat)", ScriptMethod = "SetComponents"), Category = "Math|Quat") + static void Quat_SetComponents(UPARAM(ref) FQuat& Q, float X, float Y, float Z, float W); + + /** + * Convert a vector of floating-point Euler angles (in degrees) into a Quaternion. + * + * @param Q Quaternion to update + * @param Euler the Euler angles + */ + UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set from Euler (Quat)", ScriptMethod = "SetFromEuler"), Category = "Math|Quat") + static void Quat_SetFromEuler(UPARAM(ref) FQuat& Q, const FVector& Euler); /** * Convert a vector of floating-point Euler angles (in degrees) into a Quaternion. @@ -2356,8 +2365,8 @@ class ENGINE_API UKismetMathLibrary : public UBlueprintFunctionLibrary * @param Euler the Euler angles * @return constructed Quat */ - UFUNCTION(BlueprintPure, meta = (DisplayName = "Set from Euler (Quat)", ScriptMethod = "SetFromEuler"), Category = "Math|Quat") - static void Quat_SetFromEuler(UPARAM(ref) FQuat& Q, const FVector& Euler); + UFUNCTION(BlueprintPure, meta = (DisplayName = "Make from Euler (Quat)"), Category = "Math|Quat") + static FQuat Quat_MakeFromEuler(const FVector& Euler); /** Convert to Rotator representation of this Quaternion. */ UFUNCTION(BlueprintPure, meta = (DisplayName = "ToRotator (Quat)", CompactNodeTitle = "->", ScriptMethod = "Rotator", Keywords = "cast convert", BlueprintAutocast), Category = "Math|Conversions") @@ -2448,37 +2457,37 @@ class ENGINE_API UKismetMathLibrary : public UBlueprintFunctionLibrary static void BreakColor(FLinearColor InColor, float& R, float& G, float& B, float& A); /** Assign contents of InColor */ - UFUNCTION(BlueprintPure, meta = (ScriptMethod = "Set"), Category = "Math|Color") + UFUNCTION(BlueprintCallable, meta = (ScriptMethod = "Set"), Category = "Math|Color") static void LinearColor_Set(UPARAM(ref) FLinearColor& InOutColor, FLinearColor InColor); /** Assign individual linear RGBA components. */ - UFUNCTION(BlueprintPure, meta = (ScriptMethod = "SetRGBA"), Category = "Math|Color") + UFUNCTION(BlueprintCallable, meta = (ScriptMethod = "SetRGBA"), Category = "Math|Color") static void LinearColor_SetRGBA(UPARAM(ref) FLinearColor& InOutColor, float R, float G, float B, float A = 1.0f); /** Assigns an HSV color to a linear space RGB color */ - UFUNCTION(BlueprintPure, meta = (ScriptMethod = "SetFromHSV"), Category = "Math|Color") + UFUNCTION(BlueprintCallable, meta = (ScriptMethod = "SetFromHSV"), Category = "Math|Color") static void LinearColor_SetFromHSV(UPARAM(ref) FLinearColor& InOutColor, float H, float S, float V, float A = 1.0f); /** * Assigns an FColor coming from an observed sRGB output, into a linear color. * @param InSRGB The sRGB color that needs to be converted into linear space. */ - UFUNCTION(BlueprintPure, meta = (ScriptMethod = "SetFromSRGB"), Category = "Math|Color") + UFUNCTION(BlueprintCallable, meta = (ScriptMethod = "SetFromSRGB"), Category = "Math|Color") static void LinearColor_SetFromSRGB(UPARAM(ref) FLinearColor& InOutColor, const FColor& InSRGB); /** * Assigns an FColor coming from an observed Pow(1/2.2) output, into a linear color. * @param InColor The Pow(1/2.2) color that needs to be converted into linear space. */ - UFUNCTION(BlueprintPure, meta = (ScriptMethod = "SetFromPow22"), Category = "Math|Color") + UFUNCTION(BlueprintCallable, meta = (ScriptMethod = "SetFromPow22"), Category = "Math|Color") static void LinearColor_SetFromPow22(UPARAM(ref) FLinearColor& InOutColor, const FColor& InColor); /** Converts temperature in Kelvins of a black body radiator to RGB chromaticity. */ - UFUNCTION(BlueprintPure, meta = (ScriptMethod = "SetTemperature"), Category = "Math|Color") + UFUNCTION(BlueprintCallable, meta = (ScriptMethod = "SetTemperature"), Category = "Math|Color") static void LinearColor_SetTemperature(UPARAM(ref) FLinearColor& InOutColor, float InTemperature); /** Sets to a random color. Choses a quite nice color based on a random hue. */ - UFUNCTION(BlueprintPure, meta = (ScriptMethod = "SetRandomHue"), Category = "Math|Color") + UFUNCTION(BlueprintCallable, meta = (ScriptMethod = "SetRandomHue"), Category = "Math|Color") static void LinearColor_SetRandomHue(UPARAM(ref) FLinearColor& InOutColor); /** Convert a float into a LinearColor, where each element is that float */ diff --git a/Engine/Source/Runtime/Engine/Classes/Kismet/KismetMathLibrary.inl b/Engine/Source/Runtime/Engine/Classes/Kismet/KismetMathLibrary.inl index af2a292af14f..95f27b7c1ac4 100644 --- a/Engine/Source/Runtime/Engine/Classes/Kismet/KismetMathLibrary.inl +++ b/Engine/Source/Runtime/Engine/Classes/Kismet/KismetMathLibrary.inl @@ -1046,9 +1046,9 @@ FVector2D UKismetMathLibrary::Negated2D(const FVector2D& A) } KISMET_MATH_FORCEINLINE -void UKismetMathLibrary::Set2D(FVector2D& A, float InX, float InY) +void UKismetMathLibrary::Set2D(FVector2D& A, float X, float Y) { - A.Set(InX, InY); + A.Set(X, Y); } KISMET_MATH_FORCEINLINE @@ -1254,9 +1254,9 @@ void UKismetMathLibrary::Vector_Assign(FVector& A, const FVector& InVector) } KISMET_MATH_FORCEINLINE -void UKismetMathLibrary::Vector_Set(FVector& A, float InX, float InY, float InZ) +void UKismetMathLibrary::Vector_Set(FVector& A, float X, float Y, float Z) { - A.Set(InX, InY, InZ); + A.Set(X, Y, Z); } KISMET_MATH_FORCEINLINE @@ -1920,9 +1920,9 @@ void UKismetMathLibrary::Vector4_Assign(FVector4& A, const FVector4& InVector) } KISMET_MATH_FORCEINLINE -void UKismetMathLibrary::Vector4_Set(FVector4& A, float InX, float InY, float InZ, float InW) +void UKismetMathLibrary::Vector4_Set(FVector4& A, float X, float Y, float Z, float W) { - A.Set(InX, InY, InZ, InW); + A.Set(X, Y, Z, W); } KISMET_MATH_FORCEINLINE @@ -2791,12 +2791,12 @@ FQuat UKismetMathLibrary::Quat_Log(const FQuat& Q) } KISMET_MATH_FORCEINLINE -void UKismetMathLibrary::Quat_SetComponents(UPARAM(ref) FQuat& Q, float InX, float InY, float InZ, float InW) +void UKismetMathLibrary::Quat_SetComponents(UPARAM(ref) FQuat& Q, float X, float Y, float Z, float W) { - Q.X = InX; - Q.Y = InY; - Q.Z = InZ; - Q.W = InW; + Q.X = X; + Q.Y = Y; + Q.Z = Z; + Q.W = W; } KISMET_MATH_FORCEINLINE @@ -2805,6 +2805,12 @@ void UKismetMathLibrary::Quat_SetFromEuler(UPARAM(ref) FQuat& Q, const FVector& Q = FQuat::MakeFromEuler(Euler); } +KISMET_MATH_FORCEINLINE +FQuat UKismetMathLibrary::Quat_MakeFromEuler(const FVector& Euler) +{ + return FQuat::MakeFromEuler(Euler); +} + KISMET_MATH_FORCEINLINE FRotator UKismetMathLibrary::Quat_Rotator(const FQuat& Q) { diff --git a/Engine/Source/Runtime/Engine/Classes/Materials/Material.h b/Engine/Source/Runtime/Engine/Classes/Materials/Material.h index 4ef86c51df88..68cf5402b6e1 100644 --- a/Engine/Source/Runtime/Engine/Classes/Materials/Material.h +++ b/Engine/Source/Runtime/Engine/Classes/Materials/Material.h @@ -1109,7 +1109,7 @@ private: void SetUsageByFlag(const EMaterialUsage Usage, const bool NewValue); /** Sets up transient properties in MaterialResources. */ - void UpdateResourceAllocations(); + void UpdateResourceAllocations(FMaterialResourceDeferredDeletionArray* ResourcesToFree = nullptr); /** to share code for PostLoad() and PostEditChangeProperty(), and UMaterialInstance::InitResources(), needs to be refactored */ void PropagateDataToMaterialProxy(); diff --git a/Engine/Source/Runtime/Engine/Classes/Materials/MaterialExpressionPreSkinnedLocalBounds.h b/Engine/Source/Runtime/Engine/Classes/Materials/MaterialExpressionPreSkinnedLocalBounds.h new file mode 100644 index 000000000000..2e751cefa760 --- /dev/null +++ b/Engine/Source/Runtime/Engine/Classes/Materials/MaterialExpressionPreSkinnedLocalBounds.h @@ -0,0 +1,21 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" +#include "Materials/MaterialExpression.h" +#include "MaterialExpressionPreSkinnedLocalBounds.generated.h" + +UCLASS(collapsecategories, hidecategories = Object) +class UMaterialExpressionPreSkinnedLocalBounds : public UMaterialExpression +{ + GENERATED_UCLASS_BODY() + + //~ Begin UMaterialExpression Interface +#if WITH_EDITOR + virtual int32 Compile(class FMaterialCompiler* Compiler, int32 OutputIndex) override; + virtual void GetCaption(TArray& OutCaptions) const override; +#endif + //~ End UMaterialExpression Interface +}; \ No newline at end of file diff --git a/Engine/Source/Runtime/Engine/Classes/Materials/MaterialExpressionSceneTexture.h b/Engine/Source/Runtime/Engine/Classes/Materials/MaterialExpressionSceneTexture.h index 3a3f9f44a816..3b1aa8e8ab42 100644 --- a/Engine/Source/Runtime/Engine/Classes/Materials/MaterialExpressionSceneTexture.h +++ b/Engine/Source/Runtime/Engine/Classes/Materials/MaterialExpressionSceneTexture.h @@ -5,74 +5,11 @@ #include "CoreMinimal.h" #include "UObject/ObjectMacros.h" +#include "MaterialSceneTextureId.h" #include "MaterialExpressionIO.h" #include "Materials/MaterialExpression.h" #include "MaterialExpressionSceneTexture.generated.h" -/** like EPassInputId but can expose more e.g. GBuffer */ -UENUM() -enum ESceneTextureId -{ - /** Scene color, normal post process passes should use PostProcessInput0 */ - PPI_SceneColor UMETA(DisplayName="SceneColor"), - /** Scene depth, single channel, contains the linear depth of the opaque objects */ - PPI_SceneDepth UMETA(DisplayName="SceneDepth"), - /** Material diffuse, RGB color (computed from GBuffer) */ - PPI_DiffuseColor UMETA(DisplayName="DiffuseColor"), - /** Material specular, RGB color (computed from GBuffer) */ - PPI_SpecularColor UMETA(DisplayName="SpecularColor"), - /** Material subsurface, RGB color (GBuffer, only for some ShadingModels) */ - PPI_SubsurfaceColor UMETA(DisplayName="SubsurfaceColor"), - /** Material base, RGB color (GBuffer), can be modified on read by the ShadingModel, consider StoredBasedColor */ - PPI_BaseColor UMETA(DisplayName="BaseColor (for lighting)"), - /** Material specular, single channel (GBuffer), can be modified on read by the ShadingModel, consider StoredSpecular */ - PPI_Specular UMETA(DisplayName="Specular (for lighting)"), - /** Material metallic, single channel (GBuffer) */ - PPI_Metallic UMETA(DisplayName="Metallic"), - /** Normal, RGB in -1..1 range, not normalized (GBuffer) */ - PPI_WorldNormal UMETA(DisplayName="WorldNormal"), - /** Not yet supported */ - PPI_SeparateTranslucency UMETA(DisplayName="SeparateTranslucency"), - /** Material opacity, single channel (GBuffer) */ - PPI_Opacity UMETA(DisplayName="Opacity"), - /** Material roughness, single channel (GBuffer) */ - PPI_Roughness UMETA(DisplayName="Roughness"), - /** Material ambient occlusion, single channel (GBuffer) */ - PPI_MaterialAO UMETA(DisplayName="MaterialAO"), - /** Scene depth, single channel, contains the linear depth of the opaque objects rendered with CustomDepth (mesh property) */ - PPI_CustomDepth UMETA(DisplayName="CustomDepth"), - /** Input #0 of this postprocess pass, usually the only one hooked up */ - PPI_PostProcessInput0 UMETA(DisplayName="PostProcessInput0"), - /** Input #1 of this postprocess pass, usually not used */ - PPI_PostProcessInput1 UMETA(DisplayName="PostProcessInput1"), - /** Input #2 of this postprocess pass, usually not used */ - PPI_PostProcessInput2 UMETA(DisplayName="PostProcessInput2"), - /** Input #3 of this postprocess pass, usually not used */ - PPI_PostProcessInput3 UMETA(DisplayName="PostProcessInput3"), - /** Input #4 of this postprocess pass, usually not used */ - PPI_PostProcessInput4 UMETA(DisplayName="PostProcessInput4"), - /** Input #5 of this postprocess pass, usually not used */ - PPI_PostProcessInput5 UMETA(DisplayName="PostProcessInput5"), - /** Input #6 of this postprocess pass, usually not used */ - PPI_PostProcessInput6 UMETA(DisplayName="PostProcessInput6"), - /** Decal Mask, single bit (was moved to stencil for better performance, not accessible at the moment) */ - PPI_DecalMask UMETA(DisplayName="Decal Mask"), - /** Shading model */ - PPI_ShadingModelColor UMETA(DisplayName="Shading Model Color"), - /** Shading model ID */ - PPI_ShadingModelID UMETA(DisplayName="Shading Model ID"), - /** Ambient Occlusion, single channel */ - PPI_AmbientOcclusion UMETA(DisplayName="Ambient Occlusion"), - /** Scene stencil, contains CustomStencil mesh property of the opaque objects rendered with CustomDepth */ - PPI_CustomStencil UMETA(DisplayName="CustomStencil"), - /** Material base, RGB color (GBuffer) */ - PPI_StoredBaseColor UMETA(DisplayName="BaseColor (as stored in GBuffer)"), - /** Material specular, single channel (GBuffer) */ - PPI_StoredSpecular UMETA(DisplayName="Specular (as stored in GBuffer)"), - /** Scene Velocity */ - PPI_Velocity UMETA(DisplayName="Velocity"), -}; - UCLASS(collapsecategories, hidecategories=Object) class UMaterialExpressionSceneTexture : public UMaterialExpression { diff --git a/Engine/Source/Runtime/Engine/Classes/Materials/MaterialInstance.h b/Engine/Source/Runtime/Engine/Classes/Materials/MaterialInstance.h index 86dd84125c2f..110b560a5788 100644 --- a/Engine/Source/Runtime/Engine/Classes/Materials/MaterialInstance.h +++ b/Engine/Source/Runtime/Engine/Classes/Materials/MaterialInstance.h @@ -635,7 +635,7 @@ protected: * This is a helper used when recompiling MI's with static parameters. * Assumes that the rendering thread command queue has been flushed by the caller. */ - void UpdatePermutationAllocations(); + void UpdatePermutationAllocations(FMaterialResourceDeferredDeletionArray* ResourcesToFree = nullptr); #if WITH_EDITOR /** diff --git a/Engine/Source/Runtime/Engine/Classes/Materials/MaterialInstanceConstant.h b/Engine/Source/Runtime/Engine/Classes/Materials/MaterialInstanceConstant.h index 4899df673b5b..a8fb97f4fcf9 100644 --- a/Engine/Source/Runtime/Engine/Classes/Materials/MaterialInstanceConstant.h +++ b/Engine/Source/Runtime/Engine/Classes/Materials/MaterialInstanceConstant.h @@ -52,8 +52,9 @@ class UMaterialInstanceConstant : public UMaterialInstance * Set the parent of this material instance. This function may only be called in the Editor! * WARNING: You MUST call PostEditChange afterwards to propagate changes to other materials in the chain! * @param NewParent - The new parent for this material instance. + * @param RecacheShader - Will recache required shaders. */ - ENGINE_API void SetParentEditorOnly(class UMaterialInterface* NewParent); + ENGINE_API void SetParentEditorOnly(class UMaterialInterface* NewParent, bool RecacheShader = true); /** * Copies the uniform parameters (scalar, vector and texture) from a material or instance hierarchy. diff --git a/Engine/Source/Runtime/Engine/Classes/Materials/MaterialInterface.h b/Engine/Source/Runtime/Engine/Classes/Materials/MaterialInterface.h index b0064ba59ea0..9778ff7f611f 100644 --- a/Engine/Source/Runtime/Engine/Classes/Materials/MaterialInterface.h +++ b/Engine/Source/Runtime/Engine/Classes/Materials/MaterialInterface.h @@ -33,6 +33,8 @@ struct FPrimitiveViewRelevance; struct FMaterialParameterInfo; struct FMaterialResourceLocOnDisk; +typedef TArray FMaterialResourceDeferredDeletionArray; + UENUM(BlueprintType) enum EMaterialUsage { diff --git a/Engine/Source/Runtime/Engine/Classes/Sound/DialogueSoundWaveProxy.h b/Engine/Source/Runtime/Engine/Classes/Sound/DialogueSoundWaveProxy.h index 571518df5126..8b18651abd63 100644 --- a/Engine/Source/Runtime/Engine/Classes/Sound/DialogueSoundWaveProxy.h +++ b/Engine/Source/Runtime/Engine/Classes/Sound/DialogueSoundWaveProxy.h @@ -20,7 +20,6 @@ class UDialogueSoundWaveProxy : public USoundBase USoundWave* SoundWave; TArray Subtitles; - FWaveInstance* CurrentWaveInstance; public: /** Returns whether the sound base is set up in a playable manner */ diff --git a/Engine/Source/Runtime/Engine/Private/Actor.cpp b/Engine/Source/Runtime/Engine/Private/Actor.cpp index 7725b8f2da70..b4729a93e845 100644 --- a/Engine/Source/Runtime/Engine/Private/Actor.cpp +++ b/Engine/Source/Runtime/Engine/Private/Actor.cpp @@ -3226,24 +3226,25 @@ void AActor::SetReplicates(bool bInReplicates) { if (Role == ROLE_Authority) { - // Only call into net driver if we actually changed - if (bReplicates != bInReplicates) - { - // Update our settings before calling into net driver - RemoteRole = bInReplicates ? ROLE_SimulatedProxy : ROLE_None; - bReplicates = bInReplicates; + const bool bNewlyReplicates = (bReplicates == false && bInReplicates == true); + + // Due to SetRemoteRoleForBackwardsCompat, it's possible that bReplicates is false, but RemoteRole is something + // other than ROLE_None. + // So, we'll always set RemoteRole here regardless of whether or not bReplicates would change, to fix up that + // case. + RemoteRole = bInReplicates ? ROLE_SimulatedProxy : ROLE_None; + bReplicates = bInReplicates; - // This actor should already be in the Network Actors List if it was already replicating. - if (bReplicates) + // Only call into net driver if we just started replicating changed + // This actor should already be in the Network Actors List if it was already replicating. + if (bNewlyReplicates) + { + // GetWorld will return nullptr on CDO, FYI + if (UWorld* MyWorld = GetWorld()) { - // GetWorld will return nullptr on CDO, FYI - if (UWorld* MyWorld = GetWorld()) - { - MyWorld->AddNetworkActor(this); - ForcePropertyCompare(); - } + MyWorld->AddNetworkActor(this); + ForcePropertyCompare(); } - } } else diff --git a/Engine/Source/Runtime/Engine/Private/ActorConstruction.cpp b/Engine/Source/Runtime/Engine/Private/ActorConstruction.cpp index 19b0cf17b2e4..e7941175efe9 100644 --- a/Engine/Source/Runtime/Engine/Private/ActorConstruction.cpp +++ b/Engine/Source/Runtime/Engine/Private/ActorConstruction.cpp @@ -1065,7 +1065,7 @@ UActorComponent* AActor::AddComponent(FName TemplateName, bool bManualAttachment TemplateData = BPGC->CookedComponentInstancingData.Find(TemplateName); } - if (!TemplateData || !TemplateData->bIsValid + if (!TemplateData || !TemplateData->bHasValidCookedData || !ensureMsgf(TemplateData->ComponentTemplateClass != nullptr, TEXT("AddComponent fast path (%s.%s): Cooked data is valid, but runtime support data is not initialized. Using the slow path instead."), *BPGC->GetName(), *TemplateName.ToString())) { Template = BPGC->FindComponentTemplateByName(TemplateName); diff --git a/Engine/Source/Runtime/Engine/Private/Animation/AnimCurveCompressionSettings.cpp b/Engine/Source/Runtime/Engine/Private/Animation/AnimCurveCompressionSettings.cpp index 192388cca709..c80be02478bf 100644 --- a/Engine/Source/Runtime/Engine/Private/Animation/AnimCurveCompressionSettings.cpp +++ b/Engine/Source/Runtime/Engine/Private/Animation/AnimCurveCompressionSettings.cpp @@ -8,10 +8,13 @@ UAnimCurveCompressionSettings::UAnimCurveCompressionSettings(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { -#if WITH_EDITORONLY_DATA Codec = CreateDefaultSubobject(TEXT("CurveCompressionCodec")); Codec->SetFlags(RF_Transactional); -#endif +} + +UAnimCurveCompressionCodec* UAnimCurveCompressionSettings::GetCodec(const FString& Path) +{ + return Codec->GetCodec(Path); } #if WITH_EDITORONLY_DATA @@ -38,11 +41,6 @@ bool UAnimCurveCompressionSettings::Compress(UAnimSequence& AnimSeq) const return Success; } -UAnimCurveCompressionCodec* UAnimCurveCompressionSettings::GetCodec(const FString& Path) -{ - return Codec->GetCodec(Path); -} - FString UAnimCurveCompressionSettings::MakeDDCKey() const { if (Codec == nullptr) diff --git a/Engine/Source/Runtime/Engine/Private/Animation/AnimInstanceProxy.cpp b/Engine/Source/Runtime/Engine/Private/Animation/AnimInstanceProxy.cpp index 5e43a160810b..468ba2c38b8a 100644 --- a/Engine/Source/Runtime/Engine/Private/Animation/AnimInstanceProxy.cpp +++ b/Engine/Source/Runtime/Engine/Private/Animation/AnimInstanceProxy.cpp @@ -486,17 +486,20 @@ void FAnimInstanceProxy::InitializeObjects(UAnimInstance* InAnimInstance) NumUroSkippedFrames_Eval = 0; if(RateParams) { - bool bDoUro = SkeletalMeshComponent->ShouldUseUpdateRateOptimizations(); - bool bDoEvalOptimization = bDoUro && RateParams->DoEvaluationRateOptimizations(); - + const bool bDoUro = SkeletalMeshComponent->ShouldUseUpdateRateOptimizations(); if(bDoUro) { NumUroSkippedFrames_Update = RateParams->UpdateRate - 1; - } - if(bDoEvalOptimization) + const bool bDoEvalOptimization = RateParams->DoEvaluationRateOptimizations(); + if(bDoEvalOptimization) + { + NumUroSkippedFrames_Eval = RateParams->EvaluationRate - 1; + } + } + else if(SkeletalMeshComponent->IsUsingExternalTickRateControl()) { - NumUroSkippedFrames_Eval = RateParams->EvaluationRate - 1; + NumUroSkippedFrames_Update = NumUroSkippedFrames_Eval = SkeletalMeshComponent->GetExternalTickRate(); } } diff --git a/Engine/Source/Runtime/Engine/Private/Animation/AnimSequence.cpp b/Engine/Source/Runtime/Engine/Private/Animation/AnimSequence.cpp index fd952d38dc8e..307bccaeebd9 100644 --- a/Engine/Source/Runtime/Engine/Private/Animation/AnimSequence.cpp +++ b/Engine/Source/Runtime/Engine/Private/Animation/AnimSequence.cpp @@ -303,8 +303,9 @@ UAnimSequence::UAnimSequence(const FObjectInitializer& ObjectInitializer) ImportResampleFramerate = 0; bAllowFrameStripping = true; - InitCurveCompressionScheme(); #endif + + InitCurveCompressionScheme(); } void UAnimSequence::PostInitProperties() @@ -402,12 +403,16 @@ static void LoadOldCompressedTrack(FArchive& Ar, FCompressedTrack& Dst, int32 By Ar << Dst.Ranges[0] << Dst.Ranges[1] << Dst.Ranges[2]; } -#if WITH_EDITORONLY_DATA void UAnimSequence::InitCurveCompressionScheme() { // Do this is serialize as if the default animation curve compression asset isn't loaded it will // fire a warning if we try and load it in post load - if ((CurveCompressionSettings == nullptr || !CurveCompressionSettings->AreSettingsValid()) + + bool bCurveCompressionSettingsValid = CurveCompressionSettings != nullptr; +#if WITH_EDITOR + bCurveCompressionSettingsValid = bCurveCompressionSettingsValid && CurveCompressionSettings->AreSettingsValid(); +#endif + if (!bCurveCompressionSettingsValid #if WITH_HOT_RELOAD && (GetClass()->HasAnyClassFlags(CLASS_CompiledFromBlueprint) || !HasAnyFlags(RF_ClassDefaultObject) || !GIsHotReload) // Don't do this to native CDOs during Hot-Reload #endif @@ -416,7 +421,6 @@ void UAnimSequence::InitCurveCompressionScheme() CurveCompressionSettings = FAnimationUtils::GetDefaultAnimationCurveCompressionSettings(); } } -#endif void UAnimSequence::Serialize(FArchive& Ar) { @@ -2729,23 +2733,10 @@ void UAnimSequence::SerializeCompressedData(FArchive& Ar, bool bDDCData) } } -#if WITH_EDITOR - if (bDDCData) - { - FString CurveCodecPath; - Ar << CurveCodecPath; + FString CurveCodecPath; + Ar << CurveCodecPath; - CurveCompressionCodec = CurveCompressionSettings->GetCodec(CurveCodecPath); - } - else -#else - check(!bDDCData); -#endif - { - UAnimCurveCompressionCodec* CurveCodec = nullptr; - Ar << CurveCodec; - CurveCompressionCodec = CurveCodec; - } + CurveCompressionCodec = CurveCompressionSettings->GetCodec(CurveCodecPath); int32 NumCurveBytes; Ar << NumCurveBytes; @@ -2863,19 +2854,8 @@ void UAnimSequence::SerializeCompressedData(FArchive& Ar, bool bDDCData) CompressedTrackOffsets = SavedCompressedTrackOffsets; CompressedScaleOffsets = SavedCompressedScaleOffsets; -#if WITH_EDITOR - if (bDDCData) - { - FString CurveCodecPath = CurveCompressionCodec->GetPathName(); - Ar << CurveCodecPath; - } - else -#else - check(!bDDCData); -#endif - { - Ar << CurveCompressionCodec; - } + FString CurveCodecPath = CurveCompressionCodec->GetPathName(); + Ar << CurveCodecPath; int32 NumCurveBytes = CompressedCurveByteStream.Num(); Ar << NumCurveBytes; diff --git a/Engine/Source/Runtime/Engine/Private/Animation/AnimationUtils.cpp b/Engine/Source/Runtime/Engine/Private/Animation/AnimationUtils.cpp index caf8812bb6fe..1412f94ed2ea 100644 --- a/Engine/Source/Runtime/Engine/Private/Animation/AnimationUtils.cpp +++ b/Engine/Source/Runtime/Engine/Private/Animation/AnimationUtils.cpp @@ -2237,7 +2237,6 @@ void FAnimationUtils::TallyErrorsFromPerturbation( } } -#if WITH_EDITOR static UAnimCurveCompressionSettings* DefaultCurveCompressionSettings = nullptr; UAnimCurveCompressionSettings* FAnimationUtils::GetDefaultAnimationCurveCompressionSettings() @@ -2297,6 +2296,8 @@ UAnimCurveCompressionSettings* FAnimationUtils::GetDefaultAnimationCurveCompress return DefaultCurveCompressionSettings; } +#if WITH_EDITOR + bool FAnimationUtils::CompressAnimCurves(UAnimSequence& AnimSeq) { // Clear any previous data we might have even if we end up failing to compress diff --git a/Engine/Source/Runtime/Engine/Private/AudioDeviceManager.cpp b/Engine/Source/Runtime/Engine/Private/AudioDeviceManager.cpp index 66a7afc3f49c..e67ce1b18d65 100644 --- a/Engine/Source/Runtime/Engine/Private/AudioDeviceManager.cpp +++ b/Engine/Source/Runtime/Engine/Private/AudioDeviceManager.cpp @@ -143,7 +143,9 @@ void FAudioDeviceManager::ToggleAudioMixer() // To transfer mix states, we need to re-base the absolute clocks on the mix states // so the target audio device timing won't result in the mixes suddenly stopping. - TMap MixModifiers = AudioDevice->GetSoundMixModifiers(); + TMap MixModifiers = AudioDevice->GetSoundMixModifiers(); + TArray PrevPassiveSoundMixModifiers = AudioDevice->GetPrevPassiveSoundMixModifiers(); + USoundMix* BaseSoundMix = AudioDevice->GetDefaultBaseSoundMixModifier(); double AudioClock = AudioDevice->GetAudioClock(); for (TPair& SoundMixPair : MixModifiers) @@ -181,8 +183,7 @@ void FAudioDeviceManager::ToggleAudioMixer() } // Transfer the sound mix modifiers to the new audio engine - AudioDevice->SetSoundMixModifiers(MixModifiers); - + AudioDevice->SetSoundMixModifiers(MixModifiers, PrevPassiveSoundMixModifiers, BaseSoundMix); // Setup the mute state of the audio device to be the same that it was if (bIsActive) { diff --git a/Engine/Source/Runtime/Engine/Private/Blueprint.cpp b/Engine/Source/Runtime/Engine/Private/Blueprint.cpp index 1a1cc028df92..73782924118f 100644 --- a/Engine/Source/Runtime/Engine/Private/Blueprint.cpp +++ b/Engine/Source/Runtime/Engine/Private/Blueprint.cpp @@ -598,6 +598,11 @@ void UBlueprint::PostDuplicate(bool bDuplicateForPIE) { FBlueprintEditorUtils::PostDuplicateBlueprint(this, bDuplicateForPIE); } + + if (GeneratedClass) + { + GeneratedClass->GetDefaultObject()->PostDuplicate(bDuplicateForPIE); + } } extern COREUOBJECT_API bool GBlueprintUseCompilationManager; @@ -1418,7 +1423,7 @@ void UBlueprint::BeginCacheForCookedPlatformData(const ITargetPlatform *TargetPl for (auto RecordIt = TargetInheritableComponentHandler->CreateRecordIterator(); RecordIt; ++RecordIt) { // Only generate cooked data if the target platform supports the template class type. Cooked data may already have been generated if the component was inherited from a nativized parent class. - if (!RecordIt->CookedComponentInstancingData.bIsValid && ShouldCookBlueprintComponentTemplate(RecordIt->ComponentTemplate)) + if (!RecordIt->CookedComponentInstancingData.bHasValidCookedData && ShouldCookBlueprintComponentTemplate(RecordIt->ComponentTemplate)) { // Note: This will currently block until finished. // @TODO - Make this an async task so we can potentially cook instancing data for multiple components in parallel. diff --git a/Engine/Source/Runtime/Engine/Private/BlueprintGeneratedClass.cpp b/Engine/Source/Runtime/Engine/Private/BlueprintGeneratedClass.cpp index e4dc8e90f2f3..a2e4ce10ac86 100644 --- a/Engine/Source/Runtime/Engine/Private/BlueprintGeneratedClass.cpp +++ b/Engine/Source/Runtime/Engine/Private/BlueprintGeneratedClass.cpp @@ -316,16 +316,35 @@ void UBlueprintGeneratedClass::SerializeDefaultObject(UObject* Object, FArchive& // @TODO - Potentially make this serializable (or cooked data) to eliminate the slight load time cost we'll incur below to generate this list in a cooked build. For now, it's not serialized since the raw UProperty references cannot be saved out. UpdateCustomPropertyListForPostConstruction(); + const FString BPGCName = GetName(); + auto BuildCachedPropertyDataLambda = [BPGCName](FBlueprintCookedComponentInstancingData& CookedData, UActorComponent* SourceTemplate, FString CompVarName) + { + if (CookedData.bHasValidCookedData) + { + // This feature requires EDL at cook time, so ensure that the source template is also fully loaded at this point. + if (SourceTemplate != nullptr + && ensure(!SourceTemplate->HasAnyFlags(RF_NeedLoad))) + { + CookedData.BuildCachedPropertyDataFromTemplate(SourceTemplate); + } + else + { + // This situation is unexpected; templates that are filtered out by context should not be generating fast path data at cook time. Emit a warning about this. + UE_LOG(LogBlueprint, Warning, TEXT("BPComp fast path (%s.%s) : Invalid source template. Will use slow path for dynamic instancing."), *BPGCName, *CompVarName); + + // Invalidate the cooked data so that we fall back to using the slow path when dynamically instancing this node. + CookedData.bHasValidCookedData = false; + } + } + }; + // Generate "fast path" instancing data for inherited SCS node templates. This data may also be used to support inherited SCS component default value overrides // in a nativized, cooked build, in which this Blueprint class inherits from a nativized Blueprint parent. See CheckAndApplyComponentTemplateOverrides() below. if (InheritableComponentHandler && (bHasCookedComponentInstancingData || bHasNativizedParent)) { for (auto RecordIt = InheritableComponentHandler->CreateRecordIterator(); RecordIt; ++RecordIt) { - if (RecordIt->ComponentTemplate && RecordIt->CookedComponentInstancingData.bIsValid) - { - RecordIt->CookedComponentInstancingData.BuildCachedPropertyDataFromTemplate(RecordIt->ComponentTemplate); - } + BuildCachedPropertyDataLambda(RecordIt->CookedComponentInstancingData, RecordIt->ComponentTemplate, RecordIt->ComponentKey.GetSCSVariableName().ToString()); } } @@ -337,10 +356,7 @@ void UBlueprintGeneratedClass::SerializeDefaultObject(UObject* Object, FArchive& const TArray& AllSCSNodes = SimpleConstructionScript->GetAllNodes(); for (USCS_Node* SCSNode : AllSCSNodes) { - if (SCSNode->ComponentTemplate && SCSNode->CookedComponentInstancingData.bIsValid) - { - SCSNode->CookedComponentInstancingData.BuildCachedPropertyDataFromTemplate(SCSNode->ComponentTemplate); - } + BuildCachedPropertyDataLambda(SCSNode->CookedComponentInstancingData, SCSNode->ComponentTemplate, SCSNode->GetVariableName().ToString()); } } @@ -351,12 +367,10 @@ void UBlueprintGeneratedClass::SerializeDefaultObject(UObject* Object, FArchive& { if (ComponentTemplate) { - if (FBlueprintCookedComponentInstancingData* ComponentInstancingData = CookedComponentInstancingData.Find(ComponentTemplate->GetFName())) + FBlueprintCookedComponentInstancingData* ComponentInstancingData = CookedComponentInstancingData.Find(ComponentTemplate->GetFName()); + if (ComponentInstancingData != nullptr) { - if (ComponentInstancingData->bIsValid) - { - ComponentInstancingData->BuildCachedPropertyDataFromTemplate(ComponentTemplate); - } + BuildCachedPropertyDataLambda(*ComponentInstancingData, ComponentTemplate, ComponentTemplate->GetName()); } } } @@ -1186,7 +1200,7 @@ void UBlueprintGeneratedClass::CheckAndApplyComponentTemplateOverrides(UObject* if (ComponentKey.IsValid() && ComponentKey.IsSCSKey()) { const FBlueprintCookedComponentInstancingData* OverrideData = ICH->GetOverridenComponentTemplateData(ComponentKey); - if (OverrideData != nullptr && OverrideData->bIsValid) + if (OverrideData != nullptr && OverrideData->bHasValidCookedData) { // This is the instance of the inherited component subobject that's owned by the given class default object if (UObject* NativizedComponentSubobjectInstance = InClassDefaultObject->GetDefaultSubobjectByName(NativizedComponentSubobjectName)) @@ -1397,6 +1411,13 @@ void UBlueprintGeneratedClass::GetDefaultObjectPreloadDependencies(TArray& AllSCSNodes = CurrentBPClass->SimpleConstructionScript->GetAllNodes(); for (USCS_Node* SCSNode : AllSCSNodes) { + // An SCS node that's owned by this class must also be considered a preload dependency since we will access its serialized template reference property. Any SCS + // nodes that are inherited from a parent class will reference templates through the ICH instead, and that's already a preload dependency on the BP class itself. + if (CurrentBPClass == this) + { + OutDeps.Add(SCSNode); + } + OutDeps.Add(SCSNode->GetActualComponentTemplate(this)); } } @@ -1755,46 +1776,31 @@ void FBlueprintCookedComponentInstancingData::BuildCachedPropertyDataFromTemplat } }; - if (bIsValid) + checkSlow(bHasValidCookedData); + checkSlow(SourceTemplate != nullptr); + checkSlow(!SourceTemplate->HasAnyFlags(RF_NeedLoad)); + + // Cache source template attributes needed for instancing. + ComponentTemplateName = SourceTemplate->GetFName(); + ComponentTemplateClass = SourceTemplate->GetClass(); + ComponentTemplateFlags = SourceTemplate->GetFlags(); + + // This will also load the cached property list, if necessary. + const FCustomPropertyListNode* PropertyList = GetCachedPropertyList(); + + // Make sure we don't have any previously-built data. + if (!ensure(CachedPropertyData.Num() == 0)) { - if (SourceTemplate) - { - // Make sure the source template has been loaded. - if (SourceTemplate->HasAnyFlags(RF_NeedLoad)) - { - if (FLinkerLoad* Linker = SourceTemplate->GetLinker()) - { - Linker->Preload(SourceTemplate); - } - } + DEC_MEMORY_STAT_BY(STAT_BPCompInstancingFastPathMemory, CachedPropertyData.GetAllocatedSize()); - // Cache source template attributes needed for instancing. - ComponentTemplateName = SourceTemplate->GetFName(); - ComponentTemplateClass = SourceTemplate->GetClass(); - ComponentTemplateFlags = SourceTemplate->GetFlags(); - - // This will also load the cached property list, if necessary. - const FCustomPropertyListNode* PropertyList = GetCachedPropertyList(); - - // Make sure we don't have any previously-built data. - if (!ensure(CachedPropertyData.Num() == 0)) - { - DEC_MEMORY_STAT_BY(STAT_BPCompInstancingFastPathMemory, CachedPropertyData.GetAllocatedSize()); - - CachedPropertyData.Empty(); - } - - // Write template data out to the "fast path" buffer. All dependencies will be loaded at this point. - FBlueprintComponentInstanceDataWriter InstanceDataWriter(CachedPropertyData, PropertyList); - SourceTemplate->Serialize(InstanceDataWriter); - - INC_MEMORY_STAT_BY(STAT_BPCompInstancingFastPathMemory, CachedPropertyData.GetAllocatedSize()); - } - else - { - bIsValid = false; - } + CachedPropertyData.Empty(); } + + // Write template data out to the "fast path" buffer. All dependencies will be loaded at this point. + FBlueprintComponentInstanceDataWriter InstanceDataWriter(CachedPropertyData, PropertyList); + SourceTemplate->Serialize(InstanceDataWriter); + + INC_MEMORY_STAT_BY(STAT_BPCompInstancingFastPathMemory, CachedPropertyData.GetAllocatedSize()); } bool UBlueprintGeneratedClass::ArePropertyGuidsAvailable() const diff --git a/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp b/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp index 3240ac4c234f..a5b8eb5695dc 100644 --- a/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp +++ b/Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp @@ -196,7 +196,7 @@ namespace CharacterMovementCVars TEXT( "If 1, remove invalid replay samples that can occur due to oversampling (sampling at higher rate than physics is being ticked)" ), ECVF_Default); - static int32 ForceJumpPeakSubstep = 0; + static int32 ForceJumpPeakSubstep = 1; FAutoConsoleVariableRef CVarForceJumpPeakSubstep( TEXT("p.ForceJumpPeakSubstep"), ForceJumpPeakSubstep, @@ -404,6 +404,8 @@ UCharacterMovementComponent::UCharacterMovementComponent(const FObjectInitialize MaxSimulationTimeStep = 0.05f; MaxSimulationIterations = 8; + MaxJumpApexAttemptsPerSimulation = 2; + NumJumpApexAttempts = 0; MaxDepenetrationWithGeometry = 500.f; MaxDepenetrationWithGeometryAsProxy = 100.f; @@ -498,6 +500,7 @@ UCharacterMovementComponent::UCharacterMovementComponent(const FObjectInitialize bImpartBaseVelocityZ = true; bImpartBaseAngularVelocity = true; bIgnoreClientMovementErrorChecksAndCorrection = false; + bServerAcceptClientAuthoritativePosition = false; bAlwaysCheckFloor = true; // default character can jump, walk, and swim @@ -1635,6 +1638,7 @@ void UCharacterMovementComponent::SimulateRootMotion(float DeltaSeconds, const F bNetworkMovementModeChanged = false; } + NumJumpApexAttempts = 0; StartNewPhysics(DeltaSeconds, 0); // fixme laurent - simulate movement seems to have step up issues? investigate as that would be cheaper to use. // SimulateMovement(DeltaSeconds); @@ -2355,6 +2359,7 @@ void UCharacterMovementComponent::PerformMovement(float DeltaSeconds) // Clear jump input now, to allow movement events to trigger it for next update. CharacterOwner->ClearJumpInput(DeltaSeconds); + NumJumpApexAttempts = 0; // change position StartNewPhysics(DeltaSeconds, 0); @@ -4115,7 +4120,6 @@ void UCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iterations) FVector FallAcceleration = GetFallingLateralAcceleration(deltaTime); FallAcceleration.Z = 0.f; const bool bHasAirControl = (FallAcceleration.SizeSquared2D() > 0.f); - int32 NumApexAttempts = 0; float remainingTime = deltaTime; while( (remainingTime >= MIN_TICK_TIME) && (Iterations < MaxSimulationIterations) ) @@ -4191,7 +4195,7 @@ void UCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iterations) const FVector AirControlAccel = (Velocity - VelocityNoAirControl) / timeTick; // See if we need to sub-step to exactly reach the apex. This is important for avoiding "cutting off the top" of the trajectory as framerate varies. - if (CharacterMovementCVars::ForceJumpPeakSubstep && OldVelocity.Z > 0.f && Velocity.Z <= 0.f && NumApexAttempts < 2) + if (CharacterMovementCVars::ForceJumpPeakSubstep && OldVelocity.Z > 0.f && Velocity.Z <= 0.f && NumJumpApexAttempts < MaxJumpApexAttemptsPerSimulation) { const FVector DerivedAccel = (Velocity - OldVelocity) / timeTick; if (!FMath::IsNearlyZero(DerivedAccel.Z)) @@ -4210,7 +4214,7 @@ void UCharacterMovementComponent::PhysFalling(float deltaTime, int32 Iterations) remainingTime += (timeTick - TimeToApex); timeTick = TimeToApex; Iterations--; - NumApexAttempts++; + NumJumpApexAttempts++; } } } @@ -7651,17 +7655,6 @@ bool UCharacterMovementComponent::ClientUpdatePositionAfterServerUpdate() return false; } - if (bIgnoreClientMovementErrorChecksAndCorrection) - { -#if !UE_BUILD_SHIPPING - if (CharacterMovementCVars::NetShowCorrections != 0) - { - UE_LOG(LogNetPlayerMovement, Warning, TEXT("*** Client: %s is set to ignore error checks and corrections with %d saved moves in queue."), *GetNameSafe(CharacterOwner), ClientData->SavedMoves.Num()); - } -#endif // !UE_BUILD_SHIPPING - return false; - } - ClientData->bUpdatePosition = false; // Don't do any network position updates on things running PHYS_RigidBody @@ -8753,8 +8746,7 @@ void UCharacterMovementComponent::ServerMoveHandleClientError(float ClientTimeSt } else { - const AGameNetworkManager* GameNetworkManager = (const AGameNetworkManager*)(AGameNetworkManager::StaticClass()->GetDefaultObject()); - if (GameNetworkManager->ClientAuthorativePosition) + if (ServerShouldUseAuthoritativePosition(ClientTimeStamp, DeltaTime, Accel, ClientLoc, RelativeClientLoc, ClientMovementBase, ClientBaseBoneName, ClientMovementMode)) { const FVector LocDiff = UpdatedComponent->GetComponentLocation() - ClientLoc; //-V595 if (!LocDiff.IsZero() || ClientMovementMode != PackNetworkMovementMode() || GetMovementBase() != ClientMovementBase || (CharacterOwner && CharacterOwner->GetBasedMovement().BoneName != ClientBaseBoneName)) @@ -8805,12 +8797,21 @@ bool UCharacterMovementComponent::ServerCheckClientError(float ClientTimeStamp, RootMotionSourceDebug::PrintOnScreen(*CharacterOwner, AdjustedDebugString); } #endif + const AGameNetworkManager* GameNetworkManager = (const AGameNetworkManager*)(AGameNetworkManager::StaticClass()->GetDefaultObject()); if (GameNetworkManager->ExceedsAllowablePositionError(LocDiff)) { bNetworkLargeClientCorrection = (LocDiff.SizeSquared() > FMath::Square(NetworkLargeClientCorrectionDistance)); return true; } + + // Check for disagreement in movement mode + const uint8 CurrentPackedMovementMode = PackNetworkMovementMode(); + if (CurrentPackedMovementMode != ClientMovementMode) + { + return true; + } + #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (CharacterMovementCVars::NetForceClientAdjustmentPercent > SMALL_NUMBER) { @@ -8832,9 +8833,19 @@ bool UCharacterMovementComponent::ServerCheckClientError(float ClientTimeStamp, #endif // !UE_BUILD_SHIPPING } - // Check for disagreement in movement mode - const uint8 CurrentPackedMovementMode = PackNetworkMovementMode(); - if (CurrentPackedMovementMode != ClientMovementMode) + return false; +} + + +bool UCharacterMovementComponent::ServerShouldUseAuthoritativePosition(float ClientTimeStamp, float DeltaTime, const FVector& Accel, const FVector& ClientWorldLocation, const FVector& RelativeClientLocation, UPrimitiveComponent* ClientMovementBase, FName ClientBaseBoneName, uint8 ClientMovementMode) +{ + if (bServerAcceptClientAuthoritativePosition) + { + return true; + } + + const AGameNetworkManager* GameNetworkManager = (const AGameNetworkManager*)(AGameNetworkManager::StaticClass()->GetDefaultObject()); + if (GameNetworkManager->ClientAuthorativePosition) { return true; } @@ -8842,7 +8853,6 @@ bool UCharacterMovementComponent::ServerCheckClientError(float ClientTimeStamp, return false; } - bool UCharacterMovementComponent::ServerMove_Validate(float TimeStamp, FVector_NetQuantize10 InAccel, FVector_NetQuantize100 ClientLoc, uint8 MoveFlags, uint8 ClientRoll, uint32 View, UPrimitiveComponent* ClientMovementBase, FName ClientBaseBoneName, uint8 ClientMovementMode) { return true; diff --git a/Engine/Source/Runtime/Engine/Private/Components/RectLightComponent.cpp b/Engine/Source/Runtime/Engine/Private/Components/RectLightComponent.cpp index 56a683cbdfd7..bfd67b989b6c 100644 --- a/Engine/Source/Runtime/Engine/Private/Components/RectLightComponent.cpp +++ b/Engine/Source/Runtime/Engine/Private/Components/RectLightComponent.cpp @@ -25,6 +25,7 @@ float GetRectLightBarnDoorMaxAngle() URectLightComponent::URectLightComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) + , RayTracingData(new FRectLightRayTracingData()) { #if WITH_EDITORONLY_DATA if (!IsRunningCommandlet()) @@ -44,6 +45,7 @@ URectLightComponent::URectLightComponent(const FObjectInitializer& ObjectInitial SourceTexture = nullptr; BarnDoorAngle = GetRectLightBarnDoorMaxAngle(); BarnDoorLength = 20.0f; + // RayTracingData will be initialised on the render thread. } FLightSceneProxy* URectLightComponent::CreateSceneProxy() const @@ -51,6 +53,18 @@ FLightSceneProxy* URectLightComponent::CreateSceneProxy() const return new FRectLightSceneProxy(this); } +void URectLightComponent::SetSourceTexture(UTexture* NewValue) +{ + if (AreDynamicDataChangesAllowed() + && SourceTexture != NewValue) + { + SourceTexture = NewValue; + + // This will trigger a recreation of the LightSceneProxy and update RayTracingData accordingly if the texture has changed. + MarkRenderStateDirty(); + } +} + void URectLightComponent::SetSourceWidth(float NewValue) { if (AreDynamicDataChangesAllowed() @@ -153,6 +167,18 @@ float URectLightComponent::GetUniformPenumbraSize() const } } +void URectLightComponent::BeginDestroy() +{ + FRectLightRayTracingData* DeletedRenderData = RayTracingData; + RayTracingData = nullptr; + ENQUEUE_RENDER_COMMAND(DeleteBuildRectLightMipTree)( + [DeletedRenderData](FRHICommandListImmediate& RHICmdList) + { + delete DeletedRenderData; + }); + Super::BeginDestroy(); +} + #if WITH_EDITOR /** * Called after property has changed via e.g. property window or set command. @@ -174,18 +200,9 @@ FRectLightSceneProxy::FRectLightSceneProxy(const URectLightComponent* Component) , SourceHeight(Component->SourceHeight) , BarnDoorAngle(FMath::Clamp(Component->BarnDoorAngle, 0.f, GetRectLightBarnDoorMaxAngle())) , BarnDoorLength(FMath::Max(0.1f, Component->BarnDoorLength)) + , RayTracingData(Component->RayTracingData) , SourceTexture(Component->SourceTexture) { -#if RHI_RAYTRACING - if (IsRayTracingEnabled()) - { - ENQUEUE_RENDER_COMMAND(BuildRectLightMipTree)( - [this](FRHICommandListImmediate& RHICmdList) - { - BuildRectLightMipTree(RHICmdList); - }); - } -#endif } FRectLightSceneProxy::~FRectLightSceneProxy() {} @@ -248,130 +265,3 @@ bool FRectLightSceneProxy::GetWholeSceneProjectedShadowInitializer(const FSceneV return false; } - -#if RHI_RAYTRACING - -class FBuildRectLightMipTreeCS : public FGlobalShader -{ - DECLARE_SHADER_TYPE(FBuildRectLightMipTreeCS, Global) - -public: - static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) - { - return ShouldCompileRayTracingShadersForProject(Parameters.Platform); - } - - static uint32 GetGroupSize() - { - return 16; - } - - static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) - { - FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); - OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); - } - - FBuildRectLightMipTreeCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) - : FGlobalShader(Initializer) - { - TextureParameter.Bind(Initializer.ParameterMap, TEXT("RectLightTexture")); - TextureSamplerParameter.Bind(Initializer.ParameterMap, TEXT("TextureSampler")); - DimensionsParameter.Bind(Initializer.ParameterMap, TEXT("Dimensions")); - MipLevelParameter.Bind(Initializer.ParameterMap, TEXT("MipLevel")); - MipTreeParameter.Bind(Initializer.ParameterMap, TEXT("MipTree")); - } - - FBuildRectLightMipTreeCS() {} - - void SetParameters( - FRHICommandList& RHICmdList, - FTextureRHIRef Texture, - const FIntVector& Dimensions, - uint32 MipLevel, - FRWBuffer& MipTree - ) - { - FComputeShaderRHIParamRef ShaderRHI = GetComputeShader(); - - SetShaderValue(RHICmdList, ShaderRHI, DimensionsParameter, Dimensions); - SetShaderValue(RHICmdList, ShaderRHI, MipLevelParameter, MipLevel); - SetTextureParameter(RHICmdList, ShaderRHI, TextureParameter, TextureSamplerParameter, TStaticSamplerState::GetRHI(), Texture); - - check(MipTreeParameter.IsBound()); - MipTreeParameter.SetBuffer(RHICmdList, ShaderRHI, MipTree); - } - - void UnsetParameters( - FRHICommandList& RHICmdList, - EResourceTransitionAccess TransitionAccess, - EResourceTransitionPipeline TransitionPipeline, - FRWBuffer& MipTree, - FComputeFenceRHIParamRef Fence) - { - FComputeShaderRHIParamRef ShaderRHI = GetComputeShader(); - - MipTreeParameter.UnsetUAV(RHICmdList, ShaderRHI); - RHICmdList.TransitionResource(TransitionAccess, TransitionPipeline, MipTree.UAV, Fence); - } - - virtual bool Serialize(FArchive& Ar) - { - bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); - Ar << TextureParameter; - Ar << TextureSamplerParameter; - Ar << DimensionsParameter; - Ar << MipLevelParameter; - Ar << MipTreeParameter; - return bShaderHasOutdatedParameters; - } - -private: - FShaderResourceParameter TextureParameter; - FShaderResourceParameter TextureSamplerParameter; - - FShaderParameter DimensionsParameter; - FShaderParameter MipLevelParameter; - FRWShaderParameter MipTreeParameter; -}; - -IMPLEMENT_SHADER_TYPE(, FBuildRectLightMipTreeCS, TEXT("/Engine/Private/Raytracing/BuildMipTreeCS.usf"), TEXT("BuildRectLightMipTreeCS"), SF_Compute) - -void FRectLightSceneProxy::BuildRectLightMipTree(FRHICommandListImmediate& RHICmdList) -{ - check(IsInRenderingThread()); - - const auto ShaderMap = GetGlobalShaderMap(ERHIFeatureLevel::SM5); - TShaderMapRef BuildRectLightMipTreeComputeShader(ShaderMap); - RHICmdList.SetComputeShader(BuildRectLightMipTreeComputeShader->GetComputeShader()); - - // Allocate MIP tree - FLightShaderParameters LightParameters; - GetLightShaderParameters(LightParameters); - FIntVector TextureSize = LightParameters.SourceTexture->GetSizeXYZ(); - uint32 MipLevelCount = FMath::Min(FMath::CeilLogTwo(TextureSize.X), FMath::CeilLogTwo(TextureSize.Y)); - RectLightMipTreeDimensions = FIntVector(1 << MipLevelCount, 1 << MipLevelCount, 1); - uint32 NumElements = RectLightMipTreeDimensions.X * RectLightMipTreeDimensions.Y; - for (uint32 MipLevel = 1; MipLevel <= MipLevelCount; ++MipLevel) - { - uint32 NumElementsInLevel = (RectLightMipTreeDimensions.X >> MipLevel) * (RectLightMipTreeDimensions.Y >> MipLevel); - NumElements += NumElementsInLevel; - } - - RectLightMipTree.Initialize(sizeof(float), NumElements, PF_R32_FLOAT, BUF_UnorderedAccess | BUF_ShaderResource); - - // Execute hierarchical build - for (uint32 MipLevel = 0; MipLevel <= MipLevelCount; ++MipLevel) - { - FComputeFenceRHIRef MipLevelFence = RHICmdList.CreateComputeFence(TEXT("RectLightMipTree Build")); - BuildRectLightMipTreeComputeShader->SetParameters(RHICmdList, LightParameters.SourceTexture, RectLightMipTreeDimensions, MipLevel, RectLightMipTree); - FIntVector MipLevelDimensions = FIntVector(RectLightMipTreeDimensions.X >> MipLevel, RectLightMipTreeDimensions.Y >> MipLevel, 1); - FIntVector NumGroups = FIntVector::DivideAndRoundUp(MipLevelDimensions, FBuildRectLightMipTreeCS::GetGroupSize()); - DispatchComputeShader(RHICmdList, *BuildRectLightMipTreeComputeShader, NumGroups.X, NumGroups.Y, 1); - BuildRectLightMipTreeComputeShader->UnsetParameters(RHICmdList, EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EComputeToCompute, RectLightMipTree, MipLevelFence); - } - FComputeFenceRHIRef TransitionFence = RHICmdList.CreateComputeFence(TEXT("RectLightMipTree Transition")); - BuildRectLightMipTreeComputeShader->UnsetParameters(RHICmdList, EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EComputeToCompute, RectLightMipTree, TransitionFence); -} - -#endif // RHI_RAYTRACING diff --git a/Engine/Source/Runtime/Engine/Private/Components/SceneCaptureComponent.cpp b/Engine/Source/Runtime/Engine/Private/Components/SceneCaptureComponent.cpp index 3882ab54c443..dcb3e5c82cdc 100644 --- a/Engine/Source/Runtime/Engine/Private/Components/SceneCaptureComponent.cpp +++ b/Engine/Source/Runtime/Engine/Private/Components/SceneCaptureComponent.cpp @@ -874,8 +874,11 @@ void UPlanarReflectionComponent::PostEditChangeProperty(FPropertyChangedEvent& P ViewStates[ViewIndex].Allocate(); } - ProxyMeshComponent->bVisible = bShowPreviewPlane; - ProxyMeshComponent->MarkRenderStateDirty(); + if (ProxyMeshComponent) + { + ProxyMeshComponent->bVisible = bShowPreviewPlane; + ProxyMeshComponent->MarkRenderStateDirty(); + } } #endif diff --git a/Engine/Source/Runtime/Engine/Private/Components/SkeletalMeshComponent.cpp b/Engine/Source/Runtime/Engine/Private/Components/SkeletalMeshComponent.cpp index fb02d3b3a407..996c5b2d96ee 100644 --- a/Engine/Source/Runtime/Engine/Private/Components/SkeletalMeshComponent.cpp +++ b/Engine/Source/Runtime/Engine/Private/Components/SkeletalMeshComponent.cpp @@ -1649,33 +1649,18 @@ void USkeletalMeshComponent::ComputeRequiredBones(TArray& OutReq FAnimationRuntime::EnsureParentsPresent(OutFillComponentSpaceTransformsRequiredBones, SkeletalMesh->RefSkeleton); } -bool USkeletalMeshComponent::RecalcRequiredBones(int32 LODIndex) -{ - bool bSuccess = RecalcRequiredBonesInternalSMC(LODIndex); - RecalcRequiredBonesInternalAnimInstances(); - return bSuccess; -} - -bool USkeletalMeshComponent::RecalcRequiredBonesInternalSMC(int32 LODIndex) +void USkeletalMeshComponent::RecalcRequiredBones(int32 LODIndex) { if (!SkeletalMesh) { - return false; + return; } ComputeRequiredBones(RequiredBones, FillComponentSpaceTransformsRequiredBones, LODIndex, /*bIgnorePhysicsAsset=*/ false); BoneSpaceTransforms = SkeletalMesh->RefSkeleton.GetRefBonePose(); - // Invalidate cached bones. - CachedBoneSpaceTransforms.Empty(); - CachedComponentSpaceTransforms.Empty(); - CachedCurve.Empty(); - return true; -} - -void USkeletalMeshComponent::RecalcRequiredBonesInternalAnimInstances() -{ + // make sure animation requiredBone to mark as dirty if (AnimScriptInstance) { AnimScriptInstance->RecalcRequiredBones(); @@ -1695,6 +1680,11 @@ void USkeletalMeshComponent::RecalcRequiredBonesInternalAnimInstances() // this should always happen MarkRequiredCurveUpToDate(); bRequiredBonesUpToDate = true; + + // Invalidate cached bones. + CachedBoneSpaceTransforms.Empty(); + CachedComponentSpaceTransforms.Empty(); + CachedCurve.Empty(); } void USkeletalMeshComponent::MarkRequiredCurveUpToDate() @@ -1964,7 +1954,13 @@ void USkeletalMeshComponent::RefreshBoneTransforms(FActorComponentTickFunction* if (!bRequiredBonesUpToDate) { QUICK_SCOPE_CYCLE_COUNTER(STAT_USkeletalMeshComponent_RefreshBoneTransforms_RecalcRequiredBones); - RecalcRequiredBonesInternalSMC(PredictedLODLevel); + RecalcRequiredBones(PredictedLODLevel); + } + // if curves have to be refreshed + else if (!AreRequiredCurvesUpToDate()) + { + QUICK_SCOPE_CYCLE_COUNTER(STAT_USkeletalMeshComponent_RefreshBoneTransforms_RecalcRequiredCurves); + RecalcRequiredCurves(); } const bool bCachedShouldUseUpdateRateOptimizations = ShouldUseUpdateRateOptimizations() && AnimUpdateRateParams != nullptr; @@ -2010,22 +2006,6 @@ void USkeletalMeshComponent::RefreshBoneTransforms(FActorComponentTickFunction* return; } - // Recalculate the RequiredBones array, if necessary - if (!bRequiredBonesUpToDate) - { - QUICK_SCOPE_CYCLE_COUNTER(STAT_USkeletalMeshComponent_RefreshBoneTransforms_RecalcRequiredBones); - RecalcRequiredBonesInternalAnimInstances(); - } - // if curves have to be refreshed - else if (!AreRequiredCurvesUpToDate()) - { - QUICK_SCOPE_CYCLE_COUNTER(STAT_USkeletalMeshComponent_RefreshBoneTransforms_RecalcRequiredCurves); - RecalcRequiredCurves(); - } - - //Refresh UID list now curves have been updated - CurrentAnimCurveUIDFinder = (AnimScriptInstance) ? &AnimScriptInstance->GetRequiredBones().GetUIDToArrayLookupTable() : nullptr; - AnimEvaluationContext.SkeletalMesh = SkeletalMesh; AnimEvaluationContext.AnimInstance = AnimScriptInstance; AnimEvaluationContext.PostProcessAnimInstance = PostProcessAnimInstance; diff --git a/Engine/Source/Runtime/Engine/Private/Components/SkinnedMeshComponent.cpp b/Engine/Source/Runtime/Engine/Private/Components/SkinnedMeshComponent.cpp index a7a1ec9bd411..4e12a882c47e 100644 --- a/Engine/Source/Runtime/Engine/Private/Components/SkinnedMeshComponent.cpp +++ b/Engine/Source/Runtime/Engine/Private/Components/SkinnedMeshComponent.cpp @@ -359,6 +359,7 @@ USkinnedMeshComponent::USkinnedMeshComponent(const FObjectInitializer& ObjectIni ExternalInterpolationAlpha = 0.0f; ExternalDeltaTime = 0.0f; + ExternalTickRate = 1; bExternalInterpolate = false; bExternalUpdate = false; bExternalEvaluationRateLimited = false; @@ -1055,6 +1056,26 @@ FBoxSphereBounds USkinnedMeshComponent::CalcMeshBound(const FVector& RootOffset, return NewBounds; } +FBoxSphereBounds USkinnedMeshComponent::GetPreSkinnedLocalBounds() const +{ + const USkinnedMeshComponent* const MasterPoseComponentInst = MasterPoseComponent.Get(); + + if (SkeletalMesh) + { + // Get the Pre-skinned bounds from the skeletal mesh. Note that these bounds are the "ExtendedBounds", so they can be tweaked on the SkeletalMesh + return SkeletalMesh->GetBounds(); + } + else if(MasterPoseComponentInst && MasterPoseComponentInst->SkeletalMesh) + { + // Get the bounds from the master pose if ther is no skeletal mesh + return MasterPoseComponentInst->SkeletalMesh->GetBounds(); + } + else + { + // Fall back + return FBoxSphereBounds(ForceInitToZero); + } +} FMatrix USkinnedMeshComponent::GetBoneMatrix(int32 BoneIdx) const { diff --git a/Engine/Source/Runtime/Engine/Private/DataChannel.cpp b/Engine/Source/Runtime/Engine/Private/DataChannel.cpp index 3d75bf88200d..6491c2f33539 100644 --- a/Engine/Source/Runtime/Engine/Private/DataChannel.cpp +++ b/Engine/Source/Runtime/Engine/Private/DataChannel.cpp @@ -2585,7 +2585,7 @@ void UActorChannel::ProcessBunch( FInBunch & Bunch ) // note that this is a legitimate occurrence, particularly on client to server RPCs if ( !Actor || Actor->IsPendingKill() ) { - UE_LOG( LogNet, Verbose, TEXT( "UActorChannel::ProcessBunch: Actor was destroyed during Replicator.ReceivedBunch processing" ) ); + UE_LOG( LogNet, VeryVerbose, TEXT( "UActorChannel::ProcessBunch: Actor was destroyed during Replicator.ReceivedBunch processing" ) ); // If we lose the actor on this channel, we can no longer process bunches, so consider this channel broken Broken = 1; break; diff --git a/Engine/Source/Runtime/Engine/Private/DataReplication.cpp b/Engine/Source/Runtime/Engine/Private/DataReplication.cpp index fba3ea1ae901..68a00a28ad1c 100644 --- a/Engine/Source/Runtime/Engine/Private/DataReplication.cpp +++ b/Engine/Source/Runtime/Engine/Private/DataReplication.cpp @@ -42,7 +42,7 @@ static FAutoConsoleVariableRef CVarNetRPCDebug( TEXT(" 1: Print bunches as they are sent."), ECVF_Default); -int32 GSupportsFastArrayDelta = 0; +int32 GSupportsFastArrayDelta = 1; static FAutoConsoleVariableRef CVarSupportsFastArrayDelta( TEXT("net.SupportFastArrayDelta"), GSupportsFastArrayDelta, @@ -685,6 +685,100 @@ void FObjectReplicator::StopReplicating( class UActorChannel * InActorChannel ) } } +/** + * Handling NAKs / Property Retransmission. + * + * Note, NACK handling only occurs on connections that "replicate" data, which is currently + * only Servers. RPC retransmission is handled elsewhere. + * + * RepLayouts: + * + * As we send properties through FRepLayout the is a Changelist Manager that is shared + * between all connections tracks sets of properties that were recently changed (history items), + * as well as one aggregate set of all properties that have ever been sent. + * + * Each Sending Rep State, which is connection unique, also tracks the set of changed + * properties. These history items will only be created when replicating the object, + * so there will be fewer of them in general, but they will still contain any properties + * that compared differently (not *just* the properties that were actually replicated). + * + * Whenever a NAK is received, we will iterate over the SendingRepState changelist + * and mark any of the properties sent in the NAKed packet for retransmission. + * + * The next time Properties are replicated for the Object, we will merge in any changelists + * from NAKed history items. + * + * Custom Delta Properties: + * + * For Custom Delta Properties (CDP), we rely primarily on FPropertyRetirements and INetDeltaBaseState + * for tracking property retransmission. + * + * INetDeltaBaseStates are used to tracked internal state specific to a given type of CDP. + * For example, Fast Array Replicators will use FNetFastTArrayBaseState, or some type + * derived from that. + * + * When an FObjectReplicator is created, we will create an INetDeltaBaseState for every CDP, + * as well as a dummy FPropertyRetirement. This Property Retirement is used as the head + * of a linked list of Retirements, and is generally never populated with any useful information. + * + * Every time we replicate a CDP, we will pass in the most recent Base State, and we will be + * returned a new CDP. If data is actually sent, then we will create a new Property Retirement, + * adding it as the tail of our linked list. The new Property Retirement will also hold a reference + * to the old INetDeltaBaseState (i.e., the state of the CDP before it replicated its properties). + * + * Just before replicating, we will go through and free any ACKed FPropertyRetirments (see + * UpdateAckedRetirements). + * + * After replicating, we will cache off the returned Base State to be used as the "old" state + * the next time the property is replicated. + * + * Whenever a NAK is received, we will run through our Property Retirements. Any retirements + * that predate the NACK will be removed and treated as if they were ACKs. The first + * retirement that is found to be within the NAKed range will have its INetDeltaBaseState + * restored (which should be the state before the NAKed packet was sent), and then + * that retirement as well as all remaining will be removed. + * + * The onus is then on the CDP to resend any necessary properties based on its current / live + * state and the restored Net Delta Base State. + * + * Fast Array Properties: + * + * Fast Array Properties are implemented as Custom Delta Properties (CDP). Therefore, they mostly + * follow the flow laid out above. + + * FNetFastTArrayBaseState is the basis for all Fast Array Serializer INetDeltaBaseStates. + * This struct tracks the Replication Key of the Array, the ID to Replication Key map of individual + * Array Items, and a History Number. + * + * As we replicate Fast Array Properties, we use the Array Replication key to see if anything + * is possibly dirty in the Array and the ID to Replication map to see which Array Element + * items actually are dirty. A mismatch between the Net Base State Key and the Key stored on + * the live Fast Array (either the Array Replication Key, or any Item Key) is how we determine + * if the Array or Items is dirty. + * + * Whenever a NAK is received, our Old Base State will be reset to the last known ACKed value, + * as described in the CDP section above. This means that our Array Replication Key and ID To + * Item Replication Key should be reset to those states, forcing a mismatch the next time we + * replicate if anything has changed. + * + * When net.SupportFastArrayDelta is enabled, we perform an additional step in which we actually + * compare the properties of dirty items. This is very similar to normal Property replication + * using RepLayouts, and leverages most of the same code. + * + * This includes tracking history items just like Rep Layout. Instead of tracking histories per + * Sending Rep State / Per Connection, we just manage a single set of Histories on the Rep + * Changelist Mgr. Changelists are stored per Fast Array Item, and are referenced via ID. + * + * Whenever we go to replicate a Fast Array Item, we will merge together all changelists since + * we last sent that item, and send those accumulated changes. + * + * This means that property retransmission for Fast Array Items is an amalgamation of Rep Layout + * retransmission and CDP retransmission. + * + * Whenever a NAK is received, our History Number should be reset to the last known ACKed value, + * and that should be enough to force us to accumulate any of the NAKed item changelists. + */ + void FObjectReplicator::ReceivedNak( int32 NakPacketId ) { const UObject* Object = GetObject(); @@ -861,13 +955,6 @@ bool FObjectReplicator::ReceivedBunch(FNetBitReader& Bunch, const FReplicationFl FNetSerializeCB NetSerializeCB(ConnectionNetDriver); - FNetDeltaSerializeInfo Parms; - Parms.Map = PackageMap; - Parms.Reader = &Reader; - Parms.NetSerializeCB = &NetSerializeCB; - Parms.Connection = Connection; - Parms.Object = Object; - // Read each property/function blob into Reader (so we've safely jumped over this data in the Bunch/stream at this point) while (OwningChannel->ReadFieldHeaderAndPayload(Object, ClassCache, NetFieldExportGroup, Bunch, &FieldCache, Reader)) { @@ -917,6 +1004,14 @@ bool FObjectReplicator::ReceivedBunch(FNetBitReader& Bunch, const FReplicationFl } } #endif + + FNetDeltaSerializeInfo Parms; + Parms.Map = PackageMap; + Parms.Reader = &Reader; + Parms.NetSerializeCB = &NetSerializeCB; + Parms.Connection = Connection; + Parms.Object = Object; + uint32 StaticArrayIndex = 0; int32 Offset = 0; if (!FNetSerializeCB::ReceiveCustomDeltaProperty(LocalRepLayout, Parms, ReplicatedProp, StaticArrayIndex, Offset)) @@ -1354,14 +1449,11 @@ void FObjectReplicator::ReplicateCustomDeltaProperties( FNetBitWriter & Bunch, F { // Get info. FPropertyRetirement& Retire = Retirement[CustomDeltaProperty]; - FRepRecord* Rep = &ObjectClass->ClassReps[CustomDeltaProperty]; PRAGMA_DISABLE_DEPRECATION_WARNINGS UProperty* It = LocalRepLayout.GetPropertyForRepIndex(CustomDeltaProperty); PRAGMA_ENABLE_DEPRECATION_WARNINGS - int32 Index = Rep->Index; - const ELifetimeCondition RepCondition = LocalRepLayout.GetPropertyLifetimeCondition(CustomDeltaProperty); check(RepCondition >= 0 && RepCondition < COND_Max); @@ -1939,6 +2031,7 @@ void FObjectReplicator::UpdateUnmappedObjects(bool & bOutHasMoreUnmapped) checkf( bHasQueuedBunches || ReceivingRepState->RepNotifies.Num() == 0, TEXT("Failed RepState RepNotifies check. Num=%d. Object=%s. Channel QueuedBunches=%d"), ReceivingRepState->RepNotifies.Num(), *Object->GetFullName(), OwningChannel ? OwningChannel->QueuedBunches.Num() : 0 ); checkf( bHasQueuedBunches || RepNotifies.Num() == 0, TEXT("Failed replicator RepNotifies check. Num=%d. Object=%s. Channel QueuedBunches=%d"), RepNotifies.Num(), *Object->GetFullName(), OwningChannel ? OwningChannel->QueuedBunches.Num() : 0 ); + bool bCalledPreNetReceive = false; bool bSomeObjectsWereMapped = false; check(RepLayout); @@ -1946,7 +2039,7 @@ void FObjectReplicator::UpdateUnmappedObjects(bool & bOutHasMoreUnmapped) const FRepLayout& LocalRepLayout = *RepLayout; // Let the rep layout update any unmapped properties - LocalRepLayout.UpdateUnmappedObjects(ReceivingRepState, Connection->PackageMap, Object, bSomeObjectsWereMapped, bOutHasMoreUnmapped); + LocalRepLayout.UpdateUnmappedObjects(ReceivingRepState, Connection->PackageMap, Object, bCalledPreNetReceive, bSomeObjectsWereMapped, bOutHasMoreUnmapped); FNetSerializeCB NetSerializeCB(Connection->Driver); @@ -1957,7 +2050,7 @@ void FObjectReplicator::UpdateUnmappedObjects(bool & bOutHasMoreUnmapped) Parms.NetSerializeCB = &NetSerializeCB; Parms.bUpdateUnmappedObjects = true; - Parms.bCalledPreNetReceive = bSomeObjectsWereMapped; // RepLayout used this to flag whether PreNetReceive was called + Parms.bCalledPreNetReceive = bCalledPreNetReceive; TArray> CompletelyMappedProperties; @@ -1966,6 +2059,7 @@ void FObjectReplicator::UpdateUnmappedObjects(bool & bOutHasMoreUnmapped) bSomeObjectsWereMapped |= Parms.bOutSomeObjectsWereMapped; bOutHasMoreUnmapped |= Parms.bOutHasMoreUnmapped; + bCalledPreNetReceive |= Parms.bCalledPreNetReceive; // This should go away when UnmappedCustomProperties goes away, and when RepNotifies // are merged with RepState RepNotifies. @@ -1984,7 +2078,7 @@ PRAGMA_DISABLE_DEPRECATION_WARNINGS } PRAGMA_ENABLE_DEPRECATION_WARNINGS - if (bSomeObjectsWereMapped) + if (bCalledPreNetReceive) { // If we mapped some objects, make sure to call PostNetReceive (some game code will need to think this was actually replicated to work) PostNetReceive(); diff --git a/Engine/Source/Runtime/Engine/Private/DebugViewModeHelpers.cpp b/Engine/Source/Runtime/Engine/Private/DebugViewModeHelpers.cpp index 6d7061bfbea5..3aafef40ff6e 100644 --- a/Engine/Source/Runtime/Engine/Private/DebugViewModeHelpers.cpp +++ b/Engine/Source/Runtime/Engine/Private/DebugViewModeHelpers.cpp @@ -83,7 +83,7 @@ int32 GetNumActorsInWorld(UWorld* InWorld) return ActorCount; } -bool WaitForShaderCompilation(const FText& Message, FSlowTask& ProgressTask) +bool WaitForShaderCompilation(const FText& Message, FSlowTask* ProgressTask) { FlushRenderingCommands(); @@ -104,19 +104,22 @@ bool WaitForShaderCompilation(const FText& Message, FSlowTask& ProgressTask) const int32 NumberOfShadersCompiledThisFrame = RemainingShaders - RemainingShadersThisFrame; const float FrameProgress = (float)NumberOfShadersCompiledThisFrame / (float)NumShadersToBeCompiled; - ProgressTask.EnterProgressFrame(FrameProgress); - SlowTask.EnterProgressFrame(FrameProgress); - if (GWarn->ReceivedUserCancel()) + if (ProgressTask) { - return false; + ProgressTask->EnterProgressFrame(FrameProgress); + SlowTask.EnterProgressFrame(FrameProgress); + if (GWarn->ReceivedUserCancel()) + { + return false; + } } } RemainingShaders = RemainingShadersThisFrame; } } - else + else if (ProgressTask) { - ProgressTask.EnterProgressFrame(); + ProgressTask->EnterProgressFrame(); if (GWarn->ReceivedUserCancel()) { return false; @@ -134,7 +137,7 @@ bool WaitForShaderCompilation(const FText& Message, FSlowTask& ProgressTask) * * @return true if the operation is a success, false if it was canceled. */ -bool GetUsedMaterialsInWorld(UWorld* InWorld, OUT TSet& OutMaterials, FSlowTask& ProgressTask) +bool GetUsedMaterialsInWorld(UWorld* InWorld, OUT TSet& OutMaterials, FSlowTask* ProgressTask) { #if WITH_EDITORONLY_DATA if (!InWorld) @@ -145,7 +148,10 @@ bool GetUsedMaterialsInWorld(UWorld* InWorld, OUT TSet& Out const int32 NumActorsInWorld = GetNumActorsInWorld(InWorld); if (!NumActorsInWorld) { - ProgressTask.EnterProgressFrame(); + if (ProgressTask) + { + ProgressTask->EnterProgressFrame(); + } return true; } @@ -163,11 +169,14 @@ bool GetUsedMaterialsInWorld(UWorld* InWorld, OUT TSet& Out for (AActor* Actor : Level->Actors) { - ProgressTask.EnterProgressFrame(OneOverNumActorsInWorld); - SlowTask.EnterProgressFrame(OneOverNumActorsInWorld); - if (GWarn->ReceivedUserCancel()) + if (ProgressTask) { - return false; + ProgressTask->EnterProgressFrame(OneOverNumActorsInWorld); + SlowTask.EnterProgressFrame(OneOverNumActorsInWorld); + if (GWarn->ReceivedUserCancel()) + { + return false; + } } // Check the actor after incrementing the progress. @@ -215,7 +224,7 @@ bool GetUsedMaterialsInWorld(UWorld* InWorld, OUT TSet& Out * @param Materials The materials to update, the one that failed compilation will be removed (IN OUT). * @return true if the operation is a success, false if it was canceled. */ -bool CompileDebugViewModeShaders(EDebugViewShaderMode ShaderMode, EMaterialQualityLevel::Type QualityLevel, ERHIFeatureLevel::Type FeatureLevel, bool bFullRebuild, bool bWaitForPreviousShaders, TSet& Materials, FSlowTask& ProgressTask) +bool CompileDebugViewModeShaders(EDebugViewShaderMode ShaderMode, EMaterialQualityLevel::Type QualityLevel, ERHIFeatureLevel::Type FeatureLevel, bool bFullRebuild, bool bWaitForPreviousShaders, TSet& Materials, FSlowTask* ProgressTask) { #if WITH_EDITORONLY_DATA if (!GShaderCompilingManager || !Materials.Num()) diff --git a/Engine/Source/Runtime/Engine/Private/DemoNetDriver.cpp b/Engine/Source/Runtime/Engine/Private/DemoNetDriver.cpp index 7a7217acb1aa..9023c064158b 100644 --- a/Engine/Source/Runtime/Engine/Private/DemoNetDriver.cpp +++ b/Engine/Source/Runtime/Engine/Private/DemoNetDriver.cpp @@ -37,6 +37,7 @@ #include "ProfilingDebugging/CsvProfiler.h" #include "Misc/EngineVersion.h" #include "Stats/Stats2.h" +#include "Engine/ChildConnection.h" DEFINE_LOG_CATEGORY( LogDemo ); @@ -749,6 +750,7 @@ void UDemoNetDriver::FinishDestroy() } } + CleanUpSplitscreenConnections(true); FCoreUObjectDelegates::PostLoadMapWithWorld.RemoveAll(this); Super::FinishDestroy(); } @@ -1205,6 +1207,7 @@ bool UDemoNetDriver::ContinueListen(FURL& ListenURL) { SpectatorController->Player = nullptr; // Force APlayerController::DestroyNetworkActorHandled to return false World->DestroyActor(SpectatorController, true); + SpectatorControllers.Empty(); SpectatorController = nullptr; } @@ -1466,24 +1469,29 @@ void UDemoNetDriver::TickDispatch( float DeltaSeconds ) // Update time dilation on spectator pawn to compensate for any demo dilation // (we want to continue to fly around in real-time) - if ( SpectatorController != nullptr ) + for (APlayerController* CurSpectatorController : SpectatorControllers) { + if (CurSpectatorController == nullptr) + { + continue; + } + if ( World->GetWorldSettings()->DemoPlayTimeDilation > KINDA_SMALL_NUMBER ) { - SpectatorController->CustomTimeDilation = 1.0f / World->GetWorldSettings()->DemoPlayTimeDilation; + CurSpectatorController->CustomTimeDilation = 1.0f / World->GetWorldSettings()->DemoPlayTimeDilation; } else { - SpectatorController->CustomTimeDilation = 1.0f; + CurSpectatorController->CustomTimeDilation = 1.0f; } - if ( SpectatorController->GetSpectatorPawn() != nullptr ) + if (CurSpectatorController->GetSpectatorPawn() != nullptr) { - SpectatorController->GetSpectatorPawn()->CustomTimeDilation = SpectatorController->CustomTimeDilation; - - SpectatorController->GetSpectatorPawn()->PrimaryActorTick.bTickEvenWhenPaused = true; + CurSpectatorController->GetSpectatorPawn()->CustomTimeDilation = CurSpectatorController->CustomTimeDilation; - USpectatorPawnMovement* SpectatorMovement = Cast(SpectatorController->GetSpectatorPawn()->GetMovementComponent()); + CurSpectatorController->GetSpectatorPawn()->PrimaryActorTick.bTickEvenWhenPaused = true; + + USpectatorPawnMovement* SpectatorMovement = Cast(CurSpectatorController->GetSpectatorPawn()->GetMovementComponent()); if ( SpectatorMovement ) { @@ -1848,6 +1856,12 @@ void UDemoNetDriver::SaveCheckpoint() CheckpointSaveContext.TotalCheckpointSaveFrames = 0; LastCheckpointTime = DemoCurrentTime; + if (bDeltaCheckpoint) + { + CheckpointSaveContext.DeltaDeletedActorGuids = MoveTemp(DeltaDeletedActorGuids); + CheckpointSaveContext.DeltaDeletedNetStartupActors = MoveTemp(DeltaDeletedNetStartupActors); + } + UE_LOG( LogDemo, Log, TEXT( "Starting checkpoint. Actors: %i" ), GetNetworkObjectList().GetActiveObjects().Num() ); // Do the first checkpoint tick now if we're not amortizing @@ -2015,11 +2029,11 @@ void UDemoNetDriver::TickCheckpoint() // Save deleted startup actors if (bDeltaCheckpoint) { - *CheckpointArchive << DeltaDeletedNetStartupActors; - DeltaDeletedNetStartupActors.Empty(); + *CheckpointArchive << CheckpointSaveContext.DeltaDeletedNetStartupActors; + CheckpointSaveContext.DeltaDeletedNetStartupActors.Empty(); - *CheckpointArchive << DeltaDeletedActorGuids; - DeltaDeletedActorGuids.Empty(); + *CheckpointArchive << CheckpointSaveContext.DeltaDeletedActorGuids; + CheckpointSaveContext.DeltaDeletedActorGuids.Empty(); } else { @@ -2426,7 +2440,7 @@ void UDemoNetDriver::TickDemoRecordFrame( float DeltaSeconds ) } const double RecordFrameStartTime = FPlatformTime::Seconds(); - const double RecordTimeLimit = (MaxDesiredRecordTimeMS * 1000.f); + const double RecordTimeLimit = (MaxDesiredRecordTimeMS / 1000.f); // Mark any new streaming levels, so that they are saved out this frame if (!HasLevelStreamingFixes()) @@ -2927,12 +2941,7 @@ void UDemoNetDriver::PauseChannels( const bool bPause ) ActorChannel->CustomTimeDilation = bPause ? 0.0f : 1.0f; - if ( ActorChannel->GetActor() == SpectatorController ) - { - continue; - } - - if ( ActorChannel->GetActor() == nullptr ) + if (ActorChannel->GetActor() == nullptr || SpectatorControllers.Contains(ActorChannel->GetActor()) ) { continue; } @@ -3189,6 +3198,13 @@ void UDemoNetDriver::ProcessSeamlessTravel(int32 LevelIndex) Controllers.Add(Iterator->Get()); } + // Clean up any splitscreen spectators if we have them. + // Let the destroy below handle deletion of the objects. + if (SpectatorControllers.Num() > 1) + { + CleanUpSplitscreenConnections(false); + } + for (int i = 0; i < Controllers.Num(); i++) { if (Controllers[i]) @@ -3196,9 +3212,14 @@ void UDemoNetDriver::ProcessSeamlessTravel(int32 LevelIndex) // bNetForce is true so that the replicated spectator player controller will // be destroyed as well. Controllers[i]->Destroy(true); + + // If we can, remove the spectator here as well. + SpectatorControllers.Remove(Cast(Controllers[i])); } } + SpectatorControllers.Empty(); + // Set this to nullptr since we just destroyed it. SpectatorController = nullptr; @@ -3914,21 +3935,15 @@ void UDemoNetDriver::FinalizeFastForward( const double StartTime ) UE_LOG( LogDemo, Log, TEXT( "Fast forward took %.2f seconds." ), FastForwardTotalSeconds ); } -void UDemoNetDriver::SpawnDemoRecSpectator( UNetConnection* Connection, const FURL& ListenURL ) +APlayerController* UDemoNetDriver::CreateDemoPlayerController(UNetConnection* Connection, const FURL& ListenURL) { - // Optionally skip spawning the demo spectator if requested via the URL option - if (ListenURL.HasOption(TEXT("SkipSpawnSpectatorController"))) - { - return; - } - - check( Connection != nullptr ); + check(Connection != nullptr); // Get the replay spectator controller class from the default game mode object, // since the game mode instance isn't replicated to clients of live games. AGameStateBase* GameState = GetWorld() != nullptr ? GetWorld()->GetGameState() : nullptr; TSubclassOf DefaultGameModeClass = GameState != nullptr ? GameState->GameModeClass : nullptr; - + // If we don't have a game mode class from the world, try to get it from the URL option. // This may be true on clients who are recording a replay before the game mode class was replicated to them. if (DefaultGameModeClass == nullptr) @@ -3946,53 +3961,162 @@ void UDemoNetDriver::SpawnDemoRecSpectator( UNetConnection* Connection, const FU if ( C == nullptr ) { - UE_LOG( LogDemo, Error, TEXT( "UDemoNetDriver::SpawnDemoRecSpectator: Failed to load demo spectator class." ) ); - return; + UE_LOG(LogDemo, Error, TEXT("UDemoNetDriver::CreateDemoPlayerController: Failed to load demo spectator class.")); + return nullptr; } FActorSpawnParameters SpawnInfo; SpawnInfo.ObjectFlags |= RF_Transient; // We never want these to save into a map - SpectatorController = World->SpawnActor( C, SpawnInfo ); + APlayerController* NewDemoController = World->SpawnActor(C, SpawnInfo); - if ( SpectatorController == nullptr ) + if (NewDemoController == nullptr) { - UE_LOG( LogDemo, Error, TEXT( "UDemoNetDriver::SpawnDemoRecSpectator: Failed to spawn demo spectator." ) ); - return; + UE_LOG(LogDemo, Error, TEXT("UDemoNetDriver::CreateDemoPlayerController: Failed to spawn demo spectator.")); + return nullptr; } // Streaming volumes logic must not be affected by replay spectator camera - SpectatorController->bIsUsingStreamingVolumes = false; + NewDemoController->bIsUsingStreamingVolumes = false; - // Make sure SpectatorController->GetNetDriver returns this driver. Ensures functions that depend on it, + // Make sure the player controller GetNetDriver returns this driver. Ensures functions that depend on it, // such as IsLocalController, work as expected. - SpectatorController->SetNetDriverName(NetDriverName); + NewDemoController->SetNetDriverName(NetDriverName); // If the controller doesn't have a player state, we are probably recording on a client. // Spawn one manually. - if ( SpectatorController->PlayerState == nullptr && GetWorld() != nullptr && GetWorld()->IsRecordingClientReplay()) + if (NewDemoController->PlayerState == nullptr && GetWorld() != nullptr && GetWorld()->IsRecordingClientReplay()) { - SpectatorController->InitPlayerState(); + NewDemoController->InitPlayerState(); } // Tell the game that we're spectator and not a normal player - if (SpectatorController->PlayerState) + if (NewDemoController->PlayerState) { - SpectatorController->PlayerState->bOnlySpectator = true; + NewDemoController->PlayerState->bOnlySpectator = true; } - for ( FActorIterator It( World ); It; ++It) + for (FActorIterator It(World); It; ++It) { - if ( It->IsA( APlayerStart::StaticClass() ) ) + if (It->IsA(APlayerStart::StaticClass())) { - SpectatorController->SetInitialLocationAndRotation( It->GetActorLocation(), It->GetActorRotation() ); + NewDemoController->SetInitialLocationAndRotation(It->GetActorLocation(), It->GetActorRotation()); break; } } - - SpectatorController->SetReplicates( true ); - SpectatorController->SetAutonomousProxy( true ); - SpectatorController->SetPlayer( Connection ); + NewDemoController->SetReplicates(true); + NewDemoController->SetAutonomousProxy(true); + NewDemoController->SetPlayer(Connection); + + return NewDemoController; +} + +void UDemoNetDriver::SpawnDemoRecSpectator( UNetConnection* Connection, const FURL& ListenURL ) +{ + // Optionally skip spawning the demo spectator if requested via the URL option + if (ListenURL.HasOption(TEXT("SkipSpawnSpectatorController"))) + { + return; + } + + SpectatorController = CreateDemoPlayerController(Connection, ListenURL); + SpectatorControllers.Add(SpectatorController); +} + +bool UDemoNetDriver::SpawnSplitscreenViewer(ULocalPlayer* NewPlayer, UWorld* InWorld) +{ + if (NewPlayer == nullptr || InWorld == nullptr) + { + UE_LOG(LogDemo, Warning, TEXT("UDemoNetDriver::SpawnSplitscreenViewer: Local Player or World is invalid!")); + return false; + } + + if (ClientConnections.Num() == 0 && ServerConnection == nullptr) + { + UE_LOG(LogDemo, Error, TEXT("UDemoNetDriver::SpawnSplitscreenViewer: This netdriver has no demo connection data")); + return false; + } + + UNetConnection* ChildConnection = CreateChild((ClientConnections.Num() > 0) ? ClientConnections[0] : ServerConnection); + + APlayerController* NewSplitscreenController = CreateDemoPlayerController(ChildConnection, TEXT("")); + if (NewSplitscreenController == nullptr) + { + UE_LOG(LogDemo, Warning, TEXT("UDemoNetDriver::SpawnSplitscreenViewer: Unable to create new splitscreen controller")); + return false; + } + + // Link this spectator to the given local player, as this will facilitate spectator pawn creation + // (spectator pawns only create if the controller is linked to a local player) + NewSplitscreenController->Player = NewPlayer; + NewSplitscreenController->NetPlayerIndex = GEngine->GetGamePlayers(InWorld).Find(NewPlayer); + + // Create the Pawn + NewSplitscreenController->ChangeState(NAME_Spectating); + NewPlayer->CurrentNetSpeed = 0; + + // Link the local player to the player controller as the localplayer has been marked as active + // but without a playercontroller, the player will never be considered "ready" by other systems. + NewPlayer->PlayerController = NewSplitscreenController; + + // This would typically be set via SetPlayer, but we need to call SetPlayer with the LocalPlayer + // and not with the child connection, otherwise we never create the input controls we need. + ChildConnection->PlayerController = NewSplitscreenController; + ChildConnection->OwningActor = NewSplitscreenController; + + // Create input control + NewSplitscreenController->SetPlayer(NewPlayer); + + // Add to the list + SpectatorControllers.Add(NewSplitscreenController); + + return true; +} + +bool UDemoNetDriver::RemoveSplitscreenViewer(APlayerController* RemovePlayer, bool bMarkOwnerForDeletion) +{ + UE_LOG(LogDemo, Log, TEXT("Attempting to remove splitscreen viewer!")); + if (RemovePlayer && SpectatorControllers.Contains(RemovePlayer) && RemovePlayer != SpectatorController) + { + SpectatorControllers.Remove(RemovePlayer); + UNetConnection* RemovedNetConnection = RemovePlayer->NetConnection; + if (!bMarkOwnerForDeletion) + { + RemovedNetConnection->OwningActor = nullptr; + } + RemovedNetConnection->Close(); + RemovedNetConnection->CleanUp(); + RemovePlayer->NetConnection = nullptr; + return true; + } + + return false; +} + +int32 UDemoNetDriver::CleanUpSplitscreenConnections(bool bDeleteOwner) +{ + int32 NumSplitscreenConnectionsCleaned = 0; + + for (APlayerController* CurController : SpectatorControllers) + { + UNetConnection* ControllerNetConnection = CurController->NetConnection; + if (ControllerNetConnection && ControllerNetConnection->IsA(UChildConnection::StaticClass())) + { + ++NumSplitscreenConnectionsCleaned; + // With this toggled, this prevents actor deletion (which we don't want to do when scrubbing) + if (!bDeleteOwner) + { + ControllerNetConnection->OwningActor = nullptr; + } + ControllerNetConnection->Close(); + ControllerNetConnection->CleanUp(); + CurController->NetConnection = nullptr; + } + } + + FString OwnerDeletionStr(bDeleteOwner ? TEXT("with") : TEXT("without")); + UE_LOG(LogDemo, Log, TEXT("Cleaned up %d splitscreen connections %s owner deletion"), NumSplitscreenConnectionsCleaned, *OwnerDeletionStr); + return NumSplitscreenConnectionsCleaned; } void UDemoNetDriver::ReplayStreamingReady( const FStartStreamingResult& Result ) @@ -4252,44 +4376,20 @@ void UDemoNetDriver::PrepFastForwardLevels() continue; } - TSet> LevelActors; + bool bStartupActors = false; + for (AActor* Actor : Level->Actors) { - if (Actor == nullptr || !Actor->IsNetStartupActor()) + if (Actor != nullptr && Actor->IsNetStartupActor()) { - continue; - } - else if (DeletedNetStartupActors.Contains(Actor->GetFullName())) - { - // Put this actor on the rollback list so we can undelete it during future scrubbing, - // then delete it. - QueueNetStartupActorForRollbackViaDeletion(Actor); - LocalWorld->DestroyActor(Actor, true); - } - else - { - if (RollbackNetStartupActors.Contains(Actor->GetFullName())) - { - LocalWorld->DestroyActor(Actor, true); - } - else - { - LevelActors.Add(Actor); + bStartupActors = true; + break; } } - } - TArray SpawnedActors; - RespawnNecessaryNetStartupActors(SpawnedActors, Level); - - for(AActor* Actor : SpawnedActors) + if (bStartupActors) { - LevelActors.Add(Actor); - } - - if (LevelActors.Num() > 0) - { - LevelsPendingFastForward.Emplace(Level, MoveTemp(LevelActors)); + LevelsPendingFastForward.Add(Level); } } } @@ -4339,32 +4439,6 @@ bool UDemoNetDriver::FastForwardLevels(const FGotoResult& GotoResult) LevelIndices.Reserve(LevelsPendingFastForward.Num()); StartupActors.Reserve(LevelsPendingFastForward.Num() * 4); - for (auto It = LevelsPendingFastForward.CreateIterator(); It; ++It) - { - // Track the appropriate level, and mark it as ready. - FLevelStatus& LevelStatus = GetLevelStatus(GetLevelPackageName(*It.Key())); - LevelIndices.Add(LevelStatus.LevelIndex); - LevelStatus.bIsReady = true; - - // Quick sanity check to make sure the actors are still valid - // NOTE: The only way any of these should not be valid is if the level was unloaded, - // or something in the demo caused the actor to be destroyed *before* - // the level was ready. Either case seems bad if we've made it this far. - TSet>& LevelStartupActors = It.Value(); - for (auto ActorIt = LevelStartupActors.CreateIterator(); ActorIt; ++ActorIt) - { - if (!ensure((*ActorIt).IsValid())) - { - ActorIt.RemoveCurrent(); - } - } - - LocalLevels.Add(It.Key()); - StartupActors.Append(MoveTemp(LevelStartupActors)); - } - - LevelsPendingFastForward.Reset(); - struct FLocalReadPacketsHelper { FLocalReadPacketsHelper(UDemoNetDriver& InDriver, const float InLastPacketTime): @@ -4391,9 +4465,9 @@ bool UDemoNetDriver::FastForwardLevels(const FGotoResult& GotoResult) { Ar.Seek(PreFramePos); if (ensure(NumPackets != 0)) - { + { Packets.RemoveAt(NumPackets, Packets.Num() - NumPackets); - } + } return false; } @@ -4401,9 +4475,9 @@ bool UDemoNetDriver::FastForwardLevels(const FGotoResult& GotoResult) } bool IsError() const - { + { return bErrorOccurred; - } + } TArray Packets; @@ -4412,16 +4486,19 @@ bool UDemoNetDriver::FastForwardLevels(const FGotoResult& GotoResult) UDemoNetDriver& Driver; const float LastPacketTime; - // We only want to process packets that are before anything we've currently processed. - // Further, we want to make sure that we leave the archive in a good state for later use. - int32 NumPackets = 0; - float LastReadTime = 0; - FArchivePos PreFramePos = 0; + // We only want to process packets that are before anything we've currently processed. + // Further, we want to make sure that we leave the archive in a good state for later use. + int32 NumPackets = 0; + float LastReadTime = 0; + FArchivePos PreFramePos = 0; bool bErrorOccurred = false; } ReadPacketsHelper(*this, LastProcessedPacketTime); + DeletedNetStartupActors.Empty(); + DeletedActorGuids.Empty(); + { auto IgnoreReceivedExportGUIDs = ((UPackageMapClient*)ServerConnection->PackageMap)->ScopedIgnoreReceivedExportGUIDs(); @@ -4451,7 +4528,39 @@ bool UDemoNetDriver::FastForwardLevels(const FGotoResult& GotoResult) FArchivePos PacketOffset = 0; *CheckpointArchive << PacketOffset; - CheckpointArchive->Seek(PacketOffset + CheckpointArchive->Tell()); + + PacketOffset += CheckpointArchive->Tell(); + + if (PlaybackDemoHeader.Version >= HISTORY_MULTIPLE_LEVELS) + { + int32 LevelIndex; + *CheckpointArchive << LevelIndex; + } + + if (PlaybackDemoHeader.Version >= HISTORY_DELETED_STARTUP_ACTORS) + { + if (bDeltaCheckpoint) + { + TSet DeltaActors; + *CheckpointArchive << DeltaActors; + + DeletedNetStartupActors.Append(DeltaActors); + + TSet DeltaGuids; + *CheckpointArchive << DeltaGuids; + + DeletedActorGuids.Append(DeltaGuids); + } + else + { + DeletedNetStartupActors.Empty(); + DeletedActorGuids.Empty(); + + *CheckpointArchive << DeletedNetStartupActors; + } + } + + CheckpointArchive->Seek(PacketOffset); if (!ReadPacketsHelper.ReadPackets(*CheckpointArchive) && ReadPacketsHelper.IsError()) { @@ -4482,6 +4591,54 @@ bool UDemoNetDriver::FastForwardLevels(const FGotoResult& GotoResult) // If we've gotten this far, it means we should have something to process. check(ReadPacketsHelper.Packets.Num() > 0); + UWorld* LocalWorld = GetWorld(); + for (ULevel* Level : LevelsPendingFastForward) + { + // Track the appropriate level, and mark it as ready. + FLevelStatus& LevelStatus = GetLevelStatus(GetLevelPackageName(*Level)); + LevelIndices.Add(LevelStatus.LevelIndex); + LevelStatus.bIsReady = true; + + TSet> LevelActors; + for (AActor* Actor : Level->Actors) + { + if (Actor == nullptr || !Actor->IsNetStartupActor()) + { + continue; + } + else if (DeletedNetStartupActors.Contains(Actor->GetFullName())) + { + // Put this actor on the rollback list so we can undelete it during future scrubbing, + // then delete it. + QueueNetStartupActorForRollbackViaDeletion(Actor); + LocalWorld->DestroyActor(Actor, true); + } + else + { + if (RollbackNetStartupActors.Contains(Actor->GetFullName())) + { + LocalWorld->DestroyActor(Actor, true); + } + else + { + StartupActors.Add(Actor); + } + } + } + + TArray SpawnedActors; + RespawnNecessaryNetStartupActors(SpawnedActors, Level); + + for (AActor* Actor : SpawnedActors) + { + StartupActors.Add(Actor); + } + + LocalLevels.Add(Level); + } + + LevelsPendingFastForward.Reset(); + // It's possible that the level we're streaming in may spawn Dynamic Actors. // In that case, we want to make sure we track them so we can process them below. // We only care about the actors if they're outered to the Level. @@ -4702,6 +4859,9 @@ bool UDemoNetDriver::LoadCheckpoint(const FGotoResult& GotoResult) // Clean package map to prepare to restore it to the checkpoint state GuidCache->ResetCacheForDemo(); + // Since we only count the number of sub-spectators, add one more slot for main spectator + // Very small optimization. We do want to clear this so that we don't end up doing during ProcessSeamlessTravel + SpectatorControllers.Empty(CleanUpSplitscreenConnections(true) + 1); SpectatorController = nullptr; ServerConnection->Close(); @@ -4776,6 +4936,21 @@ bool UDemoNetDriver::LoadCheckpoint(const FGotoResult& GotoResult) #if 1 TSet KeepAliveActors; + // Determine if an Actor has a reference to a spectator in some way. + // This prevents garbage collection on splitscreen playercontrollers + auto HasPlayerSpectatorRef = [this](const FActorIterator& InActorIterator) { + for (const APlayerController* CurSpectator : SpectatorControllers) + { + if (*InActorIterator == CurSpectator || *InActorIterator == CurSpectator->GetSpectatorPawn() + || InActorIterator->GetOwner() == CurSpectator) + { + return true; + } + } + return false; + }; + + // Destroy all non startup actors. They will get restored with the checkpoint for ( FActorIterator It( GetWorld() ); It; ++It ) { @@ -4789,7 +4964,7 @@ bool UDemoNetDriver::LoadCheckpoint(const FGotoResult& GotoResult) AddNonQueuedActorForScrubbing(*It); } - const bool bShouldPreserveForPlayerController = (SpectatorController != nullptr && (*It == SpectatorController || *It == SpectatorController->GetSpectatorPawn() || It->GetOwner() == SpectatorController)); + const bool bShouldPreserveForPlayerController = HasPlayerSpectatorRef(It); const bool bShouldPreserveForRewindability = (It->bReplayRewindable && !It->IsNetStartupActor()); if (bShouldPreserveForPlayerController || bShouldPreserveForRewindability) @@ -4868,6 +5043,8 @@ bool UDemoNetDriver::LoadCheckpoint(const FGotoResult& GotoResult) ExternalDataToObjectMap.Empty(); PlaybackPackets.Empty(); + // Going to be recreating the splitscreen connections, but keep around the player controller. + CleanUpSplitscreenConnections(false); ServerConnection->Close(); ServerConnection->CleanUp(); @@ -4899,6 +5076,15 @@ bool UDemoNetDriver::LoadCheckpoint(const FGotoResult& GotoResult) // Create fake control channel CreateInitialClientChannels(); + // Respawn child connections as the parent connection has been recreated. + for (APlayerController* CurController : SpectatorControllers) + { + if (CurController != SpectatorController) + { + RestoreConnectionPostScrub(CurController, CreateChild(ServerConnection)); + } + } + // Catch a rare case where the spectator controller is null, but a valid GUID is // found on the GuidCache. The weak pointers in the NetGUIDLookup map are probably // going null, and we want catch these cases and investigate further. @@ -5321,6 +5507,30 @@ bool UDemoNetDriver::ComparePropertyState(const FDemoSavedPropertyState& State) return bWasDifferent; } +void UDemoNetDriver::RestoreConnectionPostScrub(APlayerController* PC, UNetConnection* NetConnection) +{ + check(NetConnection != nullptr); + check(PC != nullptr); + + PC->Role = ROLE_AutonomousProxy; + PC->NetConnection = NetConnection; + NetConnection->LastReceiveTime = Time; + NetConnection->LastReceiveRealtime = FPlatformTime::Seconds(); + NetConnection->LastGoodPacketRealtime = FPlatformTime::Seconds(); + NetConnection->State = USOCK_Open; + NetConnection->PlayerController = PC; + NetConnection->OwningActor = PC; +} + +void UDemoNetDriver::SetSpectatorController(APlayerController* PC) +{ + SpectatorController = PC; + if (PC != nullptr) + { + SpectatorControllers.AddUnique(PC); + } +} + /*----------------------------------------------------------------------------- UDemoNetConnection. -----------------------------------------------------------------------------*/ @@ -5411,39 +5621,39 @@ void UDemoNetConnection::FlushNet( bool bIgnoreSimulation ) void UDemoNetConnection::HandleClientPlayer( APlayerController* PC, UNetConnection* NetConnection ) { + UDemoNetDriver* DemoDriver = GetDriver(); // If the spectator is the same, assume this is for scrubbing, and we are keeping the old one // (so don't set the position, since we want to persist all that) - if ( GetDriver()->SpectatorController == PC ) + if (DemoDriver->SpectatorController == PC ) { - PC->Role = ROLE_AutonomousProxy; - PC->NetConnection = NetConnection; - LastReceiveTime = Driver->Time; - LastReceiveRealtime = FPlatformTime::Seconds(); - LastGoodPacketRealtime = FPlatformTime::Seconds(); - State = USOCK_Open; - PlayerController = PC; - OwningActor = PC; + DemoDriver->RestoreConnectionPostScrub(PC, NetConnection); + DemoDriver->SetSpectatorController(PC); return; } ULocalPlayer* LocalPlayer = nullptr; - for (FLocalPlayerIterator It(GEngine, Driver->GetWorld()); It; ++It) + uint8 PlayerIndex = 0; + // Attempt to find the player that doesn't already have a connection. + for (FLocalPlayerIterator It(GEngine, Driver->GetWorld()); It; ++It, PlayerIndex++) { - LocalPlayer = *It; - break; + if (PC->NetPlayerIndex == PlayerIndex) + { + LocalPlayer = *It; + break; + } } int32 SavedNetSpeed = LocalPlayer ? LocalPlayer->CurrentNetSpeed : 0; Super::HandleClientPlayer( PC, NetConnection ); // Restore the netspeed if we're a local replay - if (GetDriver()->bIsLocalReplay && LocalPlayer) + if (DemoDriver->bIsLocalReplay && LocalPlayer) { LocalPlayer->CurrentNetSpeed = SavedNetSpeed; } - // Assume this is our special spectator controller - GetDriver()->SpectatorController = PC; + // This is very likely our main demo controller. + DemoDriver->SetSpectatorController(PC); for ( FActorIterator It( Driver->World ); It; ++It) { @@ -5572,6 +5782,32 @@ void UDemoNetDriver::OnSeamlessTravelStartDuringRecording(const FString& LevelNa ReplayStreamer->RefreshHeader(); } +void UDemoNetDriver::InitDestroyedStartupActors() +{ + Super::InitDestroyedStartupActors(); + + if (World) + { + check(DeletedNetStartupActors.Num() == 0); + check(DeltaDeletedNetStartupActors.Num() == 0); + + // add startup actors destroyed before the creation of this net driver + for (auto LevelIt(World->GetLevelIterator()); LevelIt; ++LevelIt) + { + ULevel* Level = *LevelIt; + if (Level) + { + const TArray& DestroyedReplicatedStaticActors = Level->GetDestroyedReplicatedStaticActors(); + for(const FReplicatedStaticActorDestructionInfo& Info : DestroyedReplicatedStaticActors) + { + DeletedNetStartupActors.Add(Info.FullName); + DeltaDeletedNetStartupActors.Add(Info.FullName); + } + } + } + } +} + void UDemoNetDriver::NotifyActorDestroyed( AActor* Actor, bool IsSeamlessTravel ) { check( Actor != nullptr ); diff --git a/Engine/Source/Runtime/Engine/Private/DialogueWave.cpp b/Engine/Source/Runtime/Engine/Private/DialogueWave.cpp index 8f1b75b6b908..49416ccf6e38 100644 --- a/Engine/Source/Runtime/Engine/Private/DialogueWave.cpp +++ b/Engine/Source/Runtime/Engine/Private/DialogueWave.cpp @@ -453,6 +453,9 @@ void UDialogueSoundWaveProxy::Parse(class FAudioDevice* AudioDevice, const UPTRI int OldWaveInstanceCount = WaveInstances.Num(); bool bHasSubtitles = (Subtitles.Num() > 0); + // Check if the wave instance exists before we try to add it... + const bool bWaveInstanceAlreadyExisted = (ActiveSound.WaveInstances.Contains(NodeWaveInstanceHash)); + ActiveSound.bHasExternalSubtitles = bHasSubtitles; // Need to set this so the sound will virtualize when silent if necessary. SoundWave->Parse(AudioDevice, NodeWaveInstanceHash, ActiveSound, ParseParams, WaveInstances); int NewWaveInstanceCount = WaveInstances.Num(); @@ -464,9 +467,8 @@ void UDialogueSoundWaveProxy::Parse(class FAudioDevice* AudioDevice, const UPTRI } // Only Queue subtitles once per each playback of a wave instance - if (NewWaveInstance != nullptr && NewWaveInstance != CurrentWaveInstance) + if (NewWaveInstance != nullptr && !bWaveInstanceAlreadyExisted) { - CurrentWaveInstance = NewWaveInstance; // Add in the subtitle if they exist if (ActiveSound.bHandleSubtitles && bHasSubtitles) { @@ -474,7 +476,7 @@ void UDialogueSoundWaveProxy::Parse(class FAudioDevice* AudioDevice, const UPTRI { QueueSubtitleParams.AudioComponentID = ActiveSound.GetAudioComponentID(); QueueSubtitleParams.WorldPtr = ActiveSound.GetWeakWorld(); - QueueSubtitleParams.WaveInstance = (PTRINT)CurrentWaveInstance; + QueueSubtitleParams.WaveInstance = (PTRINT)NewWaveInstance; QueueSubtitleParams.SubtitlePriority = ActiveSound.SubtitlePriority; QueueSubtitleParams.Duration = GetDuration(); QueueSubtitleParams.bManualWordWrap = false; diff --git a/Engine/Source/Runtime/Engine/Private/GameEngine.cpp b/Engine/Source/Runtime/Engine/Private/GameEngine.cpp index a2e4b34200c3..3a438183c223 100644 --- a/Engine/Source/Runtime/Engine/Private/GameEngine.cpp +++ b/Engine/Source/Runtime/Engine/Private/GameEngine.cpp @@ -1102,7 +1102,7 @@ void UGameEngine::PreExit() UWorld* const World = WorldList[WorldIndex].World(); if ( World != NULL ) { - World->bIsTearingDown = true; + World->BeginTearingDown(); // Cancel any pending connection to a server CancelPending(World); diff --git a/Engine/Source/Runtime/Engine/Private/GameInstance.cpp b/Engine/Source/Runtime/Engine/Private/GameInstance.cpp index 24fa8c077974..52bda3a959aa 100644 --- a/Engine/Source/Runtime/Engine/Private/GameInstance.cpp +++ b/Engine/Source/Runtime/Engine/Private/GameInstance.cpp @@ -641,15 +641,25 @@ ULocalPlayer* UGameInstance::CreateLocalPlayer(int32 ControllerId, FString& OutE NewPlayer = NewObject(GetEngine(), GetEngine()->LocalPlayerClass); InsertIndex = AddLocalPlayer(NewPlayer, ControllerId); - if (bSpawnPlayerController && InsertIndex != INDEX_NONE && GetWorld() != NULL) + UWorld* CurrentWorld = GetWorld(); + if (bSpawnPlayerController && InsertIndex != INDEX_NONE && CurrentWorld != nullptr) { - if (GetWorld()->GetNetMode() != NM_Client) + if (CurrentWorld->GetNetMode() != NM_Client) { // server; spawn a new PlayerController immediately - if (!NewPlayer->SpawnPlayActor("", OutError, GetWorld())) + if (!NewPlayer->SpawnPlayActor("", OutError, CurrentWorld)) { RemoveLocalPlayer(NewPlayer); - NewPlayer = NULL; + NewPlayer = nullptr; + } + } + else if (CurrentWorld->IsPlayingReplay()) + { + // demo playback; ask the replay driver to spawn a splitscreen client + if (!CurrentWorld->DemoNetDriver->SpawnSplitscreenViewer(NewPlayer, CurrentWorld)) + { + RemoveLocalPlayer(NewPlayer); + NewPlayer = nullptr; } } else @@ -699,15 +709,29 @@ int32 UGameInstance::AddLocalPlayer(ULocalPlayer* NewLocalPlayer, int32 Controll bool UGameInstance::RemoveLocalPlayer(ULocalPlayer* ExistingPlayer) { // FIXME: Notify server we want to leave the game if this is an online game - if (ExistingPlayer->PlayerController != NULL) + if (ExistingPlayer->PlayerController != nullptr) { + bool bShouldRemovePlayer = (ExistingPlayer->PlayerController->Role == ROLE_Authority); + // FIXME: Do this all inside PlayerRemoved? ExistingPlayer->PlayerController->CleanupGameViewport(); - // Destroy the player's actors. - if ( ExistingPlayer->PlayerController->Role == ROLE_Authority ) + UWorld* CurrentWorld = GetWorld(); + if (CurrentWorld && CurrentWorld->DemoNetDriver && CurrentWorld->IsPlayingReplay()) { - ExistingPlayer->PlayerController->Destroy(); + if (!CurrentWorld->DemoNetDriver->RemoveSplitscreenViewer(ExistingPlayer->PlayerController)) + { + UE_LOG(LogPlayerManagement, Warning, TEXT("UGameInstance::RemovePlayer: Did not remove player %s with ControllerId %i as it was unable to be removed from the demo"), + *ExistingPlayer->GetName(), ExistingPlayer->GetControllerId()); + } + bShouldRemovePlayer = true; + } + + // Destroy the player's actors. + if (bShouldRemovePlayer) + { + // This is fine to forceremove as we have to be in a special case for demos or the authority + ExistingPlayer->PlayerController->Destroy(true); } } @@ -720,7 +744,7 @@ bool UGameInstance::RemoveLocalPlayer(ULocalPlayer* ExistingPlayer) LocalPlayers.RemoveAt(OldIndex); // Notify the viewport so the viewport can do the fixups, resize, etc - if (GetGameViewportClient() != NULL) + if (GetGameViewportClient() != nullptr) { GetGameViewportClient()->NotifyPlayerRemoved(OldIndex, ExistingPlayer); } @@ -728,7 +752,7 @@ bool UGameInstance::RemoveLocalPlayer(ULocalPlayer* ExistingPlayer) // Disassociate this viewport client from the player. // Do this after notifications, as some of them require the ViewportClient. - ExistingPlayer->ViewportClient = NULL; + ExistingPlayer->ViewportClient = nullptr; UE_LOG(LogPlayerManagement, Log, TEXT("UGameInstance::RemovePlayer: Removed player %s with ControllerId %i at index %i (%i remaining players)"), *ExistingPlayer->GetName(), ExistingPlayer->GetControllerId(), OldIndex, LocalPlayers.Num()); diff --git a/Engine/Source/Runtime/Engine/Private/GameNetworkManager.cpp b/Engine/Source/Runtime/Engine/Private/GameNetworkManager.cpp index ce44ff1b62c1..35d44e83a6ed 100644 --- a/Engine/Source/Runtime/Engine/Private/GameNetworkManager.cpp +++ b/Engine/Source/Runtime/Engine/Private/GameNetworkManager.cpp @@ -28,6 +28,8 @@ AGameNetworkManager::AGameNetworkManager(const FObjectInitializer& ObjectInitial CLIENTADJUSTUPDATECOST = 180.0f; MAXCLIENTUPDATEINTERVAL = 0.25f; MaxClientForcedUpdateDuration=1.0f; + ServerForcedUpdateHitchThreshold = 0.150f; + ServerForcedUpdateHitchCooldown = 0.100f; MaxMoveDeltaTime = 0.125f; MaxClientSmoothingDeltaTime = 0.50f; ClientNetSendMoveDeltaTime = 0.0166f; diff --git a/Engine/Source/Runtime/Engine/Private/GameViewportClient.cpp b/Engine/Source/Runtime/Engine/Private/GameViewportClient.cpp index 3b710c4a49a5..600f7e605807 100644 --- a/Engine/Source/Runtime/Engine/Private/GameViewportClient.cpp +++ b/Engine/Source/Runtime/Engine/Private/GameViewportClient.cpp @@ -834,17 +834,13 @@ EMouseCursor::Type UGameViewportClient::GetCursor(FViewport* InViewport, int32 X void UGameViewportClient::SetVirtualCursorWidget(EMouseCursor::Type Cursor, UUserWidget* UserWidget) { - if (ensure(UserWidget)) + TSharedPtr& ExistingWidget = CursorWidgets.FindOrAdd(Cursor); + TSharedPtr NewWidget = UserWidget ? UserWidget->TakeWidget() : TSharedPtr(); + if (NewWidget != ExistingWidget) { - if (CursorWidgets.Contains(Cursor)) - { - TSharedRef* Widget = CursorWidgets.Find(Cursor); - (*Widget) = UserWidget->TakeWidget(); - } - else - { - CursorWidgets.Add(Cursor, UserWidget->TakeWidget()); - } + // Pure safety + ExistingWidget.Reset(); + ExistingWidget = NewWidget; } } @@ -852,8 +848,7 @@ void UGameViewportClient::AddSoftwareCursor(EMouseCursor::Type Cursor, const FSo { if (CursorClass.IsValid()) { - UClass* Class = CursorClass.TryLoadClass(); - if (Class) + if (UClass* Class = CursorClass.TryLoadClass()) { UUserWidget* UserWidget = CreateWidget(GetGameInstance(), Class); AddCursorWidget(Cursor, UserWidget); @@ -888,18 +883,19 @@ TOptional> UGameViewportClient::MapCursor(FViewport* InViewp { if (CursorReply.GetCursorType() != EMouseCursor::None) { - const TSharedRef* CursorWidgetPtr = CursorWidgets.Find(CursorReply.GetCursorType()); + const TSharedPtr CursorWidgetPtr = CursorWidgets.FindRef(CursorReply.GetCursorType()); - if (CursorWidgetPtr != nullptr) + if (CursorWidgetPtr.IsValid()) { - return *CursorWidgetPtr; + return CursorWidgetPtr.ToSharedRef(); } else { - UE_LOG(LogPlayerManagement, Warning, TEXT("UGameViewportClient::MapCursor: Could not find cursor to map to %d."),int32(CursorReply.GetCursorType())); + UE_LOG(LogPlayerManagement, Warning, TEXT("UGameViewportClient::MapCursor: Could not find cursor to map to %d."), int32(CursorReply.GetCursorType())); } } } + return TOptional>(); } @@ -1104,6 +1100,14 @@ void UGameViewportClient::Draw(FViewport* InViewport, FCanvas* SceneCanvas) UWorld* MyWorld = GetWorld(); + // Force path tracing view mode, and extern code set path tracer show flags + const bool bForcePathTracing = InViewport->GetClient()->GetEngineShowFlags()->PathTracing; + if (bForcePathTracing) + { + EngineShowFlags.SetPathTracing(true); + ViewModeIndex = VMI_PathTracing; + } + // create the view family for rendering the world scene to the viewport's render target FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues( InViewport, @@ -1117,6 +1121,14 @@ void UGameViewportClient::Draw(FViewport* InViewport, FCanvas* SceneCanvas) // Force enable view family show flag for HighDPI derived's screen percentage. ViewFamily.EngineShowFlags.ScreenPercentage = true; } + if (ViewFamily.GetDebugViewShaderMode() != DVSM_None && HasMissingDebugViewModeShaders(true)) + { + TSet Materials; + if (GetUsedMaterialsInWorld(MyWorld, Materials, nullptr)) + { + CompileDebugViewModeShaders(ViewFamily.GetDebugViewShaderMode(), GetCachedScalabilityCVars().MaterialQualityLevel, ViewFamily.GetFeatureLevel(), false, false, Materials, nullptr); + } + } #endif ViewFamily.ViewExtensions = GEngine->ViewExtensions->GatherActiveExtensions(InViewport); @@ -1299,6 +1311,9 @@ void UGameViewportClient::Draw(FViewport* InViewport, FCanvas* SceneCanvas) } } + #if RHI_RAYTRACING + View->SetupRayTracedRendering(); + #endif } // Add view information for resource streaming. Allow up to 5X boost for small FOV. diff --git a/Engine/Source/Runtime/Engine/Private/GameplayStatics.cpp b/Engine/Source/Runtime/Engine/Private/GameplayStatics.cpp index 35b8113fd908..925dd8b665f1 100644 --- a/Engine/Source/Runtime/Engine/Private/GameplayStatics.cpp +++ b/Engine/Source/Runtime/Engine/Private/GameplayStatics.cpp @@ -1167,7 +1167,12 @@ void UGameplayStatics::PlaySound2D(const UObject* WorldContextObject, class USou NewActiveSound.bIsUISound = true; NewActiveSound.bAllowSpatialization = false; - NewActiveSound.ConcurrencySet.Add(ConcurrencySettings); + + if (ConcurrencySettings) + { + NewActiveSound.ConcurrencySet.Add(ConcurrencySettings); + } + NewActiveSound.Priority = Sound->Priority; NewActiveSound.SubtitlePriority = Sound->GetSubtitlePriority(); @@ -1328,7 +1333,11 @@ UAudioComponent* UGameplayStatics::SpawnSoundAttached(USoundBase* Sound, USceneC Params.SetLocation(TestLocation); Params.bStopWhenOwnerDestroyed = bStopWhenAttachedToDestroyed; Params.AttenuationSettings = AttenuationSettings; - Params.ConcurrencySet.Add(ConcurrencySettings); + + if (ConcurrencySettings) + { + Params.ConcurrencySet.Add(ConcurrencySettings); + } UAudioComponent* AudioComponent = FAudioDevice::CreateComponent(Sound, Params); if (AudioComponent) diff --git a/Engine/Source/Runtime/Engine/Private/ImageUtils.cpp b/Engine/Source/Runtime/Engine/Private/ImageUtils.cpp index 901e15126354..7b812c86fa57 100644 --- a/Engine/Source/Runtime/Engine/Private/ImageUtils.cpp +++ b/Engine/Source/Runtime/Engine/Private/ImageUtils.cpp @@ -61,11 +61,30 @@ static bool GetRawData(UTextureRenderTarget2D* TexRT, TArray& RawData) * @param DstData Destination image data. * @param bLinearSpace If true, convert colors into linear space before interpolating (slower but more accurate) */ -void FImageUtils::ImageResize(int32 SrcWidth, int32 SrcHeight, const TArray &SrcData, int32 DstWidth, int32 DstHeight, TArray &DstData, bool bLinearSpace ) +void FImageUtils::ImageResize(int32 SrcWidth, int32 SrcHeight, const TArray &SrcData, int32 DstWidth, int32 DstHeight, TArray &DstData, bool bLinearSpace) { DstData.Empty(DstWidth*DstHeight); DstData.AddZeroed(DstWidth*DstHeight); + ImageResize(SrcWidth, SrcHeight, TArrayView(SrcData), DstWidth, DstHeight, TArrayView(DstData), bLinearSpace); +} + +/** + * Resizes the given image using a simple average filter and stores it in the destination array. This version constrains aspect ratio. + * Accepts TArrayViews but requires that DstData be pre-sized appropriately + * + * @param SrcWidth Source image width. + * @param SrcHeight Source image height. + * @param SrcData Source image data. + * @param DstWidth Destination image width. + * @param DstHeight Destination image height. + * @param DstData Destination image data. (must already be sized to DstWidth*DstHeight) + */ +void FImageUtils::ImageResize(int32 SrcWidth, int32 SrcHeight, const TArrayView &SrcData, int32 DstWidth, int32 DstHeight, const TArrayView &DstData, bool bLinearSpace) +{ + check(SrcData.Num() >= SrcWidth * SrcHeight); + check(DstData.Num() >= DstWidth * DstHeight); + float SrcX = 0; float SrcY = 0; diff --git a/Engine/Source/Runtime/Engine/Private/InheritableComponentHandler.cpp b/Engine/Source/Runtime/Engine/Private/InheritableComponentHandler.cpp index c9bc5b9e44f0..38f4812a1891 100644 --- a/Engine/Source/Runtime/Engine/Private/InheritableComponentHandler.cpp +++ b/Engine/Source/Runtime/Engine/Private/InheritableComponentHandler.cpp @@ -113,7 +113,24 @@ UActorComponent* UInheritableComponentHandler::CreateOverridenComponentTemplate( } ensure(Cast(GetOuter())); - auto NewComponentTemplate = NewObject( + + // If we find an existing object with our name that the object recycling system won't allow for we need to deal with it + // or else the NewObject call below will fatally assert + UObject* ExistingObj = FindObjectFast(GetOuter(), NewComponentTemplateName); + if (ExistingObj && !ExistingObj->GetClass()->IsChildOf(BestArchetype->GetClass())) + { + // If this isn't an unnecessary component there is something else we need to investigate + // but if it is, just consign it to oblivion as its purpose is no longer required with the allocation + // of an object of the same name + UActorComponent* ExistingComp = Cast(ExistingObj); + if (ensure(ExistingComp) && ensure(UnnecessaryComponents.RemoveSwap(ExistingComp) > 0)) + { + ExistingObj->Rename(nullptr, GetTransientPackage(), REN_DontCreateRedirectors | REN_ForceNoResetLoaders); + ExistingObj->MarkPendingKill(); + } + } + + UActorComponent* NewComponentTemplate = NewObject( GetOuter(), BestArchetype->GetClass(), NewComponentTemplateName, RF_ArchetypeObject | RF_Public | RF_InheritableComponentTemplate, BestArchetype); // HACK: NewObject can return a pre-existing object which will not have been initialized to the archetype. When we remove the old handlers, we mark them pending @@ -128,14 +145,14 @@ UActorComponent* UInheritableComponentHandler::CreateOverridenComponentTemplate( // Clear transient flag if it was transient before and re copy off archetype if (NewComponentTemplate->HasAnyFlags(RF_Transient) && UnnecessaryComponents.Contains(NewComponentTemplate)) - { - NewComponentTemplate->ClearFlags(RF_Transient); + { + NewComponentTemplate->ClearFlags(RF_Transient); UnnecessaryComponents.Remove(NewComponentTemplate); - UEngine::FCopyPropertiesForUnrelatedObjectsParams CopyParams; - CopyParams.bDoDelta = false; - UEngine::CopyPropertiesForUnrelatedObjects(BestArchetype, NewComponentTemplate, CopyParams); - } + UEngine::FCopyPropertiesForUnrelatedObjectsParams CopyParams; + CopyParams.bDoDelta = false; + UEngine::CopyPropertiesForUnrelatedObjects(BestArchetype, NewComponentTemplate, CopyParams); + } FComponentOverrideRecord NewRecord; NewRecord.ComponentKey = Key; diff --git a/Engine/Source/Runtime/Engine/Private/InstancedStaticMesh.cpp b/Engine/Source/Runtime/Engine/Private/InstancedStaticMesh.cpp index ba20d329e423..27b1cc039184 100644 --- a/Engine/Source/Runtime/Engine/Private/InstancedStaticMesh.cpp +++ b/Engine/Source/Runtime/Engine/Private/InstancedStaticMesh.cpp @@ -2168,8 +2168,8 @@ void UInstancedStaticMeshComponent::GetInstancesMinMaxScale(FVector& MinScale, F } else { - MinScale = FVector(0); - MaxScale = FVector(0); + MinScale = FVector(1.0f); + MaxScale = FVector(1.0f); } } diff --git a/Engine/Source/Runtime/Engine/Private/Level.cpp b/Engine/Source/Runtime/Engine/Private/Level.cpp index baf2801c6fa4..4b9f4caf2b17 100644 --- a/Engine/Source/Runtime/Engine/Private/Level.cpp +++ b/Engine/Source/Runtime/Engine/Private/Level.cpp @@ -431,6 +431,7 @@ void ULevel::CreateReplicatedDestructionInfo(AActor* const Actor) { FReplicatedStaticActorDestructionInfo NewInfo; NewInfo.PathName = Actor->GetFName(); + NewInfo.FullName = Actor->GetFullName(); NewInfo.DestroyedPosition = Actor->GetActorLocation(); NewInfo.ObjOuter = Actor->GetOuter(); NewInfo.ObjClass = Actor->GetClass(); diff --git a/Engine/Source/Runtime/Engine/Private/LevelActor.cpp b/Engine/Source/Runtime/Engine/Private/LevelActor.cpp index 12dab570a6d1..eb5b61b8b766 100644 --- a/Engine/Source/Runtime/Engine/Private/LevelActor.cpp +++ b/Engine/Source/Runtime/Engine/Private/LevelActor.cpp @@ -30,11 +30,13 @@ #include "ContentStreaming.h" #include "EditorSupportDelegates.h" #include "GameFramework/GameModeBase.h" -#include "Engine/DemoNetDriver.h" #include "AudioDeviceManager.h" #include "Logging/TokenizedMessage.h" #include "Logging/MessageLog.h" #include "Misc/MapErrors.h" +#include "GameFramework/WorldSettings.h" +#include "Engine/NetDriver.h" +#include "Engine/Player.h" #include "Components/BoxComponent.h" #include "GameFramework/MovementComponent.h" @@ -674,12 +676,14 @@ bool UWorld::DestroyActor( AActor* ThisActor, bool bNetForce, bool bShouldModify } // Notify net drivers that this guy has been destroyed. - if (GEngine->GetWorldContextFromWorld(this)) + if (FWorldContext* Context = GEngine->GetWorldContextFromWorld(this)) { - UNetDriver* ActorNetDriver = GEngine->FindNamedNetDriver(this,ThisActor->GetNetDriverName()); - if (ActorNetDriver) + for (FNamedNetDriver& Driver : Context->ActiveNetDrivers) { - ActorNetDriver->NotifyActorDestroyed(ThisActor); + if (Driver.NetDriver != nullptr && Driver.NetDriver->ShouldReplicateActor(ThisActor)) + { + Driver.NetDriver->NotifyActorDestroyed(ThisActor); + } } } else if (WorldType != EWorldType::Inactive && !IsRunningCommandlet()) @@ -689,11 +693,6 @@ bool UWorld::DestroyActor( AActor* ThisActor, bool bNetForce, bool bShouldModify UE_LOG(LogSpawn, Warning, TEXT("UWorld::DestroyActor: World has no context! World: %s, Actor: %s"), *GetName(), *ThisActor->GetPathName()); } - if ( DemoNetDriver ) - { - DemoNetDriver->NotifyActorDestroyed( ThisActor ); - } - // Remove the actor from the actor list. RemoveActor( ThisActor, bShouldModifyLevel ); diff --git a/Engine/Source/Runtime/Engine/Private/Materials/HLSLMaterialTranslator.h b/Engine/Source/Runtime/Engine/Private/Materials/HLSLMaterialTranslator.h index f5c00ea503a3..31153e4947c5 100644 --- a/Engine/Source/Runtime/Engine/Private/Materials/HLSLMaterialTranslator.h +++ b/Engine/Source/Runtime/Engine/Private/Materials/HLSLMaterialTranslator.h @@ -3179,6 +3179,11 @@ protected: return GetPrimitiveProperty(MCT_Float3, TEXT("ObjectBounds"), TEXT("ObjectBounds.xyz")); } + virtual int32 PreSkinnedLocalBounds() override + { + return GetPrimitiveProperty(MCT_Float3, TEXT("PreSkinnedLocalBounds"), TEXT("PreSkinnedLocalBounds.xyz")); + } + virtual int32 DistanceCullFade() override { bUsesDistanceCullFade = true; @@ -3803,11 +3808,6 @@ protected: // SceneColor.A channel holds depth till BeforeTonemapping location, then it's gets overwritten return Errorf(TEXT("SceneDepth lookups are only available when BlendableLocation is BeforeTranslucency or BeforeTonemapping")); } - - if (ViewportUV == INDEX_NONE) - { - UV = TextureCoordinate(0, false, false); - } } return AddCodeChunk(MCT_Float4, TEXT("MobileSceneTextureLookup(Parameters, %d, %s)"), (int32)SceneTextureId, *CoerceParameter(UV, MCT_Float2)); diff --git a/Engine/Source/Runtime/Engine/Private/Materials/Material.cpp b/Engine/Source/Runtime/Engine/Private/Materials/Material.cpp index 37c7ab52a5b6..5a2faede5b21 100644 --- a/Engine/Source/Runtime/Engine/Private/Materials/Material.cpp +++ b/Engine/Source/Runtime/Engine/Private/Materials/Material.cpp @@ -3063,7 +3063,10 @@ void UMaterial::CacheResourceShadersForRendering(bool bRegenerateId) FlushResourceShaderMaps(); } - UpdateResourceAllocations(); + // Resources cannot be deleted before uniform expressions are recached because + // UB layouts will be accessed and they are owned by material resources + FMaterialResourceDeferredDeletionArray ResourcesToFree; + UpdateResourceAllocations(&ResourcesToFree); if (FApp::CanEverRender()) { @@ -3118,7 +3121,21 @@ void UMaterial::CacheResourceShadersForRendering(bool bRegenerateId) } } - RecacheUniformExpressions(true); + // Material reload can call this function again. Don't recreate since mesh + // draw commands cache a pointer to the uniform buffer + RecacheUniformExpressions(!FPlatformProperties::RequiresCookedData()); + } + + if (ResourcesToFree.Num()) + { + ENQUEUE_RENDER_COMMAND(CmdFreeUnusedMaterialResources)( + [ResourcesToFreeRT = MoveTemp(ResourcesToFree)](FRHICommandList&) + { + for (int32 Idx = 0; Idx < ResourcesToFreeRT.Num(); ++Idx) + { + delete ResourcesToFreeRT[Idx]; + } + }); } } @@ -3697,7 +3714,7 @@ void UMaterial::GetQualityLevelNodeUsage(TArrayAdd(Resource); + } + else + { + delete Resource; + } Resource = nullptr; } else @@ -3956,14 +3980,6 @@ void UMaterial::PostLoad() { if (Texture) { - if (Texture->HasAnyFlags(RF_NeedLoad)) - { - FLinkerLoad* Loader = GetLinker(); - if (ensure(Loader)) - { - Loader->Preload(Texture); - } - } Texture->ConditionalPostLoad(); } } @@ -4112,6 +4128,9 @@ bool UMaterial::IsCachedCookedPlatformDataLoaded( const ITargetPlatform* TargetP void UMaterial::ClearCachedCookedPlatformData( const ITargetPlatform *TargetPlatform ) { + // Make sure that all CacheShaders render thead commands are finished before we destroy FMaterialResources. + FlushRenderingCommands(); + TArray *CachedMaterialResourcesForPlatform = CachedMaterialResourcesForCooking.Find( TargetPlatform ); if ( CachedMaterialResourcesForPlatform != NULL ) { @@ -4125,6 +4144,9 @@ void UMaterial::ClearCachedCookedPlatformData( const ITargetPlatform *TargetPlat void UMaterial::ClearAllCachedCookedPlatformData() { + // Make sure that all CacheShaders render thead commands are finished before we destroy FMaterialResources. + FlushRenderingCommands(); + for ( auto It : CachedMaterialResourcesForCooking ) { TArray &CachedMaterialResourcesForPlatform = It.Value; @@ -4288,9 +4310,6 @@ bool UMaterial::CanEditChange(const UProperty* InProperty) const void UMaterial::PreEditChange(UProperty* PropertyThatChanged) { - // In Editor realtime rendering changing a material connection (or via undo/redo) changes what is rendered on the RT (specifically in FMeshDecalMeshProcessor::AddMeshBatch() Emmissive connection test) - // before the Shader Map has been updated in PostEditChangeProperty() below - Viewport render command enqueue has already happenned so we need to flush that before the material change. - FlushRenderingCommands(); Super::PreEditChange(PropertyThatChanged); } diff --git a/Engine/Source/Runtime/Engine/Private/Materials/MaterialExpressions.cpp b/Engine/Source/Runtime/Engine/Private/Materials/MaterialExpressions.cpp index 9c641e51502b..3faabad22026 100644 --- a/Engine/Source/Runtime/Engine/Private/Materials/MaterialExpressions.cpp +++ b/Engine/Source/Runtime/Engine/Private/Materials/MaterialExpressions.cpp @@ -123,6 +123,7 @@ #include "Materials/MaterialExpressionOneMinus.h" #include "Materials/MaterialExpressionPanner.h" #include "Materials/MaterialExpressionParameter.h" +#include "Materials/MaterialExpressionPreSkinnedLocalBounds.h" #include "Materials/MaterialExpressionPreviousFrameSwitch.h" #include "Materials/MaterialExpressionReroute.h" #include "Materials/MaterialExpressionScalarParameter.h" @@ -9444,6 +9445,47 @@ void UMaterialExpressionObjectBounds::GetCaption(TArray& OutCaptions) c } #endif // WITH_EDITOR +/////////////////////////////////////////////////////////////////////////////// +// UMaterialExpressionPreSkinnedLocalBounds +/////////////////////////////////////////////////////////////////////////////// +UMaterialExpressionPreSkinnedLocalBounds::UMaterialExpressionPreSkinnedLocalBounds(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ +#if WITH_EDITORONLY_DATA + // Structure to hold one-time initialization + struct FConstructorStatics + { + FText NAME_Vectors; + FConstructorStatics() + : NAME_Vectors(LOCTEXT("Vectors", "Vectors")) + { + } + }; + static FConstructorStatics ConstructorStatics; + + MenuCategories.Add(ConstructorStatics.NAME_Vectors); + + bShaderInputData = true; +#endif +} + +#if WITH_EDITOR +int32 UMaterialExpressionPreSkinnedLocalBounds::Compile(class FMaterialCompiler* Compiler, int32 OutputIndex) +{ + if (Material && Material->MaterialDomain == MD_DeferredDecal) + { + return CompilerError(Compiler, TEXT("Expression not available in the deferred decal material domain.")); + } + + return Compiler->PreSkinnedLocalBounds(); +} + +void UMaterialExpressionPreSkinnedLocalBounds::GetCaption(TArray& OutCaptions) const +{ + OutCaptions.Add(TEXT("Pre-Skinned Local Bounds")); +} +#endif // WITH_EDITOR + /////////////////////////////////////////////////////////////////////////////// // UMaterialExpressionDistanceCullFade /////////////////////////////////////////////////////////////////////////////// diff --git a/Engine/Source/Runtime/Engine/Private/Materials/MaterialInstance.cpp b/Engine/Source/Runtime/Engine/Private/Materials/MaterialInstance.cpp index 6d50ea8e666f..be3cb9a6a218 100644 --- a/Engine/Source/Runtime/Engine/Private/Materials/MaterialInstance.cpp +++ b/Engine/Source/Runtime/Engine/Private/Materials/MaterialInstance.cpp @@ -311,7 +311,7 @@ void FMaterialInstanceResource::GameThread_SetParent(UMaterialInterface* ParentM [Resource, ParentMaterialInterface](FRHICommandListImmediate& RHICmdList) { Resource->Parent = ParentMaterialInterface; - Resource->InvalidateUniformExpressionCache(false); + Resource->InvalidateUniformExpressionCache(true); }); if (OldParent) @@ -2474,7 +2474,7 @@ FMaterialResource* UMaterialInstance::AllocatePermutationResource() return new FMaterialResource(); } -void UMaterialInstance::UpdatePermutationAllocations() +void UMaterialInstance::UpdatePermutationAllocations(FMaterialResourceDeferredDeletionArray* ResourcesToFree) { if (bHasStaticPermutationResource) { @@ -2494,7 +2494,14 @@ void UMaterialInstance::UpdatePermutationAllocations() FMaterialResource*& StaticPermResource = StaticPermutationMaterialResources[Quality][Feature]; if (Feature != ActiveFeatureLevel || Quality != ActiveQualityLevel) { - delete StaticPermResource; + if (ResourcesToFree) + { + ResourcesToFree->Add(StaticPermResource); + } + else + { + delete StaticPermResource; + } StaticPermResource = nullptr; } else @@ -2535,7 +2542,8 @@ void UMaterialInstance::CacheResourceShadersForRendering() { check(IsInGameThread() || IsAsyncLoading()); - UpdatePermutationAllocations(); + FMaterialResourceDeferredDeletionArray ResourcesToFree; + UpdatePermutationAllocations(&ResourcesToFree); UpdateOverridableBaseProperties(); if (bHasStaticPermutationResource && FApp::CanEverRender()) @@ -2578,6 +2586,18 @@ void UMaterialInstance::CacheResourceShadersForRendering() } InitResources(); + + if (ResourcesToFree.Num()) + { + ENQUEUE_RENDER_COMMAND(CmdFreeMaterialResources)( + [ResourcesToFreeRT = MoveTemp(ResourcesToFree)](FRHICommandList&) + { + for (int32 Idx = 0; Idx < ResourcesToFreeRT.Num(); ++Idx) + { + delete ResourcesToFreeRT[Idx]; + } + }); + } } void UMaterialInstance::CacheResourceShadersForCooking(EShaderPlatform ShaderPlatform, TArray& OutCachedMaterialResources) @@ -2926,6 +2946,9 @@ bool UMaterialInstance::IsCachedCookedPlatformDataLoaded( const ITargetPlatform* void UMaterialInstance::ClearCachedCookedPlatformData( const ITargetPlatform *TargetPlatform ) { + // Make sure that all CacheShaders render thead commands are finished before we destroy FMaterialResources. + FlushRenderingCommands(); + TArray *CachedMaterialResourcesForPlatform = CachedMaterialResourcesForCooking.Find( TargetPlatform ); if ( CachedMaterialResourcesForPlatform != NULL ) { @@ -2940,6 +2963,9 @@ void UMaterialInstance::ClearCachedCookedPlatformData( const ITargetPlatform *Ta void UMaterialInstance::ClearAllCachedCookedPlatformData() { + // Make sure that all CacheShaders render thead commands are finished before we destroy FMaterialResources. + FlushRenderingCommands(); + for ( auto It : CachedMaterialResourcesForCooking ) { TArray &CachedMaterialResourcesForPlatform = It.Value; @@ -3127,14 +3153,6 @@ void UMaterialInstance::PostLoad() UTexture* Texture = TextureParameterValues[ValueIndex].ParameterValue; if( Texture ) { - if (Texture->HasAnyFlags(RF_NeedLoad)) - { - FLinkerLoad* Loader = GetLinker(); - if (ensure(Loader)) - { - Loader->Preload(Texture); - } - } Texture->ConditionalPostLoad(); } } diff --git a/Engine/Source/Runtime/Engine/Private/Materials/MaterialInstanceConstant.cpp b/Engine/Source/Runtime/Engine/Private/Materials/MaterialInstanceConstant.cpp index 1c5e04926342..2e51a65573a7 100644 --- a/Engine/Source/Runtime/Engine/Private/Materials/MaterialInstanceConstant.cpp +++ b/Engine/Source/Runtime/Engine/Private/Materials/MaterialInstanceConstant.cpp @@ -49,10 +49,10 @@ void UMaterialInstanceConstant::PostEditChangeProperty(FPropertyChangedEvent& Pr ParameterStateId = FGuid::NewGuid(); } -void UMaterialInstanceConstant::SetParentEditorOnly(UMaterialInterface* NewParent) +void UMaterialInstanceConstant::SetParentEditorOnly(UMaterialInterface* NewParent, bool RecacheShader) { check(GIsEditor || IsRunningCommandlet()); - SetParentInternal(NewParent, true); + SetParentInternal(NewParent, RecacheShader); } void UMaterialInstanceConstant::CopyMaterialUniformParametersEditorOnly(UMaterialInterface* Source, bool bIncludeStaticParams) diff --git a/Engine/Source/Runtime/Engine/Private/Materials/MaterialShared.cpp b/Engine/Source/Runtime/Engine/Private/Materials/MaterialShared.cpp index 30fe7efa4900..43c66df84bbf 100644 --- a/Engine/Source/Runtime/Engine/Private/Materials/MaterialShared.cpp +++ b/Engine/Source/Runtime/Engine/Private/Materials/MaterialShared.cpp @@ -2068,6 +2068,7 @@ void FMaterialRenderProxy::EvaluateUniformExpressions(FUniformExpressionCache& O { check(OutUniformExpressionCache.UniformBuffer->GetLayout() == UniformBufferStruct.GetLayout()); RHIUpdateUniformBuffer(OutUniformExpressionCache.UniformBuffer, TempBuffer); + OutUniformExpressionCache.UniformBuffer->UpdateLayoutReference(&UniformBufferStruct.GetLayout()); } else { @@ -2779,6 +2780,7 @@ FMaterialUpdateContext::~FMaterialUpdateContext() } } + MI->RecacheUniformExpressions(true); MI->InitStaticPermutation();//bHasStaticPermutation can change. if (MI->bHasStaticPermutationResource) { diff --git a/Engine/Source/Runtime/Engine/Private/NetworkDriver.cpp b/Engine/Source/Runtime/Engine/Private/NetworkDriver.cpp index 536e00b9de22..c4d5d6af77fe 100644 --- a/Engine/Source/Runtime/Engine/Private/NetworkDriver.cpp +++ b/Engine/Source/Runtime/Engine/Private/NetworkDriver.cpp @@ -1699,9 +1699,14 @@ void UNetDriver::PostTickDispatch() ServerConnection->FlushPacketOrderCache(true); } - for (UNetConnection* CurConn : ClientConnections) + TArray ClientConnCopy = ClientConnections; + + for (UNetConnection* CurConn : ClientConnCopy) { - CurConn->FlushPacketOrderCache(true); + if (!CurConn->IsPendingKill()) + { + CurConn->FlushPacketOrderCache(true); + } } if (ReplicationDriver) diff --git a/Engine/Source/Runtime/Engine/Private/Particles/ParticleModules.cpp b/Engine/Source/Runtime/Engine/Private/Particles/ParticleModules.cpp index 38fb4a4d2227..98cb5f8b69df 100644 --- a/Engine/Source/Runtime/Engine/Private/Particles/ParticleModules.cpp +++ b/Engine/Source/Runtime/Engine/Private/Particles/ParticleModules.cpp @@ -1219,14 +1219,6 @@ void UParticleModuleRequired::PostLoad() { if (CutoutTexture) { - if (CutoutTexture->HasAnyFlags(RF_NeedLoad)) - { - FLinkerLoad* Loader = GetLinker(); - if (ensure(Loader)) - { - Loader->Preload(CutoutTexture); - } - } CutoutTexture->ConditionalPostLoad(); CacheDerivedData(); } diff --git a/Engine/Source/Runtime/Engine/Private/PlayerController.cpp b/Engine/Source/Runtime/Engine/Private/PlayerController.cpp index 355196de7a28..1f2f96e353c2 100644 --- a/Engine/Source/Runtime/Engine/Private/PlayerController.cpp +++ b/Engine/Source/Runtime/Engine/Private/PlayerController.cpp @@ -103,6 +103,9 @@ APlayerController::APlayerController(const FObjectInitializer& ObjectInitializer DefaultClickTraceChannel = ECollisionChannel::ECC_Visibility; HitResultTraceDistance = 100000.f; + LastMovementUpdateTime = 0.f; + LastMovementHitch = 0.f; + bCinemaDisableInputMove = false; bCinemaDisableInputLook = false; @@ -4535,9 +4538,15 @@ void APlayerController::TickActor( float DeltaSeconds, ELevelTick TickType, FAct } } + const float CurrentRealTime = World->GetRealTimeSeconds(); + const bool bHitch = (CurrentRealTime - LastMovementUpdateTime) > GameNetworkManager->ServerForcedUpdateHitchThreshold && (LastMovementUpdateTime != 0); + LastMovementHitch = bHitch ? CurrentRealTime : LastMovementHitch; + const bool bRecentHitch = bHitch || (CurrentRealTime - LastMovementHitch < GameNetworkManager->ServerForcedUpdateHitchCooldown); + LastMovementUpdateTime = CurrentRealTime; + // Trigger forced update if allowed - if (ForcedUpdateInterval > 0.f && PawnTimeSinceUpdate > FMath::Max(DeltaSeconds+0.06f, ForcedUpdateInterval * GetPawn()->GetActorTimeDilation())) - { + if (!bRecentHitch && ForcedUpdateInterval > 0.f && PawnTimeSinceUpdate > FMath::Max(DeltaSeconds+0.06f, ForcedUpdateInterval * GetPawn()->GetActorTimeDilation())) + { //UE_LOG(LogPlayerController, Warning, TEXT("ForcedMovementTick. PawnTimeSinceUpdate: %f, DeltaSeconds: %f, DeltaSeconds+: %f"), PawnTimeSinceUpdate, DeltaSeconds, DeltaSeconds+0.06f); const USkeletalMeshComponent* PawnMesh = GetPawn()->FindComponentByClass(); if (!ServerData->bForcedUpdateDurationExceeded && (!PawnMesh || !PawnMesh->IsSimulatingPhysics())) diff --git a/Engine/Source/Runtime/Engine/Private/PrimitiveSceneProxy.cpp b/Engine/Source/Runtime/Engine/Private/PrimitiveSceneProxy.cpp index 78299f57226e..edaff1730cbd 100644 --- a/Engine/Source/Runtime/Engine/Private/PrimitiveSceneProxy.cpp +++ b/Engine/Source/Runtime/Engine/Private/PrimitiveSceneProxy.cpp @@ -277,6 +277,7 @@ void FPrimitiveSceneProxy::UpdateUniformBuffer() ActorPosition, Bounds, LocalBounds, + GetPreSkinnedLocalBounds(), bReceivesDecals, HasDistanceFieldRepresentation(), HasDynamicIndirectShadowCasterRepresentation(), diff --git a/Engine/Source/Runtime/Engine/Private/PrimitiveUniformShaderParameters.cpp b/Engine/Source/Runtime/Engine/Private/PrimitiveUniformShaderParameters.cpp index 78f9cd6e118f..a69555ae936d 100644 --- a/Engine/Source/Runtime/Engine/Private/PrimitiveUniformShaderParameters.cpp +++ b/Engine/Source/Runtime/Engine/Private/PrimitiveUniformShaderParameters.cpp @@ -48,7 +48,8 @@ FPrimitiveSceneShaderData::FPrimitiveSceneShaderData(const FPrimitiveSceneProxy* PreviousLocalToWorld, Proxy->GetActorPosition(), Proxy->GetBounds(), - Proxy->GetLocalBounds(), + Proxy->GetLocalBounds(), + Proxy->GetPreSkinnedLocalBounds(), Proxy->ReceivesDecals(), Proxy->HasDistanceFieldRepresentation(), Proxy->HasDynamicIndirectShadowCasterRepresentation(), @@ -107,4 +108,6 @@ void FPrimitiveSceneShaderData::Setup(const FPrimitiveUniformShaderParameters& P Data[25] = FVector4(0.0f, 0.0f, 0.0f, 0.0f); Data[25].X = *(const float*)&PrimitiveUniformShaderParameters.SingleCaptureIndex; + + Data[26] = FVector4(PrimitiveUniformShaderParameters.PreSkinnedLocalBounds, 0.0f); // .w unused } \ No newline at end of file diff --git a/Engine/Source/Runtime/Engine/Private/Rendering/SkeletalMeshVertexClothBuffer.cpp b/Engine/Source/Runtime/Engine/Private/Rendering/SkeletalMeshVertexClothBuffer.cpp index b1b321d7090b..286fea5e4d7f 100644 --- a/Engine/Source/Runtime/Engine/Private/Rendering/SkeletalMeshVertexClothBuffer.cpp +++ b/Engine/Source/Runtime/Engine/Private/Rendering/SkeletalMeshVertexClothBuffer.cpp @@ -80,6 +80,16 @@ void FSkeletalMeshVertexClothBuffer::InitRHI() } } +/** +* Release the RHI resource for this vertex buffer +*/ +void FSkeletalMeshVertexClothBuffer::ReleaseRHI() +{ + VertexBufferSRV.SafeRelease(); + // call the base class's ReleaseRHI() since we overrode it + FVertexBuffer::ReleaseRHI(); +} + /** * Serializer for this class * @param Ar - archive to serialize to diff --git a/Engine/Source/Runtime/Engine/Private/RepLayout.cpp b/Engine/Source/Runtime/Engine/Private/RepLayout.cpp index 769ac773d408..51ec5ee021b6 100644 --- a/Engine/Source/Runtime/Engine/Private/RepLayout.cpp +++ b/Engine/Source/Runtime/Engine/Private/RepLayout.cpp @@ -58,6 +58,9 @@ static FAutoConsoleVariableRef CVarUsePackedShadowBuffers(TEXT("Net.UsePackedSha int32 GShareShadowState = 1; static FAutoConsoleVariableRef CVarShareShadowState(TEXT("net.ShareShadowState"), GShareShadowState, TEXT("If true, work done to compare properties will be shared across connections")); +int32 GShareInitialCompareState = 0; +static FAutoConsoleVariableRef CVarShareInitialCompareState(TEXT("net.ShareInitialCompareState"), GShareInitialCompareState, TEXT("If true and net.ShareShadowState is enabled, attempt to also share initial replication compares across connections.")); + int32 MaxRepArraySize = UNetworkSettings::DefaultMaxRepArraySize; int32 MaxRepArrayMemory = UNetworkSettings::DefaultMaxRepArrayMemory; @@ -150,20 +153,12 @@ namespace UE4_RepLayout_Private // TODO: Conditionally compilable runtime type validation. return reinterpret_cast((Buffer + Cmd).Data); } - - template - FORCEINLINE static FFastArraySerializerItem* GetFastArrayItem(FFastArrayDeltaSerializeParams& Params, const BufferType& Buffer, const CommandType& Cmd) - { - return (FFastArraySerializerItem*)(Params.PatchedItemOffset + (PTRINT)(GetTypedProperty(Buffer, Cmd))); - } - - template - FORCEINLINE static FFastArraySerializer* GetFastArray(FFastArrayDeltaSerializeParams& Params, const BufferType& Buffer, const CommandType& Cmd) - { - return (FFastArraySerializer*)(Params.PatchedArrayOffset + (PTRINT)(GetTypedProperty(Buffer, Cmd))); - } } +//~ TODO: Consider moving the FastArray members into their own sub-struct to save memory for non fast array +//~ custom delta properties. Almost all Custom Delta properties now **are** Fast Arrays, so this +//~ probably doesn't matter much at the moment. + struct FLifetimeCustomDeltaProperty { /** If this is a Fast Array Serializer property, this will be the command index for the Fast Array Item array. */ @@ -174,6 +169,59 @@ struct FLifetimeCustomDeltaProperty * This is used to lookup Changelists. */ int32 FastArrayNumber = INDEX_NONE; + + /** + * If this is a Fast Array Serializer property (and it is set up correctly for Delta Serialization), this will be an + * offset from to the property. + */ + int32 FastArrayDeltaFlagsOffset = INDEX_NONE; + + /** + * If this is a Fast Array Serializer property (and it is set up correctly for Delta Serialization), this will be a pointer + * to the FFastArraySerializer::ArrayReplicationKey property. + */ + int32 FastArrayArrayReplicationKeyOffset = INDEX_NONE; + + /** + * If this is a Fast Array Serializer property (and it is set up correctly for Delta Serialization), this will be a pointer + * to the FFastArraySerializerItem::ReplicationID property. + */ + int32 FastArrayItemReplicationIdOffset = INDEX_NONE; + + const EFastArraySerializerDeltaFlags GetFastArrayDeltaFlags(void const * const FastArray) const + { + return GetRefFromOffsetAndMemory(FastArray, FastArrayDeltaFlagsOffset); + } + + const int32 GetFastArrayArrayReplicationKey(void const * const FastArray) const + { + return GetRefFromOffsetAndMemory(FastArray, FastArrayArrayReplicationKeyOffset); + } + + const int32 GetFastArrayItemReplicationID(void const * const FastArrayItem) const + { + return GetRefFromOffsetAndMemory(FastArrayItem, FastArrayItemReplicationIdOffset); + } + + int32& GetFastArrayItemReplicationIDMutable(void* const FastArrayItem) const + { + return GetRefFromOffsetAndMemory(FastArrayItem, FastArrayItemReplicationIdOffset); + } + +private: + + template + static Output& GetRefFromOffsetAndMemory(void* Memory, const int32 Offset) + { + checkSlow(Offset != INDEX_NONE); + return *reinterpret_cast(reinterpret_cast(Memory) + Offset); + } + + template + static const Output& GetRefFromOffsetAndMemory(void const * const Memory, const int32 Offset) + { + return GetRefFromOffsetAndMemory(const_cast(Memory), Offset); + } }; /** @@ -664,6 +712,7 @@ FReplicationChangelistMgr::FReplicationChangelistMgr( FCustomDeltaChangelistState* DeltaChangelistState): LastReplicationFrame(0), + LastInitialReplicationFrame(0), RepChangelistState(InRepLayout, Source, DeltaChangelistState) { } @@ -694,21 +743,49 @@ void FRepLayout::UpdateChangelistMgr( const FReplicationFlags& RepFlags, const bool bForceCompare) const { - // See if we can re-use the work already done on a previous connection - // Rules: - // 1. We always compare once per frame (i.e. check LastReplicationFrame == ReplicationFrame) - // 2. We check LastCompareIndex > 1 so we can do at least one pass per connection to compare all properties - // This is necessary due to how RemoteRole is manipulated per connection, so we need to give all connections a chance to see if it changed - // 3. We ALWAYS compare on bNetInitial to make sure we have a fresh changelist of net initial properties in this case - if (!bForceCompare && GShareShadowState && !RepFlags.bNetInitial && RepState->LastCompareIndex > 1 && InChangelistMgr.LastReplicationFrame == ReplicationFrame) + if (GShareInitialCompareState) { - INC_DWORD_STAT_BY(STAT_NetSkippedDynamicProps, 1); - return; + // See if we can re-use the work already done on a previous connection + // Rules: + // 1. We have replicated this actor at least once this frame + // 2. This is not initial replication or we have done an initial replication this frame as well + if (!bForceCompare && GShareShadowState && (InChangelistMgr.LastReplicationFrame == ReplicationFrame) && (!RepFlags.bNetInitial || (InChangelistMgr.LastInitialReplicationFrame == ReplicationFrame))) + { + // If this is initial replication, or we have never replicated on this connection, force a role compare + if (RepFlags.bNetInitial || (RepState->LastCompareIndex == 0)) + { + FReplicationFlags TempFlags = RepFlags; + TempFlags.bRolesOnly = true; + CompareProperties(RepState, &InChangelistMgr.RepChangelistState, (const uint8*)InObject, TempFlags); + } + + INC_DWORD_STAT_BY(STAT_NetSkippedDynamicProps, 1); + return; + } + } + else + { + // See if we can re-use the work already done on a previous connection + // Rules: + // 1. We always compare once per frame (i.e. check LastReplicationFrame == ReplicationFrame) + // 2. We check LastCompareIndex > 1 so we can do at least one pass per connection to compare all properties + // This is necessary due to how RemoteRole is manipulated per connection, so we need to give all connections a chance to see if it changed + // 3. We ALWAYS compare on bNetInitial to make sure we have a fresh changelist of net initial properties in this case + if (!bForceCompare && GShareShadowState && !RepFlags.bNetInitial && RepState->LastCompareIndex > 1 && InChangelistMgr.LastReplicationFrame == ReplicationFrame) + { + INC_DWORD_STAT_BY(STAT_NetSkippedDynamicProps, 1); + return; + } } CompareProperties(RepState, &InChangelistMgr.RepChangelistState, (const uint8*)InObject, RepFlags); InChangelistMgr.LastReplicationFrame = ReplicationFrame; + + if (GShareInitialCompareState && RepFlags.bNetInitial) + { + InChangelistMgr.LastInitialReplicationFrame = ReplicationFrame; + } } void FReplicationChangelistMgr::CountBytes(FArchive& Ar) const @@ -743,6 +820,41 @@ static void CompareProperties_Array_r( const uint16 CmdIndex, const uint16 Handle); +static void CompareRoleProperties( + const FComparePropertiesSharedParams& SharedParams, + FSendingRepState* RESTRICT RepState, + const FConstRepObjectDataBuffer Data, + TArray& Changed) +{ + if (RepState && SharedParams.RoleIndex != INDEX_NONE && SharedParams.RemoteRoleIndex != INDEX_NONE) + { + // Verify that the order hasn't changed, otherwise ReceiveProperties will fail + check(SharedParams.RemoteRoleIndex < SharedParams.RoleIndex); + + const FRepParentCmd& RemoteRoleParent = SharedParams.Parents[SharedParams.RemoteRoleIndex]; + const FRepLayoutCmd& RemoteRoleCmd = SharedParams.Cmds[RemoteRoleParent.CmdStart]; + const uint16 RemoteRoleHandle = RemoteRoleCmd.RelativeHandle; + + const ENetRole ObjectRemoteRole = *(const ENetRole*)(Data + RemoteRoleParent).Data; + if (SharedParams.bForceFail || RepState->SavedRemoteRole != ObjectRemoteRole) + { + RepState->SavedRemoteRole = ObjectRemoteRole; + Changed.Add(RemoteRoleHandle); + } + + const FRepParentCmd& RoleParent = SharedParams.Parents[SharedParams.RoleIndex]; + const FRepLayoutCmd& RoleCmd = SharedParams.Cmds[RoleParent.CmdStart]; + const uint16 RoleHandle = RoleCmd.RelativeHandle; + + const ENetRole ObjectRole = *(const ENetRole*)(Data + RoleParent).Data; + if (SharedParams.bForceFail || RepState->SavedRole != ObjectRole) + { + RepState->SavedRole = ObjectRole; + Changed.Add(RoleHandle); + } + } +} + static void CompareParentProperties( const FComparePropertiesSharedParams& SharedParams, FSendingRepState* RESTRICT RepState, @@ -930,7 +1042,14 @@ bool FRepLayout::CompareProperties( Cmds }; - CompareParentProperties(SharedParams, RepState, RepChangelistState, Data, Changed); + if (RepFlags.bRolesOnly) + { + CompareRoleProperties(SharedParams, RepState, Data, Changed); + } + else + { + CompareParentProperties(SharedParams, RepState, RepChangelistState, Data, Changed); + } if (Changed.Num() == 0) { @@ -2606,7 +2725,7 @@ static bool ReceiveProperties_r(FReceivePropertiesSharedParams& Params, FReceive } else { - UE_LOG(LogRepProperties, VeryVerbose, TEXT("ReceiveProperties_r: Parent=%d, Cmd=%d, ArrayIndex=%d"), Cmd.ParentIndex, CmdIndex); + UE_LOG(LogRepProperties, VeryVerbose, TEXT("ReceiveProperties_r: Parent=%d, Cmd=%d"), Cmd.ParentIndex, CmdIndex); if (ERepLayoutCmdType::DynamicArray == Cmd.Type) { @@ -3156,11 +3275,12 @@ void FRepLayout::GatherGuidReferencesForCustomDeltaProperties(FNetDeltaSerialize UStructProperty* StructProperty = static_cast(Parent.Property); UScriptStruct::ICppStructOps* CppStructOps = StructProperty->Struct->GetCppStructOps(); - Params.Struct = StructProperty->Struct; - Params.PropertyRepIndex = KVP.Key; - Params.Data = ObjectData + Parent; + FNetDeltaSerializeInfo TempParams = Params; + TempParams.Struct = StructProperty->Struct; + TempParams.PropertyRepIndex = KVP.Key; + TempParams.Data = ObjectData + Parent; - CppStructOps->NetDeltaSerialize(Params, Params.Data); + CppStructOps->NetDeltaSerialize(TempParams, TempParams.Data); } } } @@ -3222,15 +3342,22 @@ bool FRepLayout::MoveMappedObjectToUnmappedForCustomDeltaProperties(FNetDeltaSer UStructProperty* StructProperty = static_cast(Parent.Property); UScriptStruct::ICppStructOps* CppStructOps = StructProperty->Struct->GetCppStructOps(); - Params.Struct = StructProperty->Struct; - Params.Data = ObjectData + Parent; - Params.PropertyRepIndex = KVP.Key; + FNetDeltaSerializeInfo TempParams = Params; - if (CppStructOps->NetDeltaSerialize(Params, Params.Data)) + TempParams.Struct = StructProperty->Struct; + TempParams.Data = ObjectData + Parent; + TempParams.PropertyRepIndex = KVP.Key; + TempParams.bOutHasMoreUnmapped = false; + TempParams.bOutSomeObjectsWereMapped = false; + + if (CppStructOps->NetDeltaSerialize(TempParams, TempParams.Data)) { UnmappedCustomProperties.Add(Parent.Offset, StructProperty); bFound = true; } + + Params.bOutHasMoreUnmapped |= TempParams.bOutHasMoreUnmapped; + Params.bOutSomeObjectsWereMapped |= TempParams.bOutSomeObjectsWereMapped; } } @@ -3386,16 +3513,27 @@ void FRepLayout::UpdateUnmappedObjects( FReceivingRepState* RESTRICT RepState, UPackageMap* PackageMap, UObject* OriginalObject, + bool& bCalledPreNetReceive, bool& bOutSomeObjectsWereMapped, bool& bOutHasMoreUnmapped) const { bOutSomeObjectsWereMapped = false; bOutHasMoreUnmapped = false; - bool bCalledPreNetReceive = false; + bCalledPreNetReceive = false; if (LayoutState == ERepLayoutState::Normal) { - UpdateUnmappedObjects_r(RepState, &RepState->GuidReferencesMap, OriginalObject, PackageMap, (uint8*)RepState->StaticBuffer.GetData(), (uint8*)OriginalObject, Owner->GetPropertiesSize(), bCalledPreNetReceive, bOutSomeObjectsWereMapped, bOutHasMoreUnmapped); + UpdateUnmappedObjects_r( + RepState, + &RepState->GuidReferencesMap, + OriginalObject, + PackageMap, + (uint8*)RepState->StaticBuffer.GetData(), + (uint8*)OriginalObject, + Owner->GetPropertiesSize(), + bCalledPreNetReceive, + bOutSomeObjectsWereMapped, + bOutHasMoreUnmapped); } } @@ -3409,9 +3547,6 @@ PRAGMA_ENABLE_DEPRECATION_WARNINGS void FRepLayout::UpdateUnmappedObjectsForCustomDeltaProperties(FNetDeltaSerializeInfo& Params, TArray>& CompletelyUpdated, TArray>& Updated) const { - bool bOutSomeObjectsWereMapped = false; - bool bOutHasMoreUnmapped = false; - if (LifetimeCustomPropertyState) { FRepObjectDataBuffer ObjectData(Params.Object); @@ -3423,33 +3558,33 @@ void FRepLayout::UpdateUnmappedObjectsForCustomDeltaProperties(FNetDeltaSerializ UStructProperty* StructProperty = static_cast(Parent.Property); UScriptStruct::ICppStructOps* CppStructOps = StructProperty->Struct->GetCppStructOps(); - Params.DebugName = Parent.CachedPropertyName.ToString(); - Params.Struct = StructProperty->Struct; - Params.bOutSomeObjectsWereMapped = false; - Params.bOutHasMoreUnmapped = false; - Params.PropertyRepIndex = KVP.Key; - Params.Data = ObjectData + Parent; + FNetDeltaSerializeInfo TempParams = Params; + + TempParams.DebugName = Parent.CachedPropertyName.ToString(); + TempParams.Struct = StructProperty->Struct; + TempParams.bOutSomeObjectsWereMapped = false; + TempParams.bOutHasMoreUnmapped = false; + TempParams.PropertyRepIndex = KVP.Key; + TempParams.Data = ObjectData + Parent; // Call the custom delta serialize function to handle it - CppStructOps->NetDeltaSerialize(Params, Params.Data); + CppStructOps->NetDeltaSerialize(TempParams, TempParams.Data); - if (Params.bOutSomeObjectsWereMapped) + if (TempParams.bOutSomeObjectsWereMapped) { Updated.Emplace(Parent.Offset, StructProperty); } - if (!Params.bOutHasMoreUnmapped) + if (!TempParams.bOutHasMoreUnmapped) { CompletelyUpdated.Emplace(Parent.Offset, StructProperty); } - bOutSomeObjectsWereMapped |= Params.bOutSomeObjectsWereMapped; - bOutHasMoreUnmapped |= Params.bOutHasMoreUnmapped; + Params.bOutSomeObjectsWereMapped |= TempParams.bOutSomeObjectsWereMapped; + Params.bOutHasMoreUnmapped |= TempParams.bOutHasMoreUnmapped; + Params.bCalledPreNetReceive |= TempParams.bCalledPreNetReceive; } } - - Params.bOutSomeObjectsWereMapped = bOutSomeObjectsWereMapped; - Params.bOutHasMoreUnmapped = bOutHasMoreUnmapped; } bool FRepLayout::SendCustomDeltaProperty( @@ -4846,11 +4981,26 @@ void FRepLayout::InitFromClass(UClass* InObjectClass, const UNetConnection* Serv const FRepLayoutCmd& Cmd = Cmds[CmdIndex]; if (ERepLayoutCmdType::DynamicArray == Cmd.Type) { - if (UStructProperty* MaybeFastArray = Cast(static_cast(Cmd.Property)->Inner)) + if (UStructProperty* MaybeFastArrayItemsArray = Cast(static_cast(Cmd.Property)->Inner)) { - if (MaybeFastArray->Struct->IsChildOf(FFastArraySerializerItem::StaticStruct())) + UScriptStruct* MaybeFastArrayItem = MaybeFastArrayItemsArray->Struct; + if (MaybeFastArrayItem->IsChildOf(FFastArraySerializerItem::StaticStruct())) { - FastArrayItemArrayCmd = CmdIndex; + // Can't use GET_MEMBER_NAME_CHECKED because this is private. + static const FName FastArrayDeltaFlagsName(TEXT("DeltaFlags")); + static const FName FastArrayArrayReplicationKeyName(GET_MEMBER_NAME_CHECKED(FFastArraySerializer, ArrayReplicationKey)); + static const FName FastArrayItemReplicationIDName(GET_MEMBER_NAME_CHECKED(FFastArraySerializerItem, ReplicationID)); + + // This better be a script struct, otherwise our flags aren't set up correctly! + UScriptStruct* FastArray = CastChecked(MaybeFastArrayItemsArray->GetOwnerStruct()); + + CustomDeltaProperty.FastArrayItemsCommand = CmdIndex; + CustomDeltaProperty.FastArrayItemReplicationIdOffset = MaybeFastArrayItem->FindPropertyByName(FastArrayItemReplicationIDName)->GetOffset_ForGC(); + CustomDeltaProperty.FastArrayArrayReplicationKeyOffset = FastArray->FindPropertyByName(FastArrayArrayReplicationKeyName)->GetOffset_ForGC(); + CustomDeltaProperty.FastArrayDeltaFlagsOffset = FastArray->FindPropertyByName(FastArrayDeltaFlagsName)->GetOffset_ForGC(); + + CustomDeltaProperty.FastArrayNumber = LifetimeCustomPropertyState->NumFastArrayItems; + ++LifetimeCustomPropertyState->NumFastArrayItems; break; } } @@ -4859,12 +5009,7 @@ void FRepLayout::InitFromClass(UClass* InObjectClass, const UNetConnection* Serv } } - if (FastArrayItemArrayCmd != INDEX_NONE) - { - ++LifetimeCustomPropertyState->NumFastArrayItems; - CustomDeltaProperty.FastArrayItemsCommand = FastArrayItemArrayCmd; - } - else + if (CustomDeltaProperty.FastArrayItemsCommand == INDEX_NONE) { UE_LOG(LogRep, Warning, TEXT("FRepLayout::InitFromClass: Unable to find Fast Array Item array in Fast Array Serializer: %s"), *Parents[ParentIndex].CachedPropertyName.ToString()); } @@ -5653,7 +5798,14 @@ TSharedPtr FRepLayout::CreateReplicationChangelistMgr DeltaChangelistState = new FCustomDeltaChangelistState(LifetimeCustomPropertyState->NumFastArrayItems); } - return MakeShareable(new FReplicationChangelistMgr(AsShared(), (const uint8*)InObject->GetArchetype(), DeltaChangelistState)); + const uint8* ShadowStateSource = (const uint8*)InObject->GetArchetype(); + if (ShadowStateSource == nullptr) + { + UE_LOG(LogRep, Error, TEXT("FRepLayout::CreateReplicationChangelistMgr: Invalid object archetype, initializing shadow state to current object state: %s"), *GetFullNameSafe(InObject)); + ShadowStateSource = (const uint8*)InObject; + } + + return MakeShareable(new FReplicationChangelistMgr(AsShared(), ShadowStateSource, DeltaChangelistState)); } TUniquePtr FRepLayout::CreateRepState( @@ -5887,21 +6039,28 @@ void FRepLayout::PreSendCustomDeltaProperties( { const int32 FastArrayNumber = CustomDeltaProperty.FastArrayNumber; const FRepParentCmd& FastArrayCmd = Parents[RepIndex]; - const FFastArraySerializer& FastArray = *GetTypedProperty(ObjectData, FastArrayCmd); - // Note, we can't rely on FFastArraySerializer::HasBeenSerialized here. + void const * const FastArraySerializer = ObjectData + FastArrayCmd; + const EFastArraySerializerDeltaFlags Flags = CustomDeltaProperty.GetFastArrayDeltaFlags(FastArraySerializer); + + // Note, we can't rely on EFastArraySerializerDeltaFlags::HasBeenSerialized here. // It's possible we're calling PreSendCustomDeltaProperties **before** the first time the struct // was ever serialized, and in that case it would still be false, and prevent us from creating // a history the first time. // - // This does mean that Fast Arrays *not* using delta serialization will still have their history - // incremented the first time, but that can be fixed in other ways and the history memory - // will have already been allocated elsewhere. - if (FastArray.IsUsingStructDeltaSerialization()) + // This does mean that Fast Arrays requesting delta serialization will still have their history + // incremented the first time, even if the feature is generally disabled. + // + // TODO: If any fast arrays failed this check, we could probably reset their state, + // because we know we should never try sending them again + if (EnumHasAnyFlags(Flags, EFastArraySerializerDeltaFlags::IsUsingDeltaSerialization) || + (!EnumHasAnyFlags(Flags, EFastArraySerializerDeltaFlags::HasBeenSerialized) && + EnumHasAnyFlags(Flags, EFastArraySerializerDeltaFlags::HasDeltaBeenRequested))) { FDeltaArrayHistoryState& FastArrayHistoryState = CustomDeltaChangelistState.ArrayStates[FastArrayNumber]; - if (FastArrayHistoryState.ArrayReplicationKey != FastArray.ArrayReplicationKey) + const int32 FastArrayReplicationKey = CustomDeltaProperty.GetFastArrayArrayReplicationKey(FastArraySerializer); + if (FastArrayHistoryState.ArrayReplicationKey != FastArrayReplicationKey) { const uint32 HistoryDelta = FastArrayHistoryState.HistoryEnd - FastArrayHistoryState.HistoryStart; const uint32 CurrentHistoryIndex = FastArrayHistoryState.HistoryEnd % FDeltaArrayHistoryState::MAX_CHANGE_HISTORY; @@ -5982,7 +6141,7 @@ bool FRepLayout::DeltaSerializeFastArrayProperty(FFastArrayDeltaSerializeParams& FScriptArray* ObjectArray = GetTypedProperty(ObjectData, FastArrayItemCmd); FRepObjectDataBuffer ObjectArrayData(ObjectArray->GetData()); - FFastArraySerializer& ArraySerializer = *GetFastArray(Params, ObjectData, Parent); + FFastArraySerializer& ArraySerializer = Params.ArraySerializer; check(&ArraySerializer == &Params.ArraySerializer); @@ -6065,11 +6224,13 @@ bool FRepLayout::DeltaSerializeFastArrayProperty(FFastArrayDeltaSerializeParams& { HistoryItem.bWasUpdated = true; + FastArrayState.ArrayReplicationKey = NewArrayDeltaState->ArrayReplicationKey; + // Update our shadow array, and reset our pointer in case we reallocated. FScriptArrayHelper ShadowArrayHelper((UArrayProperty*)FastArrayItemCmd.Property, ShadowArray); TBitArray<> ShadowArrayItemIsNew(false, ObjectArrayNum); - const bool bIsInitial = (FastArrayState.HistoryEnd - FastArrayState.HistoryStart) == 1; + const bool bIsInitial = CompareChangelistDelta == 1; // It's possible that elements have been deleted or otherwise reordered, and our shadow state is out of date. // In order to prevent issues, we'll shuffle our shadow state back to the correct order. @@ -6099,7 +6260,7 @@ bool FRepLayout::DeltaSerializeFastArrayProperty(FFastArrayDeltaSerializeParams& // // a. If we find the Shadow Element in the Shadow Line, it must have been reordered. // Go ahead and swap the current front of the Shadow Line with the found Shadow Element. - // Now, the elements at the front of the line have mathcing IDs, and we can move onto + // Now, the elements at the front of the line have matching IDs, and we can move onto // the next Element in both lines. // // b. If we don't find the Shadow Element in the Shadow Line, the Object Element must be new. @@ -6122,74 +6283,107 @@ bool FRepLayout::DeltaSerializeFastArrayProperty(FFastArrayDeltaSerializeParams& // TODO: Optimize this. Luckily, it only happens once per frame, and only if the array is dirty. // Maybe this could be merged into the sending code below, the only concern is that // doesn't tracked deleted elements. + // + // Alternatively, if Custom Delta code was merged into FRepLayout, we might be able to track + // lists of deleted items on a given frame and merge those together just like changelists. + // This would prevent us from needing to call BuildChangedAndDeletedBuffers on Fast TArrays + // for every connection, unless a specific connection was very out of date. // Note, this code serves a very similar purpose to FFastArraySerializer::TFastArraySerializeHelper::BuildChangedAndDeletedBuffers. // The main issue is that we can't rely on that method, because it will be comparing the last state that was replicated to a given particular connection, // and we want to compare the last state that was replicated to *any* connection. - if (ObjectArrayNum != 0 && ShadowArrayHelper.Num() != 0 && !bIsInitial) { FScriptArrayHelper ObjectArrayHelper((UArrayProperty*)FastArrayItemCmd.Property, ObjectArray); - TMap& ShadowIDToIndex = FastArrayState.IDToIndexMap; - const TMap& ObjectIDToIndex = ArraySerializer.ItemMap; + // We track this as a non-const, because if we append any items into the middle of the + // array, they will be explicitly marked as new, and we still want to compare items + // that existed in the array originally. + int32 ShadowArrayNum = ShadowArrayHelper.Num(); - for (int32 Index = 0; Index < ObjectArrayNum; ++Index) + if (ObjectArrayNum != 0 && ShadowArrayNum != 0 && !bIsInitial) { - // Anything else must be a newly appended element, so we are done. - if (Index >= ShadowArrayHelper.Num()) + const TMap OldShadowIDToIndexMap(MoveTemp(FastArrayState.IDToIndexMap)); + FastArrayState.IDToIndexMap.Reserve(ObjectArrayNum); + + // We track the Appended Shadow Items, because any index we try and use after such + // an append needs to be shifted appropriately. + // TODO: We may be able to iterate the list backwards instead, but that may break + // some assumptions laid out in the algorithm above. + int32 AppendedShadowItems = 0; + + UE_LOG(LogRep, VeryVerbose, TEXT("DeltaSerializeFastArrayProperty: Fixup Shadow State. Owner=%s, Property=%s, bInitial=%d, ObjecyArrayNum=%d, ShadowArrayNum=%d"), + *Owner->GetName(), *Parent.CachedPropertyName.ToString(), !!bIsInitial, ObjectArrayNum, ShadowArrayHelper.Num()); + + for (int32 Index = 0; Index < ObjectArrayNum && Index < ShadowArrayNum; ++Index) { - break; + const int32 ObjectReplicationID = CustomDeltaProperty.GetFastArrayItemReplicationID(ObjectArrayHelper.GetRawPtr(Index)); + int32& ShadowReplicationID = CustomDeltaProperty.GetFastArrayItemReplicationIDMutable(ShadowArrayHelper.GetRawPtr(Index)); + + FastArrayState.IDToIndexMap.Emplace(ObjectReplicationID, Index); + + UE_LOG(LogRep, VeryVerbose, TEXT("DeltaSerializeFastArrayProperty: Handling Item. ID=%d, Index=%d, ShadowID=%d"), ObjectReplicationID, Index, ShadowReplicationID); + + // If our IDs match, there's nothing to do. + if (ObjectReplicationID != ShadowReplicationID) + { + // The IDs didn't match, so this is an insert, delete, or swap. + if (int32 const * const FoundShadowIndex = OldShadowIDToIndexMap.Find(ObjectReplicationID)) + { + // We found the element in the shadow array, so there must have been a swap. + // Sanity check that the invalid element can only possibly later in our lines. + const int32 FixedShadowIndex = *FoundShadowIndex + AppendedShadowItems; + + UE_LOG(LogRepProperties, VeryVerbose, TEXT("DeltaSerializeFastArrayProperty: Swapped Shadow Item. OldIndex=%d, NewIndex=%d"), Index, FixedShadowIndex); + + check(FixedShadowIndex > Index); + + ShadowArrayHelper.SwapValues(Index, FixedShadowIndex); + } + else + { + // This item must have been inserted into the array (or appended and then shuffled in). + // So, insert it into our shadow array and update its ID. + + ShadowArrayItemIsNew[Index] = true; + ShadowArrayHelper.InsertValues(Index); + + int32& NewShadowReplicationID = CustomDeltaProperty.GetFastArrayItemReplicationIDMutable(ShadowArrayHelper.GetRawPtr(Index)); + NewShadowReplicationID = ObjectReplicationID; + + ++AppendedShadowItems; + ++ShadowArrayNum; + UE_LOG(LogRepProperties, VeryVerbose, TEXT("DeltaSerializeFastArrayProperty: Added Shadow Item. AppendedShadowItems=%d"), AppendedShadowItems); + } + } } + } - FFastArraySerializerItem* ObjectItem = GetFastArrayItem(Params, ObjectArrayHelper.GetRawPtr(Index), FastArrayItemCmd); - FFastArraySerializerItem* ShadowItem = GetFastArrayItem(Params, ShadowArrayHelper.GetRawPtr(Index), FastArrayItemCmd); + // Now we can go ahead and resize the array, to make any other changes we need. + ShadowArrayHelper.Resize(ObjectArrayNum); + ShadowArrayData = ShadowArray->GetData(); - if (ObjectItem->ReplicationID != ShadowItem->ReplicationID) + // Go ahead and fix up IDs for any elements that may have just been appended. + // Note, we need to do this for all elements on the initial pass. + // Deleted elements will have been chopped off by the resize. + if (bIsInitial || (ShadowArrayNum < ObjectArrayNum)) + { + const int32 StartIndex = bIsInitial ? 0 : ShadowArrayNum; + for (int32 Index = StartIndex; Index < ObjectArrayNum; ++Index) { - // The IDs didn't match, so this is an insert, delete, or swap. - if (int32* FoundShadowIndex = ShadowIDToIndex.Find(ShadowItem->ReplicationID)) - { - // We found the element in the shadow array, so there must have been a swap. - // Sanity check that the invalid element can only possibly be later in our lines. - check(*FoundShadowIndex > Index); + const int32 ObjectReplicationID = CustomDeltaProperty.GetFastArrayItemReplicationID(ObjectArrayHelper.GetRawPtr(Index)); + int32& ShadowReplicationID = CustomDeltaProperty.GetFastArrayItemReplicationIDMutable(ShadowArrayHelper.GetRawPtr(Index)); - ShadowArrayHelper.SwapValues(Index, *FoundShadowIndex); - } - else - { - // This item must have been inserted into the array (or appended and then shuffled in). - // So, insert it into our shadow array and update its ID. + ShadowReplicationID = ObjectReplicationID; + ShadowArrayItemIsNew[Index] = true; - ShadowArrayItemIsNew[Index] = true; - ShadowArrayHelper.InsertValues(Index); - - ShadowItem = GetFastArrayItem(Params, ShadowArrayHelper.GetRawPtr(Index), FastArrayItemCmd); - ShadowItem->ReplicationID = ObjectItem->ReplicationID; - } + UE_LOG(LogRep, VeryVerbose, TEXT("DeltaSerializeFastArrayProperty: Added Shadow Item. Index=%d, ID=%d"), Index, ShadowReplicationID); + FastArrayState.IDToIndexMap.Emplace(ObjectReplicationID, Index); } } } - // Now we can go ahead and resize the array, to make any other changes we need. - ShadowArrayHelper.Resize(ObjectArrayNum); - ShadowArrayData = ShadowArray->GetData(); - - // Go ahead and fix up IDs for any elements that may have just been appended. - // Note, we need to this for all elements on the initial pass. - // Deleted elements will have been chopped off by the resize. - if (bIsInitial || (ShadowArrayHelper.Num() < ObjectArrayNum)) - { - FScriptArrayHelper ObjectArrayHelper((UArrayProperty*)FastArrayItemCmd.Property, ObjectArray); - for (int32 ObjectIndex = ShadowArrayHelper.Num(); ObjectIndex < ObjectArrayNum; ++ObjectIndex) - { - FFastArraySerializerItem* ObjectItem = GetFastArrayItem(Params, ObjectArrayHelper.GetRawPtr(ObjectIndex), FastArrayItemCmd); - FFastArraySerializerItem* ShadowItem = GetFastArrayItem(Params, ShadowArrayHelper.GetRawPtr(ObjectIndex), FastArrayItemCmd); - ShadowItem->ReplicationID = ObjectItem->ReplicationID; - ShadowArrayItemIsNew[ObjectIndex] = true; - } - } - + TArray NewChangelist; for (auto& IDIndexPair : ChangedElements) { // Go ahead and do a property compare here, regardless of what we'll actually use below. @@ -6208,18 +6402,16 @@ bool FRepLayout::DeltaSerializeFastArrayProperty(FFastArrayDeltaSerializeParams& FConstRepObjectDataBuffer ElementData(ObjectArrayData + ArrayElementOffset); FRepShadowDataBuffer ElementShadowData(ShadowArrayData + ArrayElementOffset); - TArray& HistoryChangelist = HistoryItem.ChangelistByID.Emplace(IDIndexPair.ID); - CompareProperties_r(SharedParams, ItemLayoutStart, ItemLayoutEnd, ElementShadowData, ElementData, HistoryChangelist, 0); + NewChangelist.Empty(); - if (HistoryChangelist.Num()) + CompareProperties_r(SharedParams, ItemLayoutStart, ItemLayoutEnd, ElementShadowData, ElementData, NewChangelist, 0); + + if (NewChangelist.Num()) { - HistoryChangelist.Add(0); + NewChangelist.Add(0); + HistoryItem.ChangelistByID.Emplace(IDIndexPair.ID, MoveTemp(NewChangelist)); } } - - // Copy our replication key, and the IDToIndexMap so we can use them later. - FastArrayState.ArrayReplicationKey = NewArrayDeltaState->ArrayReplicationKey; - FastArrayState.IDToIndexMap = ArraySerializer.ItemMap; } } @@ -6229,7 +6421,7 @@ bool FRepLayout::DeltaSerializeFastArrayProperty(FFastArrayDeltaSerializeParams& // Note, this won't be all changes since the beginning, but just all changes for the currently dirty items. - if (LastSentChangelistDelta != 0 && LastSentChangelistDelta < FDeltaArrayHistoryState::MAX_CHANGE_HISTORY) + if (LastSentHistory != 0 && LastSentChangelistDelta > 0 && LastSentChangelistDelta < (FDeltaArrayHistoryState::MAX_CHANGE_HISTORY - 1)) { const FConstRepObjectDataBuffer ConstObjectData(ObjectData); Changelists.SetNum(ChangedElements.Num()); @@ -6390,7 +6582,7 @@ bool FRepLayout::DeltaSerializeFastArrayProperty(FFastArrayDeltaSerializeParams& int32* ElementIndexPtr = ArraySerializer.ItemMap.Find(ID); int32 ElementIndex = 0; - FFastArraySerializerItem* ThisElement = nullptr; + void* ThisElement = nullptr; if (!ElementIndexPtr) { @@ -6399,24 +6591,18 @@ bool FRepLayout::DeltaSerializeFastArrayProperty(FFastArrayDeltaSerializeParams& ElementIndex = FastArrayHelper.AddValue(); ArraySerializer.ItemMap.Add(ID, ElementIndex); AddedElements.Add(ElementIndex); - - ThisElement = GetFastArrayItem(Params, FastArrayHelper.GetRawPtr(ElementIndex), FastArrayItemCmd); - ThisElement->ReplicationID = ID; } else { ElementIndex = *ElementIndexPtr; - ThisElement = GetFastArrayItem(Params, FastArrayHelper.GetRawPtr(ElementIndex), FastArrayItemCmd); ChangedElements.Add(ElementIndex); UE_LOG(LogNetFastTArray, Log, TEXT(" Changed. ID: %d -> Idx: %d"), ID, ElementIndex); } - // Update this element's most recent array replication key - ThisElement->MostRecentArrayReplicationKey = ArraySerializer.ArrayReplicationKey; + ThisElement = FastArrayHelper.GetRawPtr(ElementIndex); - // Update this element's replication key so that a client can re-serialize the array for client replay recording - ThisElement->ReplicationKey++; + Params.ReceivedItem(ThisElement, Params, ID); FGuidReferencesMap& GuidReferences = ArraySerializer.GuidReferencesMap_StructDelta.FindOrAdd(ID); @@ -6506,7 +6692,7 @@ void FRepLayout::GatherGuidReferencesForFastArray(FFastArrayDeltaSerializeParams const FConstRepObjectDataBuffer ObjectData(Params.DeltaSerializeInfo.Object); const FRepParentCmd& Parent = Parents[Params.DeltaSerializeInfo.PropertyRepIndex]; - const FFastArraySerializer& ArraySerializer = *GetFastArray(Params, ObjectData, Parent); + const FFastArraySerializer& ArraySerializer = Params.ArraySerializer; TSet& GatherGuids = *Params.DeltaSerializeInfo.GatherGuidReferences; int32 TrackedGuidMemory = 0; @@ -6528,7 +6714,7 @@ bool FRepLayout::MoveMappedObjectToUnmappedForFastArray(FFastArrayDeltaSerialize const FRepObjectDataBuffer ObjectData(Params.DeltaSerializeInfo.Object); const FRepParentCmd& Parent = Parents[Params.DeltaSerializeInfo.PropertyRepIndex]; - FFastArraySerializer& ArraySerializer = *GetFastArray(Params, ObjectData, Parent); + FFastArraySerializer& ArraySerializer = Params.ArraySerializer; const FNetworkGUID& MoveToUnmapped = *Params.DeltaSerializeInfo.MoveGuidToUnmapped; bool bFound = false; @@ -6569,27 +6755,34 @@ void FRepLayout::UpdateUnmappedGuidsForFastArray(FFastArrayDeltaSerializeParams& FScriptArray* ScriptArray = GetTypedProperty(ObjectData, FastArrayItemCmd); FRepObjectDataBuffer ArrayData(ScriptArray->GetData()); - FFastArraySerializer& ArraySerializer = *GetFastArray(Params, ObjectData, Parent); + FFastArraySerializer& ArraySerializer = Params.ArraySerializer; - bool bCalledPreNetReceive = Params.DeltaSerializeInfo.bOutSomeObjectsWereMapped; - for (auto& GuidReferencesPair : ArraySerializer.GuidReferencesMap_StructDelta) + for (auto It = ArraySerializer.GuidReferencesMap_StructDelta.CreateIterator(); It; ++It) { - bool bOutSomeObjectsWereMapped = false; - bool bOutHasMoreUnmapped = false; - - const int32 ItemIndex = ArraySerializer.ItemMap[GuidReferencesPair.Key]; - const int32 ArrayElementOffset = ItemIndex * ElementSize; - FRepObjectDataBuffer ElementData(ArrayData + ArrayElementOffset); - - UpdateUnmappedObjects_r(nullptr, &GuidReferencesPair.Value, Object, PackageMap, nullptr, ElementData, ElementSize, bCalledPreNetReceive, bOutSomeObjectsWereMapped, bOutHasMoreUnmapped); - - if (bOutSomeObjectsWereMapped) + const int32 ElementID = It.Key(); + if (int32 const * const FoundItemIndex = ArraySerializer.ItemMap.Find(ElementID)) { - ((FFastArraySerializerItem*)(ElementData).Data)->PostReplicatedChange(ArraySerializer); - } + bool bOutSomeObjectsWereMapped = false; + bool bOutHasMoreUnmapped = false; - DeltaSerializeInfo.bOutHasMoreUnmapped |= bOutHasMoreUnmapped; - DeltaSerializeInfo.bOutSomeObjectsWereMapped |= bOutSomeObjectsWereMapped; + const int32 ItemIndex = *FoundItemIndex; + const int32 ArrayElementOffset = ItemIndex * ElementSize; + FRepObjectDataBuffer ElementData(ArrayData + ArrayElementOffset); + + UpdateUnmappedObjects_r(nullptr, &It.Value(), Object, PackageMap, nullptr, ElementData, ElementSize, Params.DeltaSerializeInfo.bCalledPreNetReceive, bOutSomeObjectsWereMapped, bOutHasMoreUnmapped); + + if (bOutSomeObjectsWereMapped) + { + Params.PostReplicatedChange(ElementData, Params); + } + + DeltaSerializeInfo.bOutHasMoreUnmapped |= bOutHasMoreUnmapped; + DeltaSerializeInfo.bOutSomeObjectsWereMapped |= bOutSomeObjectsWereMapped; + } + else + { + It.RemoveCurrent(); + } } } diff --git a/Engine/Source/Runtime/Engine/Private/SCS_Node.cpp b/Engine/Source/Runtime/Engine/Private/SCS_Node.cpp index ea51f8948470..764f7a30b7cf 100644 --- a/Engine/Source/Runtime/Engine/Private/SCS_Node.cpp +++ b/Engine/Source/Runtime/Engine/Private/SCS_Node.cpp @@ -88,7 +88,7 @@ UActorComponent* USCS_Node::ExecuteNodeOnActor(AActor* Actor, USceneComponent* P UActorComponent* NewActorComp = nullptr; UBlueprintGeneratedClass* ActualBPGC = CastChecked(Actor->GetClass()); const FBlueprintCookedComponentInstancingData* ActualComponentTemplateData = ActualBPGC->UseFastPathComponentInstancing() ? GetActualComponentTemplateData(ActualBPGC) : nullptr; - if (ActualComponentTemplateData && ActualComponentTemplateData->bIsValid + if (ActualComponentTemplateData && ActualComponentTemplateData->bHasValidCookedData && ensureMsgf(ActualComponentTemplateData->ComponentTemplateClass != nullptr, TEXT("SCS fast path (%s.%s): Cooked data is valid, but runtime support data is not initialized. Using the slow path instead."), *ActualBPGC->GetName(), *InternalVariableName.ToString())) { // Use cooked instancing data if valid (fast path). diff --git a/Engine/Source/Runtime/Engine/Private/SceneManagement.cpp b/Engine/Source/Runtime/Engine/Private/SceneManagement.cpp index e921bdbd0b97..c054a96a60ca 100644 --- a/Engine/Source/Runtime/Engine/Private/SceneManagement.cpp +++ b/Engine/Source/Runtime/Engine/Private/SceneManagement.cpp @@ -305,16 +305,29 @@ void FDynamicPrimitiveUniformBuffer::Set( const FMatrix& PreviousLocalToWorld, const FBoxSphereBounds& WorldBounds, const FBoxSphereBounds& LocalBounds, + const FBoxSphereBounds& PreSkinnedLocalBounds, bool bReceivesDecals, bool bHasPrecomputedVolumetricLightmap, bool bUseEditorDepthTest) { check(IsInRenderingThread()); UniformBuffer.SetContents( - GetPrimitiveUniformShaderParameters(LocalToWorld, PreviousLocalToWorld, WorldBounds.Origin, WorldBounds, LocalBounds, bReceivesDecals, false, false, false, bHasPrecomputedVolumetricLightmap, bUseEditorDepthTest, GetDefaultLightingChannelMask(), 1.0f, INDEX_NONE, INDEX_NONE)); + GetPrimitiveUniformShaderParameters(LocalToWorld, PreviousLocalToWorld, WorldBounds.Origin, WorldBounds, LocalBounds, PreSkinnedLocalBounds, bReceivesDecals, false, false, false, bHasPrecomputedVolumetricLightmap, bUseEditorDepthTest, GetDefaultLightingChannelMask(), 1.0f, INDEX_NONE, INDEX_NONE)); UniformBuffer.InitResource(); } +void FDynamicPrimitiveUniformBuffer::Set( + const FMatrix& LocalToWorld, + const FMatrix& PreviousLocalToWorld, + const FBoxSphereBounds& WorldBounds, + const FBoxSphereBounds& LocalBounds, + bool bReceivesDecals, + bool bHasPrecomputedVolumetricLightmap, + bool bUseEditorDepthTest) +{ + Set(LocalToWorld, PreviousLocalToWorld, WorldBounds, LocalBounds, LocalBounds, bReceivesDecals, bHasPrecomputedVolumetricLightmap, bUseEditorDepthTest); +} + FLightMapInteraction FLightMapInteraction::Texture( const class ULightMapTexture2D* const* InTextures, const ULightMapTexture2D* InSkyOcclusionTexture, diff --git a/Engine/Source/Runtime/Engine/Private/SceneUtils.cpp b/Engine/Source/Runtime/Engine/Private/SceneUtils.cpp index 4db4c49a98f6..42d412d222f0 100644 --- a/Engine/Source/Runtime/Engine/Private/SceneUtils.cpp +++ b/Engine/Source/Runtime/Engine/Private/SceneUtils.cpp @@ -264,13 +264,14 @@ public: bool GatherQueryResults(FRHICommandListImmediate& RHICmdList) { + QUICK_SCOPE_CYCLE_COUNTER(STAT_SceneUtils_GatherQueryResults); // Get the query results which are still outstanding check(GFrameNumberRenderThread != FrameNumber); if ( HasQueriesAllocated() ) { if (StartResultMicroseconds == InvalidQueryResult) { - if (!RHICmdList.GetRenderQueryResult(StartQuery, StartResultMicroseconds, true)) + if (!RHICmdList.GetRenderQueryResult(StartQuery, StartResultMicroseconds, false)) { StartResultMicroseconds = InvalidQueryResult; } @@ -278,7 +279,7 @@ public: } if (EndResultMicroseconds == InvalidQueryResult) { - if (!RHICmdList.GetRenderQueryResult(EndQuery, EndResultMicroseconds, true)) + if (!RHICmdList.GetRenderQueryResult(EndQuery, EndResultMicroseconds, false)) { EndResultMicroseconds = InvalidQueryResult; } diff --git a/Engine/Source/Runtime/Engine/Private/SceneView.cpp b/Engine/Source/Runtime/Engine/Private/SceneView.cpp index 4184f2bd91c5..ef0aec2b6860 100644 --- a/Engine/Source/Runtime/Engine/Private/SceneView.cpp +++ b/Engine/Source/Runtime/Engine/Private/SceneView.cpp @@ -2245,6 +2245,7 @@ void FSceneView::SetupCommonViewUniformBufferParameters( ViewUniformShaderParameters.ViewToTranslatedWorld = InViewMatrices.GetOverriddenInvTranslatedViewMatrix(); ViewUniformShaderParameters.TranslatedWorldToClip = InViewMatrices.GetTranslatedViewProjectionMatrix(); ViewUniformShaderParameters.WorldToClip = InViewMatrices.GetViewProjectionMatrix(); + ViewUniformShaderParameters.ClipToWorld = InViewMatrices.GetInvViewProjectionMatrix(); ViewUniformShaderParameters.TranslatedWorldToView = InViewMatrices.GetOverriddenTranslatedViewMatrix(); ViewUniformShaderParameters.TranslatedWorldToCameraView = InViewMatrices.GetTranslatedViewMatrix(); ViewUniformShaderParameters.CameraViewToTranslatedWorld = InViewMatrices.GetInvTranslatedViewMatrix(); diff --git a/Engine/Source/Runtime/Engine/Private/ShaderCompiler/ShaderCompiler.cpp b/Engine/Source/Runtime/Engine/Private/ShaderCompiler/ShaderCompiler.cpp index ec369a526b94..7403b6ea0f73 100644 --- a/Engine/Source/Runtime/Engine/Private/ShaderCompiler/ShaderCompiler.cpp +++ b/Engine/Source/Runtime/Engine/Private/ShaderCompiler/ShaderCompiler.cpp @@ -3918,9 +3918,6 @@ void VerifyGlobalShaders(EShaderPlatform Platform, bool bLoadedFromCacheFile) // Metal also needs this when using RHI thread because it uses TOneColorVS very early in RHIPostInit() !IsOpenGLPlatform(GMaxRHIShaderPlatform) && !IsVulkanPlatform(GMaxRHIShaderPlatform) && !IsMetalPlatform(GMaxRHIShaderPlatform) && !IsSwitchPlatform(GMaxRHIShaderPlatform) && -#if RHI_RAYTRACING - !GRHISupportsRayTracing && //This is here because DXR is caching its BuiltIn Shaders in PostInit (see FD3D12Device::InitRayTracing) -#endif // RHI_RAYTRACING GShaderCompilingManager->AllowAsynchronousShaderCompiling(); if (!bAllowAsynchronousGlobalShaderCompiling) diff --git a/Engine/Source/Runtime/Engine/Private/ShaderDerivedDataVersion.h b/Engine/Source/Runtime/Engine/Private/ShaderDerivedDataVersion.h index bc3bd721fcc3..2701fa5d9e07 100644 --- a/Engine/Source/Runtime/Engine/Private/ShaderDerivedDataVersion.h +++ b/Engine/Source/Runtime/Engine/Private/ShaderDerivedDataVersion.h @@ -11,5 +11,5 @@ // In case of merge conflicts with DDC versions, you MUST generate a new GUID and set this new // guid as version -#define GLOBALSHADERMAP_DERIVEDDATA_VER TEXT("4FFBFDF601234B80893B59E7779F89E7") -#define MATERIALSHADERMAP_DERIVEDDATA_VER TEXT("FE2EEAF7F8BC456195582BBB000D17CD") +#define GLOBALSHADERMAP_DERIVEDDATA_VER TEXT("73D907EC61CB431EB3FEE5F78C520EF6") +#define MATERIALSHADERMAP_DERIVEDDATA_VER TEXT("3C50894C66CC4D2690D7BCA3554F7F3B") diff --git a/Engine/Source/Runtime/Engine/Private/SkeletalMesh.cpp b/Engine/Source/Runtime/Engine/Private/SkeletalMesh.cpp index 8140ed72f2a1..ae780ac7390e 100644 --- a/Engine/Source/Runtime/Engine/Private/SkeletalMesh.cpp +++ b/Engine/Source/Runtime/Engine/Private/SkeletalMesh.cpp @@ -321,12 +321,12 @@ void USkeletalMesh::PostInitProperties() Super::PostInitProperties(); } -FBoxSphereBounds USkeletalMesh::GetBounds() +FBoxSphereBounds USkeletalMesh::GetBounds() const { return ExtendedBounds; } -FBoxSphereBounds USkeletalMesh::GetImportedBounds() +FBoxSphereBounds USkeletalMesh::GetImportedBounds() const { return ImportedBounds; } @@ -863,6 +863,7 @@ void USkeletalMesh::PostEditChangeProperty(FPropertyChangedEvent& PropertyChange BuildPhysicsData(); } + bool bHasToReregisterComponent = false; if(UProperty* MemberProperty = PropertyChangedEvent.MemberProperty) { if(MemberProperty->GetFName() == GET_MEMBER_NAME_CHECKED(USkeletalMesh, PositiveBoundsExtension) || @@ -871,10 +872,16 @@ void USkeletalMesh::PostEditChangeProperty(FPropertyChangedEvent& PropertyChange // If the bounds extensions change, recalculate extended bounds. ValidateBoundsExtension(); CalculateExtendedBounds(); + bHasToReregisterComponent = true; } } + + if (PropertyThatChanged && PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(USkeletalMesh, PostProcessAnimBlueprint)) + { + bHasToReregisterComponent = true; + } - if(PropertyThatChanged && PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(USkeletalMesh, PostProcessAnimBlueprint)) + if (bHasToReregisterComponent) { TArray ComponentsToReregister; for(TObjectIterator It; It; ++It) @@ -888,6 +895,7 @@ void USkeletalMesh::PostEditChangeProperty(FPropertyChangedEvent& PropertyChange FMultiComponentReregisterContext ReregisterContext(ComponentsToReregister); } + if (PropertyThatChanged && PropertyChangedEvent.MemberProperty) { if (PropertyChangedEvent.MemberProperty->GetFName() == FName(TEXT("SamplingInfo"))) @@ -3133,6 +3141,15 @@ void USkeletalMesh::GetMappableNodeData(TArray& OutNames, TArrayGetPreSkinnedLocalBounds(); + const USkinnedMeshComponent* SkinnedMeshComponent = Cast(Component); if(SkinnedMeshComponent && SkinnedMeshComponent->bPerBoneMotionBlur) { @@ -3727,8 +3747,7 @@ void FSkeletalMeshSceneProxy::DrawStaticElements(FStaticPrimitiveDrawInterface* BatchElement.MaxVertexIndex = LODData.GetNumVertices() - 1; BatchElement.NumPrimitives = Section.NumTriangles; BatchElement.IndexBuffer = LODData.MultiSizeIndexContainer.GetIndexBuffer(); - BatchElement.PrimitiveUniformBuffer = GetUniformBuffer(); - + PDI->DrawMesh(MeshElement, ScreenSize); } } diff --git a/Engine/Source/Runtime/Engine/Private/StaticMesh.cpp b/Engine/Source/Runtime/Engine/Private/StaticMesh.cpp index 53c32e484edd..542ec16c60e4 100644 --- a/Engine/Source/Runtime/Engine/Private/StaticMesh.cpp +++ b/Engine/Source/Runtime/Engine/Private/StaticMesh.cpp @@ -3777,13 +3777,15 @@ void UStaticMesh::CacheMeshData() FStaticMeshSourceModel& SourceModel = SourceModels[LodIndex]; if (!SourceModel.MeshDescriptionBulkData.IsValid()) { - FString MeshDataKey; - if (GetMeshDataKey(LodIndex, MeshDataKey)) + // Legacy assets used to store their source data in the RawMeshBulkData + // Migrate it to the new description if present + if (!SourceModel.RawMeshBulkData->IsEmpty()) { - // If the DDC key doesn't exist, convert the data and save it to DDC - if (!GetDerivedDataCacheRef().CachedDataProbablyExists(*MeshDataKey)) + FString MeshDataKey; + if (GetMeshDataKey(LodIndex, MeshDataKey)) { - if (!SourceModel.RawMeshBulkData->IsEmpty()) + // If the DDC key doesn't exist, convert the data and save it to DDC + if (!GetDerivedDataCacheRef().CachedDataProbablyExists(*MeshDataKey)) { // Get the RawMesh for this LOD FRawMesh TempRawMesh; diff --git a/Engine/Source/Runtime/Engine/Private/Texture.cpp b/Engine/Source/Runtime/Engine/Private/Texture.cpp index be3d6890999b..f0bcfc845d8e 100644 --- a/Engine/Source/Runtime/Engine/Private/Texture.cpp +++ b/Engine/Source/Runtime/Engine/Private/Texture.cpp @@ -199,8 +199,8 @@ void UTexture::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEven } else if (!GDisableAutomaticTextureMaterialUpdateDependencies) { - FMaterialUpdateContext UpdateContext; // Update any material that uses this texture and must force a recompile of cache ressource + TArray MaterialsToUpdate; TSet BaseMaterialsThatUseThisTexture; for (TObjectIterator It; It; ++It) { @@ -214,12 +214,22 @@ void UTexture::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEven { if (Material->IsTextureForceRecompileCacheRessource(this)) { - UpdateContext.AddMaterial(Material); + MaterialsToUpdate.Add(Material); Material->UpdateMaterialShaderCacheAndTextureReferences(); } } } } + + if (MaterialsToUpdate.Num()) + { + FMaterialUpdateContext UpdateContext; + + for (UMaterial* MaterialToUpdate: MaterialsToUpdate) + { + UpdateContext.AddMaterial(MaterialToUpdate); + } + } } NumCinematicMipLevels = FMath::Max( NumCinematicMipLevels, 0 ); diff --git a/Engine/Source/Runtime/Engine/Private/Texture2D.cpp b/Engine/Source/Runtime/Engine/Private/Texture2D.cpp index 45b582958915..358cfa03f9b5 100644 --- a/Engine/Source/Runtime/Engine/Private/Texture2D.cpp +++ b/Engine/Source/Runtime/Engine/Private/Texture2D.cpp @@ -33,6 +33,9 @@ #include "Streaming/Texture2DStreamIn_IO_AsyncReallocate.h" #include "Streaming/Texture2DStreamIn_IO_Virtual.h" #include "Async/AsyncFileHandle.h" +#if WITH_EDITOR +#include "Settings/EditorExperimentalSettings.h" +#endif UTexture2D::UTexture2D(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) @@ -1162,10 +1165,12 @@ bool UTexture2D::ShouldMipLevelsBeForcedResident() const return true; } - if (GIsEditor && (LODGroup == TEXTUREGROUP_Terrain_Heightmap || LODGroup == TEXTUREGROUP_Terrain_Weightmap)) +#if WITH_EDITOR + if (GIsEditor && GetMutableDefault()->bProceduralLandscape && (LODGroup == TEXTUREGROUP_Terrain_Heightmap || LODGroup == TEXTUREGROUP_Terrain_Weightmap)) { return true; } +#endif return false; } @@ -1304,8 +1309,13 @@ void UTexture2D::UpdateTextureRegions(int32 MipIndex, uint32 NumRegions, const F ); } } - DataCleanupFunc(RegionData->SrcData, RegionData->Regions); - delete RegionData; + + // The deletion of source data may need to be deferred to the RHI thread after the updates occur + RHICmdList.EnqueueLambda([RegionData, DataCleanupFunc](FRHICommandList&) + { + DataCleanupFunc(RegionData->SrcData, RegionData->Regions); + delete RegionData; + }); }); } } diff --git a/Engine/Source/Runtime/Engine/Private/TextureDerivedData.cpp b/Engine/Source/Runtime/Engine/Private/TextureDerivedData.cpp index 3394d9b2c547..f19b433e0fb5 100644 --- a/Engine/Source/Runtime/Engine/Private/TextureDerivedData.cpp +++ b/Engine/Source/Runtime/Engine/Private/TextureDerivedData.cpp @@ -1047,7 +1047,7 @@ static void SerializePlatformData( #endif } - for (int32 MipIndex = 0; MipIndex < NumMips && MipIndex < OptionalMips; ++MipIndex ) + for (int32 MipIndex = 0; MipIndex < NumMips && MipIndex < OptionalMips; ++MipIndex ) //-V654 { PlatformData->Mips[MipIndex + FirstMipToSerialize].BulkData.SetBulkDataFlags(BULKDATA_Force_NOT_InlinePayload| BULKDATA_OptionalPayload); } diff --git a/Engine/Source/Runtime/Engine/Private/TimerManager.cpp b/Engine/Source/Runtime/Engine/Private/TimerManager.cpp index fa79887465e1..c1115ac38e52 100644 --- a/Engine/Source/Runtime/Engine/Private/TimerManager.cpp +++ b/Engine/Source/Runtime/Engine/Private/TimerManager.cpp @@ -23,6 +23,12 @@ CSV_DECLARE_CATEGORY_MODULE_EXTERN(CORE_API, Basic); /** Track the last assigned handle globally */ uint64 FTimerManager::LastAssignedSerialNumber = 0; +static float DumpTimerLogsThreshold = 0.f; +static FAutoConsoleVariableRef CVarDumpTimerLogsThreshold( + TEXT("TimerManager.DumpTimerLogsThreshold"), DumpTimerLogsThreshold, + TEXT("Threshold (in milliseconds) after which we log timer info to try and help track down spikes in the timer code. Disabled when set to 0"), + ECVF_Default); + namespace { void DescribeFTimerDataSafely(FOutputDevice& Ar, const FTimerData& Data) @@ -154,7 +160,7 @@ FString FTimerUnifiedDelegate::ToString() const FunctionName = NotBoundName; } - return FString::Printf(TEXT("%s,%s,%s"), bDynDelegate ? TEXT("DELEGATE") : TEXT("DYN DELEGATE"), Object == nullptr ? TEXT("NO OBJ") : *Object->GetPathName(), *FunctionName.ToString()); + return FString::Printf(TEXT("%s,%s,%s"), bDynDelegate ? TEXT("DYN DELEGATE") : TEXT("DELEGATE"), Object == nullptr ? TEXT("NO OBJ") : *Object->GetPathName(), *FunctionName.ToString()); } // --------------------------------- @@ -547,6 +553,9 @@ void FTimerManager::Tick(float DeltaTime) return; } + const double StartTime = FPlatformTime::Seconds(); + bool bDumpTimerLogsThresholdExceeded = false; + InternalTime += DeltaTime; UWorld* const OwningWorld = OwningGameInstance ? OwningGameInstance->GetWorld() : nullptr; @@ -571,6 +580,11 @@ void FTimerManager::Tick(float DeltaTime) { // Timer has expired! Fire the delegate, then handle potential looping. + if (bDumpTimerLogsThresholdExceeded) + { + DescribeFTimerDataSafely(*GLog, *Top); + } + // Set the relevant level context for this timer const int32 LevelCollectionIndex = OwningWorld ? OwningWorld->FindCollectionIndexByType(Top->LevelCollection) : INDEX_NONE; @@ -610,6 +624,26 @@ void FTimerManager::Tick(float DeltaTime) } } + if (DumpTimerLogsThreshold > 0.f && !bDumpTimerLogsThresholdExceeded) + { + // help us hunt down outliers that cause our timer manager times to spike. Recommended that users set meaningful DumpTimerLogsThresholds in appropriate ini files if they are seeing spikes in the timer manager. + const double DeltaT = (FPlatformTime::Seconds() - StartTime) * 1000.f; + if (DeltaT >= DumpTimerLogsThreshold) + { + bDumpTimerLogsThresholdExceeded = true; + UE_LOG(LogEngine, Log, TEXT("TimerManager's time threshold of %.2fms exceeded with a deltaT of %.4f, dumping current timer data."), DumpTimerLogsThreshold, DeltaT); + + if (Top) + { + DescribeFTimerDataSafely(*GLog, *Top); + } + else + { + UE_LOG(LogEngine, Log, TEXT("There was no timer data for the first timer after exceeding the time threshold!")); + } + } + } + // test to ensure it didn't get cleared during execution if (Top) { diff --git a/Engine/Source/Runtime/Engine/Private/UnrealEngine.cpp b/Engine/Source/Runtime/Engine/Private/UnrealEngine.cpp index e77ac843cd56..d20d9357c4fe 100644 --- a/Engine/Source/Runtime/Engine/Private/UnrealEngine.cpp +++ b/Engine/Source/Runtime/Engine/Private/UnrealEngine.cpp @@ -12018,13 +12018,14 @@ bool UEngine::LoadMap( FWorldContext& WorldContext, FURL URL, class UPendingNetG // send a callback message FCoreUObjectDelegates::PreLoadMap.Broadcast(URL.Map); + // make sure there is a matching PostLoadMap() no matter how we exit struct FPostLoadMapCaller { - bool bCalled; FPostLoadMapCaller() : bCalled(false) {} + ~FPostLoadMapCaller() { if (!bCalled) @@ -12032,6 +12033,19 @@ bool UEngine::LoadMap( FWorldContext& WorldContext, FURL URL, class UPendingNetG FCoreUObjectDelegates::PostLoadMapWithWorld.Broadcast(nullptr); } } + + void Broadcast(UWorld* World) + { + if (ensure(!bCalled)) + { + bCalled = true; + FCoreUObjectDelegates::PostLoadMapWithWorld.Broadcast(World); + } + } + + private: + bool bCalled; + } PostLoadMapCaller; // Cancel any pending texture streaming requests. This avoids a significant delay on consoles @@ -12074,7 +12088,7 @@ bool UEngine::LoadMap( FWorldContext& WorldContext, FURL URL, class UPendingNetG // Unload the current world if( WorldContext.World() ) { - WorldContext.World()->bIsTearingDown = true; + WorldContext.World()->BeginTearingDown(); if(!URL.HasOption(TEXT("quiet")) ) { @@ -12506,8 +12520,7 @@ bool UEngine::LoadMap( FWorldContext& WorldContext, FURL URL, class UPendingNetG WorldContext.World()->BeginPlay(); // send a callback message - PostLoadMapCaller.bCalled = true; - FCoreUObjectDelegates::PostLoadMapWithWorld.Broadcast(WorldContext.World()); + PostLoadMapCaller.Broadcast(WorldContext.World()); WorldContext.World()->bWorldWasLoadedThisTick = true; diff --git a/Engine/Source/Runtime/Engine/Private/VoiceConfig.cpp b/Engine/Source/Runtime/Engine/Private/VoiceConfig.cpp index 2809171bb82d..10098080c66b 100644 --- a/Engine/Source/Runtime/Engine/Private/VoiceConfig.cpp +++ b/Engine/Source/Runtime/Engine/Private/VoiceConfig.cpp @@ -23,7 +23,9 @@ static TAutoConsoleVariable CVarVoiceSilenceDetectionThreshold(TEXT("voic int32 UVOIPStatics::GetVoiceSampleRate() { -#if USE_DEFAULT_VOICE_SAMPLE_RATE +#if PLATFORM_UNIX + return PLATFORM_LINUX ? 16000 : 48000; +#elif USE_DEFAULT_VOICE_SAMPLE_RATE return (int32) 16000; #else static bool bRetrievedSampleRate = false; diff --git a/Engine/Source/Runtime/Engine/Private/World.cpp b/Engine/Source/Runtime/Engine/Private/World.cpp index 5050b9121a54..434630499768 100644 --- a/Engine/Source/Runtime/Engine/Private/World.cpp +++ b/Engine/Source/Runtime/Engine/Private/World.cpp @@ -2408,6 +2408,12 @@ void UWorld::AddToWorld( ULevel* Level, const FTransform& LevelTransform, bool b #endif // PERF_TRACK_DETAILED_ASYNC_STATS } +void UWorld::BeginTearingDown() +{ + bIsTearingDown = true; + UE_LOG(LogWorld, Log, TEXT("BeginTearingDown for %s"), *GetOutermost()->GetName()); +} + void UWorld::RemoveFromWorld( ULevel* Level, bool bAllowIncrementalRemoval ) { SCOPE_CYCLE_COUNTER(STAT_RemoveFromWorldTime); @@ -2422,6 +2428,8 @@ void UWorld::RemoveFromWorld( ULevel* Level, bool bAllowIncrementalRemoval ) // If the level may be removed incrementally then there must also be no level pending visibility if ( ((CurrentLevelPendingVisibility == nullptr) || (!bAllowIncrementalRemoval && (CurrentLevelPendingVisibility != Level))) && Level->bIsVisible ) { + UE_LOG(LogWorld, Log, TEXT("UWorld::RemoveFromWorld for %s"), *Level->GetOutermost()->GetName()); + // Keep track of timing. double StartTime = FPlatformTime::Seconds(); @@ -5835,8 +5843,7 @@ UWorld* FSeamlessTravelHandler::Tick() CurrentWorld->GetGameState()->SeamlessTravelTransitionCheckpoint(!bSwitchedToDefaultMap); } - - CurrentWorld->bIsTearingDown = true; + CurrentWorld->BeginTearingDown(); // If it's not still playing, destroy the demo net driver before we start renaming actors. if ( CurrentWorld->DemoNetDriver && !CurrentWorld->DemoNetDriver->IsPlaying() && !CurrentWorld->DemoNetDriver->bRecordMapChanges) diff --git a/Engine/Source/Runtime/Engine/Public/Animation/AnimTypes.h b/Engine/Source/Runtime/Engine/Public/Animation/AnimTypes.h index 6b378aa0def6..b64c3a3114cd 100644 --- a/Engine/Source/Runtime/Engine/Public/Animation/AnimTypes.h +++ b/Engine/Source/Runtime/Engine/Public/Animation/AnimTypes.h @@ -177,6 +177,12 @@ public: SkipFrames = InMaxSkippedFrames; } + /** Gets the number of expected frames to be skipped by URO */ + int16 GetMaxSkippedFrames() const + { + return SkipFrames; + } + /** Clear the internal counters and frame skip */ void Reset() { diff --git a/Engine/Source/Runtime/Engine/Public/AnimationUtils.h b/Engine/Source/Runtime/Engine/Public/AnimationUtils.h index fdd2a39978c5..666aaeb3949b 100644 --- a/Engine/Source/Runtime/Engine/Public/AnimationUtils.h +++ b/Engine/Source/Runtime/Engine/Public/AnimationUtils.h @@ -276,10 +276,10 @@ public: */ ENGINE_API static UAnimCompress* GetDefaultAnimationCompressionAlgorithm(); -#if WITH_EDITOR /** Returns the default animation curve compression settings, can never by null. */ ENGINE_API static UAnimCurveCompressionSettings* GetDefaultAnimationCurveCompressionSettings(); +#if WITH_EDITOR /** * Compresses the animation curves within a sequence with the chosen settings. * Note: This modifies the sequence. diff --git a/Engine/Source/Runtime/Engine/Public/AudioDevice.h b/Engine/Source/Runtime/Engine/Public/AudioDevice.h index 613086e8d1a3..711940d8d512 100644 --- a/Engine/Source/Runtime/Engine/Public/AudioDevice.h +++ b/Engine/Source/Runtime/Engine/Public/AudioDevice.h @@ -1397,14 +1397,26 @@ public: return Effects; } - TMap GetSoundMixModifiers() + const TMap& GetSoundMixModifiers() const { return SoundMixModifiers; } - void SetSoundMixModifiers(TMap& InSoundMixModifiers) + const TArray& GetPrevPassiveSoundMixModifiers() const + { + return PrevPassiveSoundMixModifiers; + } + + USoundMix* GetDefaultBaseSoundMixModifier() + { + return DefaultBaseSoundMix; + } + + void SetSoundMixModifiers(const TMap& InSoundMixModifiers, const TArray& InPrevPassiveSoundMixModifiers, USoundMix* InDefaultBaseSoundMix) { SoundMixModifiers = InSoundMixModifiers; + PrevPassiveSoundMixModifiers = InPrevPassiveSoundMixModifiers; + DefaultBaseSoundMix = InDefaultBaseSoundMix; } private: diff --git a/Engine/Source/Runtime/Engine/Public/DebugViewModeHelpers.h b/Engine/Source/Runtime/Engine/Public/DebugViewModeHelpers.h index 7ffb3f8c5a8a..0e46b86093e2 100644 --- a/Engine/Source/Runtime/Engine/Public/DebugViewModeHelpers.h +++ b/Engine/Source/Runtime/Engine/Public/DebugViewModeHelpers.h @@ -11,6 +11,7 @@ #include "SceneTypes.h" class UMaterialInterface; +struct FSlowTask; /** * Enumeration for different Quad Overdraw visualization mode. @@ -41,10 +42,10 @@ FORCEINLINE bool AllowDebugViewShaderMode(EDebugViewShaderMode ShaderMode, EShad #endif ENGINE_API int32 GetNumActorsInWorld(UWorld* InWorld); -ENGINE_API bool GetUsedMaterialsInWorld(UWorld* InWorld, OUT TSet& OutMaterials, struct FSlowTask& Task); -ENGINE_API bool CompileDebugViewModeShaders(EDebugViewShaderMode Mode, EMaterialQualityLevel::Type QualityLevel, ERHIFeatureLevel::Type FeatureLevel, bool bFullRebuild, bool bWaitForPreviousShaders, TSet& Materials, FSlowTask& ProgressTask); +ENGINE_API bool GetUsedMaterialsInWorld(UWorld* InWorld, OUT TSet& OutMaterials, FSlowTask* Task); +ENGINE_API bool CompileDebugViewModeShaders(EDebugViewShaderMode Mode, EMaterialQualityLevel::Type QualityLevel, ERHIFeatureLevel::Type FeatureLevel, bool bFullRebuild, bool bWaitForPreviousShaders, TSet& Materials, FSlowTask* ProgressTask); ENGINE_API bool HasMissingDebugViewModeShaders(bool bClearFlag); -ENGINE_API bool WaitForShaderCompilation(const FText& Message, FSlowTask& ProgressTask); +ENGINE_API bool WaitForShaderCompilation(const FText& Message, FSlowTask* ProgressTask); diff --git a/Engine/Source/Runtime/Engine/Public/ImageUtils.h b/Engine/Source/Runtime/Engine/Public/ImageUtils.h index 9bfce513dae5..aef5b6ce4cbf 100644 --- a/Engine/Source/Runtime/Engine/Public/ImageUtils.h +++ b/Engine/Source/Runtime/Engine/Public/ImageUtils.h @@ -62,6 +62,19 @@ public: */ ENGINE_API static void ImageResize(int32 SrcWidth, int32 SrcHeight, const TArray &SrcData, int32 DstWidth, int32 DstHeight, TArray &DstData, bool bLinearSpace ); + /** + * Resizes the given image using a simple average filter and stores it in the destination array. This version constrains aspect ratio. + * Accepts TArrayViews but requires that DstData be pre-sized appropriately + * + * @param SrcWidth Source image width. + * @param SrcHeight Source image height. + * @param SrcData Source image data. + * @param DstWidth Destination image width. + * @param DstHeight Destination image height. + * @param DstData Destination image data. (must already be sized to DstWidth*DstHeight) + */ + ENGINE_API static void ImageResize(int32 SrcWidth, int32 SrcHeight, const TArrayView &SrcData, int32 DstWidth, int32 DstHeight, const TArrayView &DstData, bool bLinearSpace); + /** * Creates a 2D texture from a array of raw color data. * diff --git a/Engine/Source/Runtime/Engine/Public/MaterialCompiler.h b/Engine/Source/Runtime/Engine/Public/MaterialCompiler.h index 4841fb0ca0b6..da40535668df 100644 --- a/Engine/Source/Runtime/Engine/Public/MaterialCompiler.h +++ b/Engine/Source/Runtime/Engine/Public/MaterialCompiler.h @@ -139,7 +139,8 @@ public: virtual int32 WorldPosition(EWorldPositionIncludedOffsets WorldPositionIncludedOffsets) = 0; virtual int32 ObjectWorldPosition() = 0; virtual int32 ObjectRadius() = 0; - virtual int32 ObjectBounds() = 0; + virtual int32 ObjectBounds() = 0; + virtual int32 PreSkinnedLocalBounds() = 0; virtual int32 DistanceCullFade() = 0; virtual int32 ActorWorldPosition() = 0; virtual int32 ParticleMacroUV() = 0; @@ -391,6 +392,7 @@ public: virtual int32 ObjectWorldPosition() override { return Compiler->ObjectWorldPosition(); } virtual int32 ObjectRadius() override { return Compiler->ObjectRadius(); } virtual int32 ObjectBounds() override { return Compiler->ObjectBounds(); } + virtual int32 PreSkinnedLocalBounds() override { return Compiler->PreSkinnedLocalBounds(); } virtual int32 DistanceCullFade() override { return Compiler->DistanceCullFade(); } virtual int32 ActorWorldPosition() override { return Compiler->ActorWorldPosition(); } virtual int32 ParticleMacroUV() override { return Compiler->ParticleMacroUV(); } diff --git a/Engine/Source/Runtime/Engine/Public/MaterialSceneTextureId.h b/Engine/Source/Runtime/Engine/Public/MaterialSceneTextureId.h new file mode 100644 index 000000000000..8760f2f5cf8b --- /dev/null +++ b/Engine/Source/Runtime/Engine/Public/MaterialSceneTextureId.h @@ -0,0 +1,72 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" +#include "MaterialSceneTextureId.generated.h" + +/** like EPassInputId but can expose more e.g. GBuffer */ +UENUM() +enum ESceneTextureId +{ + /** Scene color, normal post process passes should use PostProcessInput0 */ + PPI_SceneColor UMETA(DisplayName="SceneColor"), + /** Scene depth, single channel, contains the linear depth of the opaque objects */ + PPI_SceneDepth UMETA(DisplayName="SceneDepth"), + /** Material diffuse, RGB color (computed from GBuffer) */ + PPI_DiffuseColor UMETA(DisplayName="DiffuseColor"), + /** Material specular, RGB color (computed from GBuffer) */ + PPI_SpecularColor UMETA(DisplayName="SpecularColor"), + /** Material subsurface, RGB color (GBuffer, only for some ShadingModels) */ + PPI_SubsurfaceColor UMETA(DisplayName="SubsurfaceColor"), + /** Material base, RGB color (GBuffer), can be modified on read by the ShadingModel, consider StoredBasedColor */ + PPI_BaseColor UMETA(DisplayName="BaseColor (for lighting)"), + /** Material specular, single channel (GBuffer), can be modified on read by the ShadingModel, consider StoredSpecular */ + PPI_Specular UMETA(DisplayName="Specular (for lighting)"), + /** Material metallic, single channel (GBuffer) */ + PPI_Metallic UMETA(DisplayName="Metallic"), + /** Normal, RGB in -1..1 range, not normalized (GBuffer) */ + PPI_WorldNormal UMETA(DisplayName="WorldNormal"), + /** Not yet supported */ + PPI_SeparateTranslucency UMETA(DisplayName="SeparateTranslucency"), + /** Material opacity, single channel (GBuffer) */ + PPI_Opacity UMETA(DisplayName="Opacity"), + /** Material roughness, single channel (GBuffer) */ + PPI_Roughness UMETA(DisplayName="Roughness"), + /** Material ambient occlusion, single channel (GBuffer) */ + PPI_MaterialAO UMETA(DisplayName="MaterialAO"), + /** Scene depth, single channel, contains the linear depth of the opaque objects rendered with CustomDepth (mesh property) */ + PPI_CustomDepth UMETA(DisplayName="CustomDepth"), + /** Input #0 of this postprocess pass, usually the only one hooked up */ + PPI_PostProcessInput0 UMETA(DisplayName="PostProcessInput0"), + /** Input #1 of this postprocess pass, usually not used */ + PPI_PostProcessInput1 UMETA(DisplayName="PostProcessInput1"), + /** Input #2 of this postprocess pass, usually not used */ + PPI_PostProcessInput2 UMETA(DisplayName="PostProcessInput2"), + /** Input #3 of this postprocess pass, usually not used */ + PPI_PostProcessInput3 UMETA(DisplayName="PostProcessInput3"), + /** Input #4 of this postprocess pass, usually not used */ + PPI_PostProcessInput4 UMETA(DisplayName="PostProcessInput4"), + /** Input #5 of this postprocess pass, usually not used */ + PPI_PostProcessInput5 UMETA(DisplayName="PostProcessInput5"), + /** Input #6 of this postprocess pass, usually not used */ + PPI_PostProcessInput6 UMETA(DisplayName="PostProcessInput6"), + /** Decal Mask, single bit (was moved to stencil for better performance, not accessible at the moment) */ + PPI_DecalMask UMETA(DisplayName="Decal Mask"), + /** Shading model */ + PPI_ShadingModelColor UMETA(DisplayName="Shading Model Color"), + /** Shading model ID */ + PPI_ShadingModelID UMETA(DisplayName="Shading Model ID"), + /** Ambient Occlusion, single channel */ + PPI_AmbientOcclusion UMETA(DisplayName="Ambient Occlusion"), + /** Scene stencil, contains CustomStencil mesh property of the opaque objects rendered with CustomDepth */ + PPI_CustomStencil UMETA(DisplayName="CustomStencil"), + /** Material base, RGB color (GBuffer) */ + PPI_StoredBaseColor UMETA(DisplayName="BaseColor (as stored in GBuffer)"), + /** Material specular, single channel (GBuffer) */ + PPI_StoredSpecular UMETA(DisplayName="Specular (as stored in GBuffer)"), + /** Scene Velocity */ + PPI_Velocity UMETA(DisplayName="Velocity"), +}; diff --git a/Engine/Source/Runtime/Engine/Public/Net/RepLayout.h b/Engine/Source/Runtime/Engine/Public/Net/RepLayout.h index a85be027a2fb..07152a9e5157 100644 --- a/Engine/Source/Runtime/Engine/Public/Net/RepLayout.h +++ b/Engine/Source/Runtime/Engine/Public/Net/RepLayout.h @@ -496,6 +496,8 @@ public: private: uint32 LastReplicationFrame; + uint32 LastInitialReplicationFrame; + FRepChangelistState RepChangelistState; }; @@ -1294,6 +1296,7 @@ public: FReceivingRepState* RESTRICT RepState, UPackageMap* PackageMap, UObject* Object, + bool& bCalledPreNetReceive, bool& bOutSomeObjectsWereMapped, bool& bOutHasMoreUnmapped) const; @@ -1305,7 +1308,8 @@ public: bool& bOutSomeObjectsWereMapped, bool& bOutHasMoreUnmapped) const { - UpdateUnmappedObjects(RepState->GetReceivingRepState(), PackageMap, Object, bOutSomeObjectsWereMapped, bOutHasMoreUnmapped); + bool bCalledPreNetReceive = false; + UpdateUnmappedObjects(RepState->GetReceivingRepState(), PackageMap, Object, bCalledPreNetReceive, bOutSomeObjectsWereMapped, bOutHasMoreUnmapped); } /** diff --git a/Engine/Source/Runtime/Engine/Public/Physics/Experimental/PhysScene_ImmediatePhysX.h b/Engine/Source/Runtime/Engine/Public/Physics/Experimental/PhysScene_ImmediatePhysX.h index 6b73d117b682..b1caf796cf60 100644 --- a/Engine/Source/Runtime/Engine/Public/Physics/Experimental/PhysScene_ImmediatePhysX.h +++ b/Engine/Source/Runtime/Engine/Public/Physics/Experimental/PhysScene_ImmediatePhysX.h @@ -11,6 +11,7 @@ #include "PhysScene_PhysX.h" #include "PhysicsEngine/PhysicsSettingsEnums.h" #include "GenericPhysicsInterface.h" +#include "HAL/LowLevelMemTracker.h" struct FConstraintBrokenDelegateData; class IPhysicsReplicationFactory; @@ -174,6 +175,7 @@ class FPhysScene_ImmediatePhysX : public FGenericPhysicsInterface FPageStruct* AllocPage() { + LLM_SCOPE(ELLMTag::PhysX); FPageStruct* ReturnPage = (FPageStruct*)FMemory::Malloc(sizeof(FPageStruct), 16); new(ReturnPage) FPageStruct(); diff --git a/Engine/Source/Runtime/Engine/Public/Physics/ImmediatePhysics/ImmediatePhysicsLinearBlockAllocator.h b/Engine/Source/Runtime/Engine/Public/Physics/ImmediatePhysics/ImmediatePhysicsLinearBlockAllocator.h index 68007cff0977..c565d2a424c0 100644 --- a/Engine/Source/Runtime/Engine/Public/Physics/ImmediatePhysics/ImmediatePhysicsLinearBlockAllocator.h +++ b/Engine/Source/Runtime/Engine/Public/Physics/ImmediatePhysics/ImmediatePhysicsLinearBlockAllocator.h @@ -1,6 +1,7 @@ // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once +#include "HAL/LowLevelMemTracker.h" namespace ImmediatePhysics { @@ -36,6 +37,7 @@ struct FLinearBlockAllocator FPageStruct* AllocPage() { + LLM_SCOPE(ELLMTag::PhysX); FPageStruct* ReturnPage = (FPageStruct*)FMemory::Malloc(sizeof(FPageStruct), 16); new(ReturnPage) FPageStruct(); diff --git a/Engine/Source/Runtime/Engine/Public/PrimitiveSceneProxy.h b/Engine/Source/Runtime/Engine/Public/PrimitiveSceneProxy.h index b74253bd3906..6a517aa4d15d 100644 --- a/Engine/Source/Runtime/Engine/Public/PrimitiveSceneProxy.h +++ b/Engine/Source/Runtime/Engine/Public/PrimitiveSceneProxy.h @@ -98,14 +98,14 @@ public: class FHeightfieldComponentDescription { public: - FVector4 HeightfieldScaleBias; - FVector4 MinMaxUV; - FMatrix LocalToWorld; - FVector2D LightingAtlasLocation; - FIntRect HeightfieldRect; + FVector4 HeightfieldScaleBias = FVector4(ForceInit); + FVector4 MinMaxUV = FVector4(ForceInit); + FMatrix LocalToWorld = FMatrix::Identity; + FVector2D LightingAtlasLocation = FVector2D(ForceInit); + FIntRect HeightfieldRect; // Default initialized - int32 NumSubsections; - FVector4 SubsectionScaleAndBias; + int32 NumSubsections = 0; + FVector4 SubsectionScaleAndBias = FVector4(ForceInit); FHeightfieldComponentDescription(const FMatrix& InLocalToWorld) : LocalToWorld(InLocalToWorld) @@ -425,6 +425,7 @@ public: inline bool IsLocalToWorldDeterminantNegative() const { return bIsLocalToWorldDeterminantNegative; } inline const FBoxSphereBounds& GetBounds() const { return Bounds; } inline const FBoxSphereBounds& GetLocalBounds() const { return LocalBounds; } + virtual FBoxSphereBounds GetPreSkinnedLocalBounds() const { return LocalBounds; } inline FName GetOwnerName() const { return OwnerName; } inline FName GetResourceName() const { return ResourceName; } inline FName GetLevelName() const { return LevelName; } diff --git a/Engine/Source/Runtime/Engine/Public/PrimitiveUniformShaderParameters.h b/Engine/Source/Runtime/Engine/Public/PrimitiveUniformShaderParameters.h index 3c0b1d4a30c1..ce7380c17547 100644 --- a/Engine/Source/Runtime/Engine/Public/PrimitiveUniformShaderParameters.h +++ b/Engine/Source/Runtime/Engine/Public/PrimitiveUniformShaderParameters.h @@ -33,6 +33,7 @@ BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FPrimitiveUniformShaderParameters,ENGINE_AP SHADER_PARAMETER_EX(FVector4,NonUniformScale,EShaderPrecisionModifier::Half) SHADER_PARAMETER(FVector, LocalObjectBoundsMin) // This is used in a custom material function (ObjectLocalBounds.uasset) SHADER_PARAMETER(FVector, LocalObjectBoundsMax) // This is used in a custom material function (ObjectLocalBounds.uasset) + SHADER_PARAMETER(FVector, PreSkinnedLocalBounds) // Local space bounds, pre-skinning SHADER_PARAMETER(uint32,LightingChannelMask) SHADER_PARAMETER(uint32,LightmapDataIndex) SHADER_PARAMETER(int32, SingleCaptureIndex) @@ -45,6 +46,7 @@ inline FPrimitiveUniformShaderParameters GetPrimitiveUniformShaderParameters( FVector ActorPosition, const FBoxSphereBounds& WorldBounds, const FBoxSphereBounds& LocalBounds, + const FBoxSphereBounds& PreSkinnedLocalBounds, bool bReceivesDecals, bool bHasDistanceFieldRepresentation, bool bHasCapsuleRepresentation, @@ -66,6 +68,7 @@ inline FPrimitiveUniformShaderParameters GetPrimitiveUniformShaderParameters( Result.ObjectBounds = WorldBounds.BoxExtent; Result.LocalObjectBoundsMin = LocalBounds.GetBoxExtrema(0); // 0 == minimum Result.LocalObjectBoundsMax = LocalBounds.GetBoxExtrema(1); // 1 == maximum + Result.PreSkinnedLocalBounds = PreSkinnedLocalBounds.BoxExtent; Result.ObjectOrientation = LocalToWorld.GetUnitAxis( EAxis::Z ); Result.ActorWorldPosition = ActorPosition; Result.LightingChannelMask = LightingChannelMask; @@ -97,10 +100,35 @@ inline FPrimitiveUniformShaderParameters GetPrimitiveUniformShaderParameters( return Result; } +/** Initializes the primitive uniform shader parameters. Pre-skinned local bounds default to LocalBounds */ +inline FPrimitiveUniformShaderParameters GetPrimitiveUniformShaderParameters( + const FMatrix& LocalToWorld, + const FMatrix& PreviousLocalToWorld, + FVector ActorPosition, + const FBoxSphereBounds& WorldBounds, + const FBoxSphereBounds& LocalBounds, + bool bReceivesDecals, + bool bHasDistanceFieldRepresentation, + bool bHasCapsuleRepresentation, + bool bUseSingleSampleShadowFromStationaryLights, + bool bUseVolumetricLightmap, + bool bUseEditorDepthTest, + uint32 LightingChannelMask, + float LpvBiasMultiplier, + uint32 LightmapDataIndex, + int32 SingleCaptureIndex +) +{ + // Pass through call + return GetPrimitiveUniformShaderParameters(LocalToWorld, PreviousLocalToWorld, ActorPosition, WorldBounds, LocalBounds, LocalBounds, bReceivesDecals, bHasDistanceFieldRepresentation, bHasCapsuleRepresentation, + bUseSingleSampleShadowFromStationaryLights, bUseVolumetricLightmap, bUseEditorDepthTest, LightingChannelMask, LpvBiasMultiplier, LightmapDataIndex, SingleCaptureIndex); +} + inline TUniformBufferRef CreatePrimitiveUniformBufferImmediate( const FMatrix& LocalToWorld, const FBoxSphereBounds& WorldBounds, const FBoxSphereBounds& LocalBounds, + const FBoxSphereBounds& PreSkinnedLocalBounds, bool bReceivesDecals, bool bUseEditorDepthTest, float LpvBiasMultiplier = 1.0f @@ -108,7 +136,7 @@ inline TUniformBufferRef CreatePrimitiveUnifo { check(IsInRenderingThread()); return TUniformBufferRef::CreateUniformBufferImmediate( - GetPrimitiveUniformShaderParameters(LocalToWorld, LocalToWorld, WorldBounds.Origin, WorldBounds, LocalBounds, bReceivesDecals, false, false, false, false, bUseEditorDepthTest, GetDefaultLightingChannelMask(), LpvBiasMultiplier, INDEX_NONE, INDEX_NONE), + GetPrimitiveUniformShaderParameters(LocalToWorld, LocalToWorld, WorldBounds.Origin, WorldBounds, LocalBounds, PreSkinnedLocalBounds, bReceivesDecals, false, false, false, false, bUseEditorDepthTest, GetDefaultLightingChannelMask(), LpvBiasMultiplier, INDEX_NONE, INDEX_NONE), UniformBuffer_MultiFrame ); } @@ -156,7 +184,7 @@ extern ENGINE_API TGlobalResource GIdentityPrim struct FPrimitiveSceneShaderData { // Must match usf - enum { PrimitiveDataStrideInFloat4s = 26 }; + enum { PrimitiveDataStrideInFloat4s = 27 }; FVector4 Data[PrimitiveDataStrideInFloat4s]; diff --git a/Engine/Source/Runtime/Engine/Public/RectLightSceneProxy.h b/Engine/Source/Runtime/Engine/Public/RectLightSceneProxy.h index 6f5a5bc5be4d..dc63d0521037 100644 --- a/Engine/Source/Runtime/Engine/Public/RectLightSceneProxy.h +++ b/Engine/Source/Runtime/Engine/Public/RectLightSceneProxy.h @@ -11,6 +11,16 @@ #include "LocalLightSceneProxy.h" #include "SceneManagement.h" +struct FRectLightRayTracingData +{ +#if RHI_RAYTRACING + FRWBuffer RectLightMipTree; + FIntVector RectLightMipTreeDimensions; + FGuid TextureLightingGuid; + bool bInitialised = false; +#endif +}; + class FRectLightSceneProxy : public FLocalLightSceneProxy { public: @@ -18,11 +28,8 @@ public: float SourceHeight; float BarnDoorAngle; float BarnDoorLength; + FRectLightRayTracingData* RayTracingData; // Render thread data only UTexture* SourceTexture; -#if RHI_RAYTRACING - FRWBuffer RectLightMipTree; - FIntVector RectLightMipTreeDimensions; -#endif FRectLightSceneProxy(const URectLightComponent* Component); virtual ~FRectLightSceneProxy(); @@ -39,8 +46,4 @@ public: * @return True if the whole-scene projected shadow should be used. */ virtual bool GetWholeSceneProjectedShadowInitializer(const FSceneViewFamily& ViewFamily, TArray >& OutInitializers) const; - -#if RHI_RAYTRACING - void BuildRectLightMipTree(FRHICommandListImmediate& RHICmdList); -#endif }; \ No newline at end of file diff --git a/Engine/Source/Runtime/Engine/Public/Rendering/SkeletalMeshLODImporterData.cpp b/Engine/Source/Runtime/Engine/Public/Rendering/SkeletalMeshLODImporterData.cpp index f6011584fd8f..cb34e21149dd 100644 --- a/Engine/Source/Runtime/Engine/Public/Rendering/SkeletalMeshLODImporterData.cpp +++ b/Engine/Source/Runtime/Engine/Public/Rendering/SkeletalMeshLODImporterData.cpp @@ -224,35 +224,55 @@ bool FSkeletalMeshImportData::ApplyRigToGeo(FSkeletalMeshImportData& Other) TArray NearestWedges; FVector SearchPosition = Points[NewVertexIndex]; OctreeQueryHelper.FindNearestWedgeIndexes(SearchPosition, NearestWedges); + //The best old wedge match is base on those weight ratio + const int32 UVWeightRatioIndex = 0; + const int32 NormalWeightRatioIndex = 1; + const float MatchWeightRatio[3] = { 0.99f, 0.01f }; if (NearestWedges.Num() > 0) { - float MinDistance = MAX_FLT; - float MinNormalDiff = MAX_FLT; int32 BestOldVertexIndex = INDEX_NONE; + float MaxUVDistance = 0.0f; + float MaxNormalDelta = 0.0f; + TArray UvDistances; + UvDistances.Reserve(NearestWedges.Num()); + TArray NormalDeltas; + NormalDeltas.Reserve(NearestWedges.Num()); for (const FWedgeInfo& WedgeInfo : NearestWedges) { int32 OldWedgeIndex = WedgeInfo.WedgeIndex; int32 OldVertexIndex = Other.Wedges[OldWedgeIndex].VertexIndex; int32 OldFaceIndex = (OldWedgeIndex / 3); int32 OldFaceCorner = (OldWedgeIndex % 3); - FVector OldNormal = Other.Faces[OldFaceIndex].TangentZ[OldFaceCorner]; - - const FVector& OtherPosition = Other.Points[OldVertexIndex]; - float VectorDelta = FVector::DistSquared(OtherPosition, SearchPosition); - if (VectorDelta <= (MinDistance + KINDA_SMALL_NUMBER)) + const FVector2D& OldUV = Other.Wedges[OldWedgeIndex].UVs[0]; + const FVector& OldNormal = Other.Faces[OldFaceIndex].TangentZ[OldFaceCorner]; + float UVDelta = FVector2D::DistSquared(CurWedgeUV, OldUV); + float NormalDelta = FMath::Abs(FMath::Acos(FVector::DotProduct(NewNormal, OldNormal))); + if (UVDelta > MaxUVDistance) { - if (VectorDelta < MinDistance - KINDA_SMALL_NUMBER) - { - MinDistance = VectorDelta; - MinNormalDiff = MAX_FLT; - } - float AngleDiff = FMath::Abs(FMath::Acos(FVector::DotProduct(NewNormal, OldNormal))); - if (AngleDiff < MinNormalDiff) - { - MinNormalDiff = AngleDiff; - BestOldVertexIndex = OldVertexIndex; - } + MaxUVDistance = UVDelta; } + UvDistances.Add(UVDelta); + if (NormalDelta > MaxNormalDelta) + { + MaxNormalDelta = NormalDelta; + } + NormalDeltas.Add(NormalDelta); + } + float BestContribution = 0.0f; + for (int32 NearestWedgeIndex = 0; NearestWedgeIndex < UvDistances.Num(); ++NearestWedgeIndex) + { + float Contribution = ((MaxUVDistance - UvDistances[NearestWedgeIndex])/MaxUVDistance)*MatchWeightRatio[UVWeightRatioIndex]; + Contribution += ((MaxNormalDelta - NormalDeltas[NearestWedgeIndex]) / MaxNormalDelta)*MatchWeightRatio[NormalWeightRatioIndex]; + if (Contribution > BestContribution) + { + BestContribution = Contribution; + BestOldVertexIndex = Other.Wedges[NearestWedges[NearestWedgeIndex].WedgeIndex].VertexIndex; + } + } + if (BestOldVertexIndex == INDEX_NONE) + { + //Use the first NearestWedges entry, we end up here because all NearestWedges entries all equals, so the ratio will be zero in such a case + BestOldVertexIndex = Other.Wedges[NearestWedges[0].WedgeIndex].VertexIndex; } OldToNewRemap[BestOldVertexIndex].AddUnique(NewVertexIndex); } @@ -611,7 +631,7 @@ void FOctreeQueryHelper::FindNearestWedgeIndexes(const FVector& SearchPosition, float MinSquaredDistance = MAX_FLT; OutNearestWedges.Empty(); - FVector Extend(1.0f); + FVector Extend(2.0f); for (int i = 0; i < 2; ++i) { TWedgeInfoPosOctree::TConstIterator<> OctreeIter((*WedgePosOctree)); @@ -636,11 +656,8 @@ void FOctreeQueryHelper::FindNearestWedgeIndexes(const FVector& SearchPosition, for (const FWedgeInfo& WedgeInfo : CurNode.GetElements()) { float VectorDelta = FVector::DistSquared(SearchPosition, WedgeInfo.Position); - if (VectorDelta <= (MinSquaredDistance + SMALL_NUMBER)) - { - MinSquaredDistance = FMath::Min(VectorDelta, MinSquaredDistance); - OutNearestWedges.Add(WedgeInfo); - } + MinSquaredDistance = FMath::Min(VectorDelta, MinSquaredDistance); + OutNearestWedges.Add(WedgeInfo); } OctreeIter.Advance(); } diff --git a/Engine/Source/Runtime/Engine/Public/Rendering/SkeletalMeshVertexClothBuffer.h b/Engine/Source/Runtime/Engine/Public/Rendering/SkeletalMeshVertexClothBuffer.h index 0b18528dbedd..354fdeb84c1d 100644 --- a/Engine/Source/Runtime/Engine/Public/Rendering/SkeletalMeshVertexClothBuffer.h +++ b/Engine/Source/Runtime/Engine/Public/Rendering/SkeletalMeshVertexClothBuffer.h @@ -61,6 +61,11 @@ public: */ virtual void InitRHI() override; + /** + * Release the RHI resource for this vertex buffer + */ + virtual void ReleaseRHI() override; + /** * @return text description for the resource type */ diff --git a/Engine/Source/Runtime/Engine/Public/SceneManagement.h b/Engine/Source/Runtime/Engine/Public/SceneManagement.h index a0be363a9ed4..0ac276af1194 100644 --- a/Engine/Source/Runtime/Engine/Public/SceneManagement.h +++ b/Engine/Source/Runtime/Engine/Public/SceneManagement.h @@ -2200,6 +2200,17 @@ public: TUniformBuffer UniformBuffer; + ENGINE_API void Set( + const FMatrix& LocalToWorld, + const FMatrix& PreviousLocalToWorld, + const FBoxSphereBounds& WorldBounds, + const FBoxSphereBounds& LocalBounds, + const FBoxSphereBounds& PreSkinnedLocalBounds, + bool bReceivesDecals, + bool bHasPrecomputedVolumetricLightmap, + bool bUseEditorDepthTest); + + /** Pass-through implementation which calls the overloaded Set function with LocalBounds for PreSkinnedLocalBounds. */ ENGINE_API void Set( const FMatrix& LocalToWorld, const FMatrix& PreviousLocalToWorld, diff --git a/Engine/Source/Runtime/Engine/Public/SceneView.h b/Engine/Source/Runtime/Engine/Public/SceneView.h index 496e2149777a..c83cad0d8259 100644 --- a/Engine/Source/Runtime/Engine/Public/SceneView.h +++ b/Engine/Source/Runtime/Engine/Public/SceneView.h @@ -564,6 +564,7 @@ enum ETranslucencyVolumeCascade #define VIEW_UNIFORM_BUFFER_MEMBER_TABLE \ VIEW_UNIFORM_BUFFER_MEMBER(FMatrix, TranslatedWorldToClip) \ VIEW_UNIFORM_BUFFER_MEMBER(FMatrix, WorldToClip) \ + VIEW_UNIFORM_BUFFER_MEMBER(FMatrix, ClipToWorld) \ VIEW_UNIFORM_BUFFER_MEMBER(FMatrix, TranslatedWorldToView) \ VIEW_UNIFORM_BUFFER_MEMBER(FMatrix, ViewToTranslatedWorld) \ VIEW_UNIFORM_BUFFER_MEMBER(FMatrix, TranslatedWorldToCameraView) \ diff --git a/Engine/Source/Runtime/Engine/Public/SkeletalMeshTypes.h b/Engine/Source/Runtime/Engine/Public/SkeletalMeshTypes.h index 40d31183189a..07ad8562b6ad 100644 --- a/Engine/Source/Runtime/Engine/Public/SkeletalMeshTypes.h +++ b/Engine/Source/Runtime/Engine/Public/SkeletalMeshTypes.h @@ -241,6 +241,9 @@ public: virtual bool HasDynamicIndirectShadowCasterRepresentation() const override; virtual void GetShadowShapes(TArray& CapsuleShapes) const override; + /** Return the bounds for the pre-skinned primitive in local space */ + virtual FBoxSphereBounds GetPreSkinnedLocalBounds() const override { return PreSkinnedLocalBounds; } + /** Returns a pre-sorted list of shadow capsules's bone indicies */ const TArray& GetSortedShadowBoneIndices() const { @@ -264,7 +267,7 @@ public: */ virtual void DebugDrawPhysicsAsset(int32 ViewIndex, FMeshElementCollector& Collector, const FEngineShowFlags& EngineShowFlags) const; - /** Render the bones of the skeleton for debug display */ + /** Render the bones of the skeleton for debug display */ void DebugDrawSkeleton(int32 ViewIndex, FMeshElementCollector& Collector, const FEngineShowFlags& EngineShowFlags) const; virtual uint32 GetMemoryFootprint( void ) const override { return( sizeof( *this ) + GetAllocatedSize() ); } @@ -355,6 +358,9 @@ protected: /** Set of materials used by this scene proxy, safe to access from the game thread. */ TSet MaterialsInUse_GameThread; + /** The primitive's pre-skinned local space bounds. */ + FBoxSphereBounds PreSkinnedLocalBounds; + #if WITH_EDITORONLY_DATA /** The component streaming distance multiplier */ float StreamingDistanceMultiplier; diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/GameplayMediaEncoder.Build.cs b/Engine/Source/Runtime/GameplayMediaEncoder/GameplayMediaEncoder.Build.cs index cda10230afd8..86da33abee94 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/GameplayMediaEncoder.Build.cs +++ b/Engine/Source/Runtime/GameplayMediaEncoder/GameplayMediaEncoder.Build.cs @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. using System.IO; using UnrealBuildTool; diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/BaseVideoEncoder.cpp b/Engine/Source/Runtime/GameplayMediaEncoder/Private/BaseVideoEncoder.cpp index cc6df005e792..02bffef9d9da 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/BaseVideoEncoder.cpp +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/BaseVideoEncoder.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "BaseVideoEncoder.h" diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/BaseVideoEncoder.h b/Engine/Source/Runtime/GameplayMediaEncoder/Private/BaseVideoEncoder.h index 8f231b8f59d3..29c41329cd4d 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/BaseVideoEncoder.h +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/BaseVideoEncoder.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoder.cpp b/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoder.cpp index 66a9642ea411..87a38009953d 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoder.cpp +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoder.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "GameplayMediaEncoder.h" #include "Engine/GameEngine.h" diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoderCommon.cpp b/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoderCommon.cpp index 1e86a45be766..51c10a274328 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoderCommon.cpp +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoderCommon.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "GameplayMediaEncoderCommon.h" #include "RHI.h" diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoderCommon.h b/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoderCommon.h index 9dde15938617..336f69587466 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoderCommon.h +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoderCommon.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoderModule.cpp b/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoderModule.cpp index 55560de0c90c..2751d841d81b 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoderModule.cpp +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoderModule.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "CoreMinimal.h" #include "Modules/ModuleInterface.h" diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoderSample.cpp b/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoderSample.cpp index fcb52e209eb1..ed5426a65126 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoderSample.cpp +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/GameplayMediaEncoderSample.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "GameplayMediaEncoderSample.h" #include "GameplayMediaEncoderCommon.h" diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/IbmLiveStreaming.cpp b/Engine/Source/Runtime/GameplayMediaEncoder/Private/IbmLiveStreaming.cpp index 6c1ce106c9db..a711624e92ea 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/IbmLiveStreaming.cpp +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/IbmLiveStreaming.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "IbmLiveStreaming.h" diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/IbmLiveStreaming.h b/Engine/Source/Runtime/GameplayMediaEncoder/Private/IbmLiveStreaming.h index 9aa1d9b4f2d9..e4b975431ff1 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/IbmLiveStreaming.h +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/IbmLiveStreaming.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/AmdAmfPrivate.h b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/AmdAmfPrivate.h index 321abf19587b..402b42ddb923 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/AmdAmfPrivate.h +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/AmdAmfPrivate.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/AmdAmfVideoEncoder.cpp b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/AmdAmfVideoEncoder.cpp index 1be92d43186f..5f0d79d5648a 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/AmdAmfVideoEncoder.cpp +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/AmdAmfVideoEncoder.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "AmdAmfVideoEncoder.h" diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/AmdAmfVideoEncoder.h b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/AmdAmfVideoEncoder.h index 7792b350cb42..dfa8aad37ce5 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/AmdAmfVideoEncoder.h +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/AmdAmfVideoEncoder.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/D3D11VideoProcessor.cpp b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/D3D11VideoProcessor.cpp index 68f7a8dfac0d..179e91d5069a 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/D3D11VideoProcessor.cpp +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/D3D11VideoProcessor.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "D3D11VideoProcessor.h" diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/D3D11VideoProcessor.h b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/D3D11VideoProcessor.h index 800e4ea159fc..ba357077abc7 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/D3D11VideoProcessor.h +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/D3D11VideoProcessor.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/EncoderDevice.cpp b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/EncoderDevice.cpp index 7adfd5f44efd..2a938e3f0088 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/EncoderDevice.cpp +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/EncoderDevice.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "EncoderDevice.h" diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/EncoderDevice.h b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/EncoderDevice.h index 01cd0a0e79fe..30f93ef58308 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/EncoderDevice.h +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/EncoderDevice.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/NvVideoEncoder.cpp b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/NvVideoEncoder.cpp index db50d6593d03..f1675ec181a1 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/NvVideoEncoder.cpp +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/NvVideoEncoder.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "NvVideoEncoder.h" diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/NvVideoEncoder.h b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/NvVideoEncoder.h index 229d3d8179d5..8fcc41263a47 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/NvVideoEncoder.h +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/NvVideoEncoder.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/UtilityShaders.cpp b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/UtilityShaders.cpp index 8006de6ca664..a16f97f68e51 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/UtilityShaders.cpp +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/UtilityShaders.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. /*============================================================================= UtilityShaders.cpp: Utility shaders for Windows Media Foundation pipeline. diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/UtilityShaders.h b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/UtilityShaders.h index c375e48a3575..8d9480ed8e10 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/UtilityShaders.h +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/UtilityShaders.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. /*============================================================================= UtilityShaders.h: Screen rendering definitions. diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/WmfVideoEncoder.cpp b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/WmfVideoEncoder.cpp index 95891a7f2ad4..d27b04a02b53 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/WmfVideoEncoder.cpp +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/WmfVideoEncoder.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "WmfVideoEncoder.h" diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/WmfVideoEncoder.h b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/WmfVideoEncoder.h index d34a017a40e0..2dee263f880e 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/WmfVideoEncoder.h +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/Windows/WmfVideoEncoder.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/WmfAudioEncoder.cpp b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/WmfAudioEncoder.cpp index ddd764dd79bf..7232bd1e72c7 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/WmfAudioEncoder.cpp +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/WmfAudioEncoder.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "WmfAudioEncoder.h" #include "GameplayMediaEncoderSample.h" diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/WmfAudioEncoder.h b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/WmfAudioEncoder.h index 8fcd98fea4cf..5a1940cebacf 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/WmfAudioEncoder.h +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Private/Microsoft/WmfAudioEncoder.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Public/GameplayMediaEncoder.h b/Engine/Source/Runtime/GameplayMediaEncoder/Public/GameplayMediaEncoder.h index 7e81e0c47f25..be1bae90c856 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Public/GameplayMediaEncoder.h +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Public/GameplayMediaEncoder.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/GameplayMediaEncoder/Public/GameplayMediaEncoderSample.h b/Engine/Source/Runtime/GameplayMediaEncoder/Public/GameplayMediaEncoderSample.h index a0a525692688..53fa287732f6 100644 --- a/Engine/Source/Runtime/GameplayMediaEncoder/Public/GameplayMediaEncoderSample.h +++ b/Engine/Source/Runtime/GameplayMediaEncoder/Public/GameplayMediaEncoderSample.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/IOS/IOSRuntimeSettings/Classes/IOSRuntimeSettings.h b/Engine/Source/Runtime/IOS/IOSRuntimeSettings/Classes/IOSRuntimeSettings.h index 48b6509ce19c..49976b312f7e 100644 --- a/Engine/Source/Runtime/IOS/IOSRuntimeSettings/Classes/IOSRuntimeSettings.h +++ b/Engine/Source/Runtime/IOS/IOSRuntimeSettings/Classes/IOSRuntimeSettings.h @@ -347,6 +347,10 @@ public: // If checked, Bluetooth connected controllers will send input UPROPERTY(GlobalConfig, EditAnywhere, Category = Input, meta = (DisplayName = "Allow MFi (Bluetooth) controllers")) bool bAllowControllers; + + // Disables usage of device motion data. If application does not use motion data disabling it will improve battery life + UPROPERTY(GlobalConfig, EditAnywhere, Category = Input, meta = (DisplayName = "Disable Motion Controls")) + bool bDisableMotionData; // Supports default portrait orientation. Landscape will not be supported. UPROPERTY(GlobalConfig, EditAnywhere, Category = DeviceOrientations) diff --git a/Engine/Source/Runtime/IOS/IOSRuntimeSettings/Private/IOSRuntimeSettings.cpp b/Engine/Source/Runtime/IOS/IOSRuntimeSettings/Private/IOSRuntimeSettings.cpp index b835d801d64a..e8398f3aa7ca 100644 --- a/Engine/Source/Runtime/IOS/IOSRuntimeSettings/Private/IOSRuntimeSettings.cpp +++ b/Engine/Source/Runtime/IOS/IOSRuntimeSettings/Private/IOSRuntimeSettings.cpp @@ -32,7 +32,7 @@ UIOSRuntimeSettings::UIOSRuntimeSettings(const FObjectInitializer& ObjectInitial bShipForArmV7 = false; bShipForArm64 = true; bShipForArmV7S = false; - bShipForBitcode = false; + bShipForBitcode = true; bUseRSync = true; AdditionalPlistData = TEXT(""); AdditionalLinkerFlags = TEXT(""); @@ -41,6 +41,7 @@ UIOSRuntimeSettings::UIOSRuntimeSettings(const FObjectInitializer& ObjectInitial bAllowRemoteRotation = true; bUseRemoteAsVirtualJoystick = true; bUseRemoteAbsoluteDpadValues = false; + bDisableMotionData = false; bEnableRemoteNotificationsSupport = false; bEnableBackgroundFetch = false; bSupportsOpenGLES2 = false; diff --git a/Engine/Source/Runtime/JsonUtilities/Private/JsonObjectConverter.cpp b/Engine/Source/Runtime/JsonUtilities/Private/JsonObjectConverter.cpp index 74de35ed0675..92bddfd371fd 100644 --- a/Engine/Source/Runtime/JsonUtilities/Private/JsonObjectConverter.cpp +++ b/Engine/Source/Runtime/JsonUtilities/Private/JsonObjectConverter.cpp @@ -331,6 +331,28 @@ bool FJsonObjectConverter::GetTextFromObject(const TSharedRef& Obj, } } + // try again but only search on the locale region (in the localized data). This is a common omission (i.e. en-US source text should be used if no en is defined) + for (const FString& LocaleToMatch : CultureList) + { + int32 SeparatorPos; + // only consider base language entries in culture chain (i.e. "en") + if (!LocaleToMatch.FindChar('-', SeparatorPos)) + { + for (const auto& Pair : Obj->Values) + { + // only consider coupled entries now (base ones would have been matched on first path) (i.e. "en-US") + if (Pair.Key.FindChar('-', SeparatorPos)) + { + if (Pair.Key.StartsWith(LocaleToMatch)) + { + TextOut = FText::FromString(Pair.Value->AsString()); + return true; + } + } + } + } + } + // no luck, is this possibly an unrelated json object? return false; } diff --git a/Engine/Source/Runtime/Landscape/Classes/Landscape.h b/Engine/Source/Runtime/Landscape/Classes/Landscape.h index 0a185e44e5b9..91262b5553c5 100644 --- a/Engine/Source/Runtime/Landscape/Classes/Landscape.h +++ b/Engine/Source/Runtime/Landscape/Classes/Landscape.h @@ -36,20 +36,40 @@ enum class ERTDrawingType : uint8 enum EHeightmapRTType : uint8 { - LandscapeSizeCombinedAtlas, - LandscapeSizeCombinedNonAtlas, - LandscapeSizeScratch1, - LandscapeSizeScratch2, - LandscapeSizeScratch3, + HeightmapRT_CombinedAtlas, + HeightmapRT_CombinedNonAtlas, + HeightmapRT_Scratch1, + HeightmapRT_Scratch2, + HeightmapRT_Scratch3, // Mips RT - LandscapeSizeMip1, - LandscapeSizeMip2, - LandscapeSizeMip3, - LandscapeSizeMip4, - LandscapeSizeMip5, - LandscapeSizeMip6, - LandscapeSizeMip7, - Count + HeightmapRT_Mip1, + HeightmapRT_Mip2, + HeightmapRT_Mip3, + HeightmapRT_Mip4, + HeightmapRT_Mip5, + HeightmapRT_Mip6, + HeightmapRT_Mip7, + HeightmapRT_Count +}; + +enum EWeightmapRTType : uint8 +{ + WeightmapRT_Scratch_RGBA, + WeightmapRT_Scratch1, + WeightmapRT_Scratch2, + WeightmapRT_Scratch3, + + // Mips RT + WeightmapRT_Mip0, + WeightmapRT_Mip1, + WeightmapRT_Mip2, + WeightmapRT_Mip3, + WeightmapRT_Mip4, + WeightmapRT_Mip5, + WeightmapRT_Mip6, + WeightmapRT_Mip7, + + WeightmapRT_Count }; enum EProceduralContentUpdateFlag : uint32 @@ -58,21 +78,16 @@ enum EProceduralContentUpdateFlag : uint32 Heightmap_Render = 0x00000002u, Heightmap_BoundsAndCollision = 0x00000004u, Heightmap_ResolveToTexture = 0x00000008u, - Heightmap_ResolveToTextureDDC = 0x00000010u, - // TODO: add weightmap update type Weightmap_Setup = 0x00000100u, Weightmap_Render = 0x00000200u, - Weightmap_ResolveToTexture = 0x00000400u, - Weightmap_ResolveToTextureDDC = 0x00000800u, + Weightmap_Collision = 0x00000400u, + Weightmap_ResolveToTexture = 0x00000800u, // Combinations Heightmap_All = Heightmap_Render | Heightmap_BoundsAndCollision | Heightmap_ResolveToTexture, - Heightmap_All_WithDDCUpdate = Heightmap_Render | Heightmap_BoundsAndCollision | Heightmap_ResolveToTextureDDC, - Weightmap_All = Weightmap_Render | Weightmap_ResolveToTexture, - Weightmap_All_WithDDCUpdate = Weightmap_Render | Weightmap_ResolveToTextureDDC, + Weightmap_All = Weightmap_Render | Weightmap_Collision | Weightmap_ResolveToTexture, - All_WithDDCUpdate = Heightmap_All_WithDDCUpdate | Weightmap_All_WithDDCUpdate, All = Heightmap_All | Weightmap_All, All_Setup = Heightmap_Setup | Weightmap_Setup, All_Render = Heightmap_Render | Weightmap_Render, @@ -94,21 +109,29 @@ struct FLandscapeProceduralLayerBrush #if WITH_EDITOR UTextureRenderTarget2D* Render(bool InIsHeightmap, UTextureRenderTarget2D* InCombinedResult) { - TGuardValue AutoRestore(GAllowActorScriptExecutionInEditor, true); - return BPCustomBrush->Render(InIsHeightmap, InCombinedResult); + if (BPCustomBrush != nullptr) + { + TGuardValue AutoRestore(GAllowActorScriptExecutionInEditor, true); + return BPCustomBrush->Render(InIsHeightmap, InCombinedResult); + } + + return nullptr; } bool IsInitialized() const { - return BPCustomBrush->IsInitialized(); + return BPCustomBrush != nullptr ? BPCustomBrush->IsInitialized() : false; } void Initialize(const FIntRect& InBoundRect, const FIntPoint& InLandscapeRenderTargetSize) { - TGuardValue AutoRestore(GAllowActorScriptExecutionInEditor, true); - FIntPoint LandscapeSize = InBoundRect.Max - InBoundRect.Min; - BPCustomBrush->Initialize(LandscapeSize, InLandscapeRenderTargetSize); - BPCustomBrush->SetIsInitialized(true); + if (BPCustomBrush != nullptr) + { + TGuardValue AutoRestore(GAllowActorScriptExecutionInEditor, true); + FIntPoint LandscapeSize = InBoundRect.Max - InBoundRect.Min; + BPCustomBrush->Initialize(LandscapeSize, InLandscapeRenderTargetSize); + BPCustomBrush->SetIsInitialized(true); + } } #endif @@ -122,19 +145,31 @@ struct FProceduralLayer GENERATED_USTRUCT_BODY() FProceduralLayer() - : Name(NAME_None) - , Visible(true) - , Weight(1.0f) + : Guid(FGuid::NewGuid()) + , Name(NAME_None) + , bVisible(true) + , bLocked(false) + , HeightmapAlpha(1.0f) + , WeightmapAlpha(1.0f) {} + UPROPERTY(meta = (IgnoreForMemberInitializationTest)) + FGuid Guid; + UPROPERTY() FName Name; - UPROPERTY() - bool Visible; + UPROPERTY(Transient) + bool bVisible; UPROPERTY() - float Weight; + bool bLocked; + + UPROPERTY() + float HeightmapAlpha; + + UPROPERTY() + float WeightmapAlpha; UPROPERTY() TArray Brushes; @@ -144,6 +179,9 @@ struct FProceduralLayer UPROPERTY() TArray WeightmapBrushOrderIndices; + + UPROPERTY() + TMap WeightmapLayerAllocationBlend; // True -> Substractive, False -> Additive }; UCLASS(MinimalAPI, showcategories=(Display, Movement, Collision, Lighting, LOD, Input), hidecategories=(Mobility)) @@ -185,50 +223,135 @@ public: #endif virtual void PostLoad() override; virtual void BeginDestroy() override; + virtual void FinishDestroy() override; //~ End UObject Interface -#if WITH_EDITOR // Procedural stuff - LANDSCAPE_API void RegenerateProceduralContent(); - LANDSCAPE_API void RegenerateProceduralHeightmaps(); - LANDSCAPE_API void ResolveProceduralHeightmapTexture(bool InUpdateDDC); - LANDSCAPE_API void RegenerateProceduralWeightmaps(); +#if WITH_EDITOR + LANDSCAPE_API void RequestProceduralContentUpdate(uint32 InDataFlags, bool InUpdateAllMaterials = false); + LANDSCAPE_API void CreateProceduralLayer(FName InName = NAME_None, bool bInUpdateProceduralContent = true); + LANDSCAPE_API bool IsProceduralLayerNameUnique(const FName& InName) const; + LANDSCAPE_API void SetProceduralLayerName(int32 InLayerIndex, const FName& InName); + LANDSCAPE_API void SetProceduralLayerAlpha(int32 InLayerIndex, const float InAlpha, bool bInHeightmap); + LANDSCAPE_API void SetProceduralLayerVisibility(int32 InLayerIndex, bool bInVisible); + LANDSCAPE_API struct FProceduralLayer* GetProceduralLayer(int32 InLayerIndex); + LANDSCAPE_API const struct FProceduralLayer* GetProceduralLayer(int32 InLayerIndex) const; + LANDSCAPE_API void ClearProceduralLayer(int32 InLayerIndex); + LANDSCAPE_API void ClearProceduralLayer(const FGuid& InLayerGuid); + LANDSCAPE_API void DeleteProceduralLayer(int32 InLayerIndex); + LANDSCAPE_API void SetCurrentEditingProceduralLayer(FGuid InLayerGuid = FGuid()); + LANDSCAPE_API void ShowOnlySelectedProceduralLayer(int32 InLayerIndex); + LANDSCAPE_API void ShowAllProceduralLayers(); - LANDSCAPE_API void RequestProceduralContentUpdate(uint32 InDataFlags); +private: + void TickProcedural(float DeltaTime, ELevelTick TickType, FActorTickFunction& ThisTickFunction); + void RegenerateProceduralContent(); + void RegenerateProceduralHeightmaps(); + void ResolveProceduralHeightmapTexture(const TArray& InAllLandscapes); + void ResolveProceduralWeightmapTexture(const TArray& InAllLandscapes); + void ResolveProceduralTexture(FLandscapeProceduralTexture2DCPUReadBackResource* InCPUReadBackTexture, UTexture2D* InOriginalTexture); + void RegenerateProceduralWeightmaps(); - void GenerateHeightmapQuad(const FIntPoint& InVertexPosition, const float InVertexSize, const FVector2D& InUVStart, const FVector2D& InUVSize, TArray& OutTriangles) const; - void GenerateHeightmapQuadsAtlas(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InReadSize, const FIntPoint& InWriteSize, TArray& OutTriangles) const; - void GenerateHeightmapQuadsAtlasToNonAtlas(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InHeightmapReadTextureSize, const FIntPoint& InHeightmapWriteTextureSize, TArray& OutTriangles) const; - void GenerateHeightmapQuadsNonAtlas(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InHeightmapReadTextureSize, const FIntPoint& InHeightmapWriteTextureSize, TArray& OutTriangles) const; - void GenerateHeightmapQuadsNonAtlasToAtlas(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InHeightmapReadTextureSize, const FIntPoint& InHeightmapWriteTextureSize, TArray& OutTriangles) const; - void GenerateHeightmapQuadsMip(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InReadSize, const FIntPoint& InWriteSize, int32 CurrentMip, TArray& OutTriangles) const; + bool AreHeightmapTextureResourcesReady(const TArray& InAllLandscapes) const; + bool AreWeightmapTextureResourcesReady(const TArray& InAllLandscapes) const; - void DrawHeightmapComponentsToRenderTarget(const FString& InDebugName, TArray& InComponentsToDraw, UTexture* InHeightmapRTRead, UTextureRenderTarget2D* InOptionalHeightmapRTRead2, UTextureRenderTarget2D* InHeightmapRTWrite, ERTDrawingType InDrawType, - bool InClearRTWrite, struct FLandscapeHeightmapProceduralShaderParameters& InShaderParams, int32 InMipRender = 0) const; + void UpdateProceduralMaterialInstances(const TArray& InComponentsToUpdate, const TMap>& InZeroAllocationsPerComponents); + + void PrepareProceduralComponentDataForExtractLayersCS(const FProceduralLayer& InProceduralLayer, int32 InCurrentWeightmapToProcessIndex, bool InOutputDebugName, const TArray& InAllLandscape, class FLandscapeTexture2DResource* InOutTextureData, + TArray& OutComponentData, TMap& OutLayerInfoObjects); + void PrepareProceduralComponentDataForPackLayersCS(int32 InCurrentWeightmapToProcessIndex, bool InOutputDebugName, const TArray& InAllLandscapeComponents, + TArray& InOutProcessedWeightmaps, TArray& InOutProcessedWeightmapCPUCopy, TArray& OutComponentData); + void ReallocateProceduralWeightmaps(const TArray& InAllLandscape, const TArray& InBrushRequiredAllocations, TArray& OutComponentThatNeedMaterialRebuild); + void InitProceduralWeightmapResources(uint8 InLayerCount); + bool GenerateZeroAllocationPerComponents(const TArray& InAllLandscape, const TMap& InWeightmapLayersBlendSubstractive, TMap>& OutZeroAllocationsPerComponents); + + void GenerateProceduralRenderQuad(const FIntPoint& InVertexPosition, float InVertexSize, const FVector2D& InUVStart, const FVector2D& InUVSize, TArray& OutTriangles) const; + void GenerateProceduralRenderQuadsAtlas(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InReadSize, const FIntPoint& InWriteSize, TArray& OutTriangles) const; + void GenerateProceduralRenderQuadsAtlasToNonAtlas(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InReadSize, const FIntPoint& InWriteSize, TArray& OutTriangles) const; + void GenerateProceduralRenderQuadsNonAtlas(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InReadSize, const FIntPoint& InWriteSize, TArray& OutTriangles) const; + void GenerateProceduralRenderQuadsNonAtlasToAtlas(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InReadSize, const FIntPoint& InWriteSize, TArray& OutTriangles) const; + void GenerateProceduralRenderQuadsMip(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InReadSize, const FIntPoint& InWriteSize, uint8 InCurrentMip, TArray& OutTriangles) const; + + void ClearWeightmapTextureResource(const FString& InDebugName, FTextureRenderTargetResource* InTextureResourceToClear); + void DrawHeightmapComponentsToRenderTarget(const FString& InDebugName, const TArray& InComponentsToDraw, UTexture* InHeightmapRTRead, UTextureRenderTarget2D* InOptionalHeightmapRTRead2, UTextureRenderTarget2D* InHeightmapRTWrite, ERTDrawingType InDrawType, + bool InClearRTWrite, struct FLandscapeHeightmapProceduralShaderParameters& InShaderParams, uint8 InMipRender = 0) const; + + void DrawWeightmapComponentsToRenderTarget(const FString& InDebugName, const TArray& InComponentsToDraw, UTexture* InWeightmapRTRead, UTextureRenderTarget2D* InOptionalWeightmapRTRead2, UTextureRenderTarget2D* InWeightmapRTWrite, + bool InClearRTWrite, struct FLandscapeWeightmapProceduralShaderParameters& InShaderParams, uint8 InMipRender) const; + + void DrawWeightmapComponentsToRenderTarget(const FString& InDebugName, const FIntPoint& InSectionBase, const FVector2D& InScaleBias, UTexture* InWeightmapRTRead, UTextureRenderTarget2D* InOptionalWeightmapRTRead2, UTextureRenderTarget2D* InWeightmapRTWrite, + bool InClearRTWrite, struct FLandscapeWeightmapProceduralShaderParameters& InShaderParams, uint8 InMipRender) const; void DrawHeightmapComponentsToRenderTargetMips(TArray& InComponentsToDraw, UTexture* InReadHeightmap, bool InClearRTWrite, struct FLandscapeHeightmapProceduralShaderParameters& InShaderParams) const; + void DrawWeightmapComponentToRenderTargetMips(const FIntPoint& TopLeftTexturePosition, UTexture* InReadWeightmap, bool InClearRTWrite, struct FLandscapeWeightmapProceduralShaderParameters& InShaderParams) const; - void CopyProceduralTargetToResolveTarget(UTexture* InHeightmapRTRead, UTexture* InCopyResolveTarget, FTextureResource* InCopyResolveTargetCPUResource, const FIntPoint& InFirstComponentSectionBase, int32 InCurrentMip) const; + void CopyProceduralTexture(UTexture* InSourceTexture, UTexture* InDestTexture, FTextureResource* InDestCPUResource = nullptr, const FIntPoint& InFirstComponentSectionBase = FIntPoint(0, 0), uint8 InSourceCurrentMip = 0, uint8 InDestCurrentMip = 0, + uint32 InSourceArrayIndex = 0, uint32 InDestArrayIndex = 0) const; + void CopyProceduralTexture(const FString& InSourceDebugName, FTextureResource* InSourceResource, const FString& InDestDebugName, FTextureResource* InDestResource, FTextureResource* InDestCPUResource = nullptr, const FIntPoint& InFirstComponentSectionBase = FIntPoint(0, 0), + uint8 InSourceCurrentMip = 0, uint8 InDestCurrentMip = 0, uint32 InSourceArrayIndex = 0, uint32 uInDestArrayIndex = 0) const; - void PrintDebugRTHeightmap(FString Context, UTextureRenderTarget2D* InDebugRT, int32 InMipRender = 0, bool InOutputNormals = false) const; - void PrintDebugHeightData(const FString& InContext, const TArray& InHeightmapData, const FIntPoint& InDataSize, int32 InMipRender, bool InOutputNormals = false) const; - - void OnPreSaveWorld(uint32 SaveFlags, UWorld* World); - void OnPostSaveWorld(uint32 SaveFlags, UWorld* World, bool bSuccess); + void PrintProceduralDebugRT(const FString& InContext, UTextureRenderTarget2D* InDebugRT, uint8 InMipRender = 0, bool InOutputHeight = true, bool InOutputNormals = false) const; + void PrintProceduralDebugTextureResource(const FString& InContext, FTextureResource* InTextureResource, uint8 InMipRender = 0, bool InOutputHeight = true, bool InOutputNormals = false) const; + void PrintProceduralDebugHeightData(const FString& InContext, const TArray& InHeightmapData, const FIntPoint& InDataSize, uint8 InMipRender, bool InOutputNormals = false) const; + void PrintProceduralDebugWeightData(const FString& InContext, const TArray& InWeightmapData, const FIntPoint& InDataSize, uint8 InMipRender) const; #endif +public: + #if WITH_EDITORONLY_DATA UPROPERTY(TextExportTransient) TArray ProceduralLayers; + UPROPERTY(Transient) + TArray HeightmapRTList; + + UPROPERTY(Transient) + TArray WeightmapRTList; + UPROPERTY(Transient) bool PreviousExperimentalLandscapeProcedural; +private: + + UPROPERTY(Transient) + bool WasCompilingShaders; + UPROPERTY(Transient) uint32 ProceduralContentUpdateFlags; UPROPERTY(Transient) - TArray HeightmapRTList; + bool ProceduralUpdateAllMaterials; + + // Represent all the resolved paint layer, from all procedural layer blended together (size of the landscape x paint layer count) + class FLandscapeTexture2DArrayResource* CombinedProcLayerWeightmapAllLayersResource; + + // Represent all the resolved paint layer, from the current procedual layer only (size of the landscape x paint layer count) + class FLandscapeTexture2DArrayResource* CurrentProcLayerWeightmapAllLayersResource; + + // Used in extracting the paint layers data from procedural layer weightmaps (size of the landscape) + class FLandscapeTexture2DResource* WeightmapScratchExtractLayerTextureResource; + + // Used in packing the paint layers data contained into CombinedProcLayerWeightmapAllLayersResource to be set again for each component weightmap (size of the landscape) + class FLandscapeTexture2DResource* WeightmapScratchPackLayerTextureResource; +#endif + +protected: +#if WITH_EDITOR + FName GenerateUniqueProceduralLayerName(FName InName = NAME_None) const; #endif }; + +#if WITH_EDITOR +class LANDSCAPE_API FScopedSetLandscapeCurrentEditingProceduralLayer +{ +public: + FScopedSetLandscapeCurrentEditingProceduralLayer(ALandscape* InLandscape, const FGuid& InProceduralLayer, TFunction InCompletionCallback = TFunction()); + ~FScopedSetLandscapeCurrentEditingProceduralLayer(); + +private: + TWeakObjectPtr Landscape; + const FGuid& ProceduralLayer; + TFunction CompletionCallback; +}; +#endif diff --git a/Engine/Source/Runtime/Landscape/Classes/LandscapeBPCustomBrush.h b/Engine/Source/Runtime/Landscape/Classes/LandscapeBPCustomBrush.h index b4597b219442..df8b20a99808 100644 --- a/Engine/Source/Runtime/Landscape/Classes/LandscapeBPCustomBrush.h +++ b/Engine/Source/Runtime/Landscape/Classes/LandscapeBPCustomBrush.h @@ -20,6 +20,9 @@ private: UPROPERTY(Category= "Settings", EditAnywhere, NonTransactional) bool AffectWeightmap; + UPROPERTY(Category = "Settings", EditAnywhere, NonTransactional) + TArray AffectedWeightmapLayers; + #if WITH_EDITORONLY_DATA UPROPERTY(NonTransactional, DuplicateTransient) class ALandscape* OwningLandscape; @@ -43,6 +46,7 @@ public: bool IsAffectingHeightmap() const { return AffectHeightmap; } bool IsAffectingWeightmap() const { return AffectWeightmap; } + bool IsAffectingWeightmapLayer(const FName& InLayerName) const; UFUNCTION(BlueprintImplementableEvent) UTextureRenderTarget2D* Render(bool InIsHeightmap, UTextureRenderTarget2D* InCombinedResult); diff --git a/Engine/Source/Runtime/Landscape/Classes/LandscapeComponent.h b/Engine/Source/Runtime/Landscape/Classes/LandscapeComponent.h index b1ce9a36ccd3..9afd9a40cf21 100644 --- a/Engine/Source/Runtime/Landscape/Classes/LandscapeComponent.h +++ b/Engine/Source/Runtime/Landscape/Classes/LandscapeComponent.h @@ -8,6 +8,7 @@ #include "Engine/TextureStreamingTypes.h" #include "Components/PrimitiveComponent.h" #include "PerPlatformProperties.h" +#include "LandscapeWeightmapUsage.h" #include "LandscapeComponent.generated.h" @@ -304,14 +305,6 @@ class ULandscapeComponent : public UPrimitiveComponent UPROPERTY(TextExportTransient) TArray MaterialIndexToDisabledTessellationMaterial; - /** List of layers, and the weightmap and channel they are stored */ - UPROPERTY() - TArray WeightmapLayerAllocations; - - /** Weightmap texture reference */ - UPROPERTY(TextExportTransient) - TArray WeightmapTextures; - /** XYOffsetmap texture reference */ UPROPERTY(TextExportTransient) UTexture2D* XYOffsetmapTexture; @@ -342,15 +335,28 @@ private: UPROPERTY() FGuid LightingGuid; - /** Heightmap texture reference */ - UPROPERTY(Transient, TextExportTransient) + /** Current data we're working on (only used in Procedural mode) This data will get set by tools as needed */ UTexture2D* CurrentEditingHeightmapTexture; + TArray* CurrentEditingWeightmapLayerAllocations; + TArray* CurrentEditingWeightmapTextures; + TArray* CurrentEditingWeightmapTexturesUsage; + FGuid CurrentProceduralLayerGuid; + + TArray WeightmapTexturesUsage; #endif // WITH_EDITORONLY_DATA /** Heightmap texture reference */ UPROPERTY(TextExportTransient) UTexture2D* HeightmapTexture; + /** List of layers, and the weightmap and channel they are stored */ + UPROPERTY() + TArray WeightmapLayerAllocations; + + /** Weightmap texture reference */ + UPROPERTY(TextExportTransient) + TArray WeightmapTextures; + public: /** Uniquely identifies this component's built map data. */ @@ -501,8 +507,25 @@ public: virtual bool IsPrecomputedLightingValid() const override; LANDSCAPE_API UTexture2D* GetHeightmap(bool InReturnCurrentEditingHeightmap = false) const; + LANDSCAPE_API TArray& GetWeightmapTextures(bool InReturnCurrentEditingWeightmap = false); + LANDSCAPE_API const TArray& GetWeightmapTextures(bool InReturnCurrentEditingWeightmap = false) const; + + LANDSCAPE_API TArray& GetWeightmapLayerAllocations(bool InReturnCurrentEditingWeightmap = false); + LANDSCAPE_API const TArray& GetWeightmapLayerAllocations(bool InReturnCurrentEditingWeightmap = false) const; + +#if WITH_EDITOR LANDSCAPE_API void SetHeightmap(UTexture2D* NewHeightmap); - LANDSCAPE_API void SetCurrentEditingHeightmap(UTexture2D* InNewHeightmap); + + LANDSCAPE_API void SetWeightmapTextures(const TArray& InNewWeightmapTextures, bool InApplyToCurrentEditingWeightmap = false); + LANDSCAPE_API const FGuid& GetCurrentProceduralLayerGuid() const; + + LANDSCAPE_API void SetWeightmapLayerAllocations(const TArray& InNewWeightmapLayerAllocations); + LANDSCAPE_API void SetWeightmapTexturesUsage(const TArray& InNewWeightmapTexturesUsage, bool InApplyToCurrentEditingWeightmap = false); + LANDSCAPE_API TArray& GetWeightmapTexturesUsage(bool InReturnCurrentEditingWeightmap = false); + LANDSCAPE_API const TArray& GetWeightmapTexturesUsage(bool InReturnCurrentEditingWeightmap = false) const; + + LANDSCAPE_API void SetCurrentEditingProceduralLayer(struct FProceduralLayer* Layer, struct FProceduralLayerData* LayerData); +#endif #if WITH_EDITOR virtual int32 GetNumMaterials() const override; @@ -721,7 +744,7 @@ public: /** * Create weightmaps for this component for the layers specified in the WeightmapLayerAllocations array */ - void ReallocateWeightmaps(FLandscapeEditDataInterface* DataInterface=NULL); + void ReallocateWeightmaps(FLandscapeEditDataInterface* DataInterface= nullptr, bool InCanUseCurrentEditingWeightmap = true, bool InSaveToTransactionBuffer = true, bool InInitPlatformDataAsync = false, TArray* OutNewCreatedTextures = nullptr); /** Returns the actor's LandscapeMaterial, or the Component's OverrideLandscapeMaterial if set */ LANDSCAPE_API UMaterialInterface* GetLandscapeMaterial(int8 InLODIndex = INDEX_NONE) const; @@ -775,6 +798,14 @@ public: protected: +#if WITH_EDITOR + void SetCurrentEditingHeightmap(UTexture2D* InNewHeightmap); + void SetCurrentEditingWeightmaps(TArray* InNewWeightmapTextures); + void SetCurrentProceduralLayerGuid(const FGuid& InLayerGuid); + void SetCurrentEditingWeightmapLayerAllocations(TArray* InNewWeightmapLayerAllocations); + void SetCurrentEditingWeightmapTexturesUsage(TArray* InNewWeightmapTexturesUsage); +#endif + /** Whether the component type supports static lighting. */ virtual bool SupportsStaticLighting() const override { diff --git a/Engine/Source/Runtime/Landscape/Classes/LandscapeProxy.h b/Engine/Source/Runtime/Landscape/Classes/LandscapeProxy.h index b41d88cc6967..bc4827dd6271 100644 --- a/Engine/Source/Runtime/Landscape/Classes/LandscapeProxy.h +++ b/Engine/Source/Runtime/Landscape/Classes/LandscapeProxy.h @@ -13,6 +13,9 @@ #include "Engine/Texture.h" #include "PerPlatformProperties.h" #include "LandscapeBPCustomBrush.h" +#include "LandscapeComponent.h" +#include "LandscapeWeightmapUsage.h" + #include "LandscapeProxy.generated.h" class ALandscape; @@ -35,32 +38,6 @@ struct FLandscapeInfoLayerSettings; struct FMeshDescription; enum class ENavDataGatheringMode : uint8; -/** Structure storing channel usage for weightmap textures */ -USTRUCT() -struct FLandscapeWeightmapUsage -{ - GENERATED_USTRUCT_BODY() - - UPROPERTY() - ULandscapeComponent* ChannelUsage[4]; - - FLandscapeWeightmapUsage() - { - ChannelUsage[0] = nullptr; - ChannelUsage[1] = nullptr; - ChannelUsage[2] = nullptr; - ChannelUsage[3] = nullptr; - } - friend FArchive& operator<<( FArchive& Ar, FLandscapeWeightmapUsage& U ); - int32 FreeChannelCount() const - { - return ((ChannelUsage[0] == nullptr) ? 1 : 0) + - ((ChannelUsage[1] == nullptr) ? 1 : 0) + - ((ChannelUsage[2] == nullptr) ? 1 : 0) + - ((ChannelUsage[3] == nullptr) ? 1 : 0); - } -}; - USTRUCT() struct FLandscapeEditorLayerSettings { @@ -399,6 +376,20 @@ struct FRenderDataPerHeightmap FIntPoint TopLeftSectionBase; }; +USTRUCT() +struct FWeightmapLayerData +{ + GENERATED_USTRUCT_BODY() + + UPROPERTY() + TArray Weightmaps; + + UPROPERTY() + TArray WeightmapLayerAllocations; + + TArray WeightmapTextureUsages; // Easy Access ref to data stored into the LandscapeProxy weightmap usage map +}; + USTRUCT() struct FProceduralLayerData { @@ -408,9 +399,10 @@ struct FProceduralLayerData {} UPROPERTY() - TMap Heightmaps; + TMap Heightmaps; // Mapping between Original Heightmap -> Layer Heightmap - // TODO: add weightmap data + UPROPERTY() + TMap WeightmapData; // Weightmaps per components }; UCLASS(Abstract, MinimalAPI, NotBlueprintable, hidecategories=(Display, Attachment, Physics, Debug, Lighting, LOD), showcategories=(Lighting, Rendering, "Utilities|Transformation"), hidecategories=(Mobility)) @@ -651,7 +643,7 @@ public: TArray EditorLayerSettings; UPROPERTY(TextExportTransient) - TMap ProceduralLayersData; + TMap ProceduralLayersData; UPROPERTY() bool HasProceduralContent; @@ -659,6 +651,8 @@ public: UPROPERTY(Transient) TMap RenderDataPerHeightmap; // Mapping between Original heightmap and general render data + TMap WeightmapCPUReadBackTextures; // Mapping between Original weightmap and tyhe CPU readback resource + FRenderCommandFence ReleaseResourceFence; #endif @@ -704,7 +698,8 @@ public: #endif /** Map of weightmap usage */ - TMap WeightmapUsageMap; + UPROPERTY(Transient) + TMap WeightmapUsageMap; // Blueprint functions diff --git a/Engine/Source/Runtime/Landscape/Classes/LandscapeWeightmapUsage.h b/Engine/Source/Runtime/Landscape/Classes/LandscapeWeightmapUsage.h new file mode 100644 index 000000000000..891cacc4bb3a --- /dev/null +++ b/Engine/Source/Runtime/Landscape/Classes/LandscapeWeightmapUsage.h @@ -0,0 +1,43 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" + +#include "LandscapeWeightmapUsage.generated.h" + +class ULandscapeComponent; + +UCLASS(MinimalAPI, NotBlueprintable) +class ULandscapeWeightmapUsage : public UObject +{ + GENERATED_UCLASS_BODY() + +public: + UPROPERTY() + ULandscapeComponent* ChannelUsage[4]; + + UPROPERTY() + FGuid ProceduralLayerGuid; + + int32 FreeChannelCount() const + { + int32 Count = 0; + + for (int8 i = 0; i < 4; ++i) + { + Count += (ChannelUsage[i] == nullptr) ? 1 : 0; + } + + return Count; + } + + void ClearUsage() + { + for (int8 i = 0; i < 4; ++i) + { + ChannelUsage[i] = nullptr; + } + } +}; diff --git a/Engine/Source/Runtime/Landscape/Private/Landscape.cpp b/Engine/Source/Runtime/Landscape/Private/Landscape.cpp index fd60e787d9d9..e837a4f4d225 100644 --- a/Engine/Source/Runtime/Landscape/Private/Landscape.cpp +++ b/Engine/Source/Runtime/Landscape/Private/Landscape.cpp @@ -49,6 +49,7 @@ Landscape.cpp: Terrain rendering #include "Engine/Engine.h" #include "EngineUtils.h" #include "ComponentRecreateRenderStateContext.h" +#include "LandscapeWeightmapUsage.h" #if WITH_EDITOR #include "MaterialUtilities.h" @@ -75,10 +76,17 @@ DEFINE_STAT(STAT_LandscapeComponentUsingSubSectionDrawCalls); DEFINE_STAT(STAT_LandscapeDrawCalls); DEFINE_STAT(STAT_LandscapeTriangles); +DEFINE_STAT(STAT_LandscapeRegenerateProcedural_RenderThread); +DEFINE_STAT(STAT_LandscapeRegenerateProceduralDrawCalls); + DEFINE_STAT(STAT_LandscapeRegenerateProceduralHeightmaps); -DEFINE_STAT(STAT_LandscapeRegenerateProceduralHeightmaps_RenderThread); DEFINE_STAT(STAT_LandscapeResolveProceduralHeightmap); -DEFINE_STAT(STAT_LandscapeRegenerateProceduralHeightmapsDrawCalls); + +DEFINE_STAT(STAT_LandscapeProceduralUpdateMaterialInstance); +DEFINE_STAT(STAT_LandscapeReallocateProceduralWeightmaps); + +DEFINE_STAT(STAT_LandscapeResolveProceduralWeightmap); +DEFINE_STAT(STAT_LandscapeRegenerateProceduralWeightmaps); DEFINE_STAT(STAT_LandscapeVertexMem); DEFINE_STAT(STAT_LandscapeOccluderMem); @@ -161,6 +169,7 @@ ULandscapeComponent::ULandscapeComponent(const FObjectInitializer& ObjectInitial #if WITH_EDITORONLY_DATA EditToolRenderData = FLandscapeEditToolRenderData(); + CurrentProceduralLayerGuid.Invalidate(); #endif LpvBiasMultiplier = 0.0f; // Bias is 0 for landscape, since it's single sided @@ -213,6 +222,41 @@ UMaterialInstanceDynamic* ULandscapeComponent::GetMaterialInstanceDynamic(int32 void ULandscapeComponent::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector) { ULandscapeComponent* This = CastChecked(InThis); + +#if WITH_EDITORONLY_DATA + Collector.AddReferencedObject(This->CurrentEditingHeightmapTexture); + + if (This->CurrentEditingWeightmapLayerAllocations != nullptr) + { + for (FWeightmapLayerAllocationInfo& WeightmapLayerAllocation : *This->CurrentEditingWeightmapLayerAllocations) + { + Collector.AddReferencedObject(WeightmapLayerAllocation.LayerInfo); + } + } + + if (This->CurrentEditingWeightmapTexturesUsage != nullptr) + { + for (ULandscapeWeightmapUsage* TextureUsage : *This->CurrentEditingWeightmapTexturesUsage) + { + if (TextureUsage != nullptr) + { + for (int32 i = 0; i < 4; ++i) + { + Collector.AddReferencedObject(TextureUsage->ChannelUsage[i]); + } + } + } + } + + if (This->CurrentEditingWeightmapTextures != nullptr) + { + for (UTexture2D* Weightmap : *This->CurrentEditingWeightmapTextures) + { + Collector.AddReferencedObject(Weightmap); + } + } +#endif + Super::AddReferencedObjects(This, Collector); } @@ -537,21 +581,23 @@ void ULandscapeComponent::GetLayerDebugColorKey(int32& R, int32& G, int32& B) co if (LayerStruct.DebugColorChannel > 0 && LayerStruct.LayerInfoObj) { - for (int32 LayerIdx = 0; LayerIdx < WeightmapLayerAllocations.Num(); LayerIdx++) + const TArray& ComponentWeightmapLayerAllocations = GetWeightmapLayerAllocations(true); + + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { - if (WeightmapLayerAllocations[LayerIdx].LayerInfo == LayerStruct.LayerInfoObj) + if (ComponentWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerStruct.LayerInfoObj) { if (LayerStruct.DebugColorChannel & 1) // R { - R = (WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex * 4 + WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel); + R = (ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex * 4 + ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel); } if (LayerStruct.DebugColorChannel & 2) // G { - G = (WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex * 4 + WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel); + G = (ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex * 4 + ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel); } if (LayerStruct.DebugColorChannel & 4) // B { - B = (WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex * 4 + WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel); + B = (ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex * 4 + ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel); } break; } @@ -942,7 +988,12 @@ ALandscape::ALandscape(const FObjectInitializer& ObjectInitializer) #if WITH_EDITORONLY_DATA bLockLocation = false; PreviousExperimentalLandscapeProcedural = false; + WasCompilingShaders = false; ProceduralContentUpdateFlags = 0; + CombinedProcLayerWeightmapAllLayersResource = nullptr; + CurrentProcLayerWeightmapAllLayersResource = nullptr; + WeightmapScratchExtractLayerTextureResource = nullptr; + WeightmapScratchPackLayerTextureResource = nullptr; #endif // WITH_EDITORONLY_DATA } @@ -1037,9 +1088,9 @@ void ULandscapeComponent::GetGeneratedTexturesAndMaterialInstances(TArrayWeightmapUsageMap.Find(WeightmapTexture); - if (Usage != nullptr) + ULandscapeWeightmapUsage** Usage = Proxy->WeightmapUsageMap.Find(WeightmapTexture); + if (Usage != nullptr && (*Usage) != nullptr) { - Usage->ChannelUsage[WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel] = nullptr; + (*Usage)->ChannelUsage[WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel] = nullptr; - if (Usage->FreeChannelCount() == 4) + if ((*Usage)->FreeChannelCount() == 4) { Proxy->WeightmapUsageMap.Remove(WeightmapTexture); } } } } + + WeightmapTexturesUsage.Reset(); } #endif } @@ -1301,14 +1354,88 @@ UTexture2D* ULandscapeComponent::GetHeightmap(bool InReturnCurrentEditingHeightm return HeightmapTexture; } +const TArray& ULandscapeComponent::GetWeightmapTextures(bool InReturnCurrentEditingWeightmap) const +{ +#if WITH_EDITORONLY_DATA + if (InReturnCurrentEditingWeightmap && GetMutableDefault()->bProceduralLandscape) + { + if (CurrentEditingWeightmapTextures != nullptr) + { + return *CurrentEditingWeightmapTextures; + } + } +#endif + + return WeightmapTextures; +} + +TArray& ULandscapeComponent::GetWeightmapTextures(bool InReturnCurrentEditingWeightmap) +{ +#if WITH_EDITORONLY_DATA + if (InReturnCurrentEditingWeightmap && GetMutableDefault()->bProceduralLandscape) + { + if (CurrentEditingWeightmapTextures != nullptr) + { + return *CurrentEditingWeightmapTextures; + } + } +#endif + + return WeightmapTextures; +} + +const TArray& ULandscapeComponent::GetWeightmapLayerAllocations(bool InReturnCurrentEditingWeightmap) const +{ +#if WITH_EDITORONLY_DATA + if (InReturnCurrentEditingWeightmap && GetMutableDefault()->bProceduralLandscape) + { + if (CurrentEditingWeightmapLayerAllocations != nullptr) + { + return *CurrentEditingWeightmapLayerAllocations; + } + } +#endif + + return WeightmapLayerAllocations; +} + +TArray& ULandscapeComponent::GetWeightmapLayerAllocations(bool InReturnCurrentEditingWeightmap) +{ +#if WITH_EDITORONLY_DATA + if (InReturnCurrentEditingWeightmap && GetMutableDefault()->bProceduralLandscape) + { + if (CurrentEditingWeightmapLayerAllocations != nullptr) + { + return *CurrentEditingWeightmapLayerAllocations; + } + } +#endif + + return WeightmapLayerAllocations; +} + #if WITH_EDITOR + +void ULandscapeComponent::SetCurrentEditingProceduralLayer(FProceduralLayer* Layer, FProceduralLayerData* LayerData) +{ + // Update Current Heightmap + UTexture2D** LayerHeightmap = LayerData ? LayerData->Heightmaps.Find(GetHeightmap()) : nullptr; + SetCurrentEditingHeightmap(LayerHeightmap ? *LayerHeightmap : nullptr); + + // Update Current Weightmaps + FWeightmapLayerData* WeightmapData = LayerData ? LayerData->WeightmapData.Find(this) : nullptr; + SetCurrentEditingWeightmapLayerAllocations(WeightmapData ? &WeightmapData->WeightmapLayerAllocations : nullptr); + SetCurrentEditingWeightmaps(WeightmapData ? &WeightmapData->Weightmaps : nullptr); + SetCurrentProceduralLayerGuid(WeightmapData && Layer ? Layer->Guid : FGuid()); + SetCurrentEditingWeightmapTexturesUsage(WeightmapData ? &WeightmapData->WeightmapTextureUsages : nullptr); +} + void ULandscapeComponent::SetCurrentEditingHeightmap(UTexture2D* InNewHeightmap) { #if WITH_EDITORONLY_DATA CurrentEditingHeightmapTexture = InNewHeightmap; #endif } -#endif void ULandscapeComponent::SetHeightmap(UTexture2D* NewHeightmap) { @@ -1316,6 +1443,104 @@ void ULandscapeComponent::SetHeightmap(UTexture2D* NewHeightmap) HeightmapTexture = NewHeightmap; } +void ULandscapeComponent::SetWeightmapTextures(const TArray& InNewWeightmapTextures, bool InApplyToCurrentEditingWeightmap) +{ +#if WITH_EDITORONLY_DATA + if (InApplyToCurrentEditingWeightmap && CurrentEditingWeightmapTextures != nullptr) + { + CurrentEditingWeightmapTextures->Reset(InNewWeightmapTextures.Num()); + CurrentEditingWeightmapTextures->Append(InNewWeightmapTextures); + } + else +#endif + { + WeightmapTextures = InNewWeightmapTextures; + } +} + +void ULandscapeComponent::SetCurrentEditingWeightmaps(TArray* InNewWeightmapTextures) +{ +#if WITH_EDITORONLY_DATA + CurrentEditingWeightmapTextures = InNewWeightmapTextures; +#endif +} + +void ULandscapeComponent::SetWeightmapLayerAllocations(const TArray& InNewWeightmapLayerAllocations) +{ + WeightmapLayerAllocations = InNewWeightmapLayerAllocations; +} + + +void ULandscapeComponent::SetCurrentEditingWeightmapLayerAllocations(TArray* InNewWeightmapLayerAllocations) +{ +#if WITH_EDITORONLY_DATA + CurrentEditingWeightmapLayerAllocations = InNewWeightmapLayerAllocations; +#endif +} + +TArray& ULandscapeComponent::GetWeightmapTexturesUsage(bool InReturnCurrentEditingWeightmap) +{ +#if WITH_EDITORONLY_DATA + if (InReturnCurrentEditingWeightmap && GetMutableDefault()->bProceduralLandscape) + { + if (CurrentEditingWeightmapTexturesUsage != nullptr) + { + return *CurrentEditingWeightmapTexturesUsage; + } + } +#endif + + return WeightmapTexturesUsage; +} + +const TArray& ULandscapeComponent::GetWeightmapTexturesUsage(bool InReturnCurrentEditingWeightmap) const +{ +#if WITH_EDITORONLY_DATA + if (InReturnCurrentEditingWeightmap && GetMutableDefault()->bProceduralLandscape) + { + if (CurrentEditingWeightmapTexturesUsage != nullptr) + { + return *CurrentEditingWeightmapTexturesUsage; + } + } +#endif + + return WeightmapTexturesUsage; +} + +void ULandscapeComponent::SetCurrentEditingWeightmapTexturesUsage(TArray* InNewWeightmapTexturesUsage) +{ +#if WITH_EDITORONLY_DATA + CurrentEditingWeightmapTexturesUsage = InNewWeightmapTexturesUsage; +#endif +} + +void ULandscapeComponent::SetWeightmapTexturesUsage(const TArray& InNewWeightmapTexturesUsage, bool InApplyToCurrentEditingWeightmap ) +{ +#if WITH_EDITORONLY_DATA + if (InApplyToCurrentEditingWeightmap && CurrentEditingWeightmapTexturesUsage != nullptr) + { + CurrentEditingWeightmapTexturesUsage->Reset(InNewWeightmapTexturesUsage.Num()); + CurrentEditingWeightmapTexturesUsage->Append(InNewWeightmapTexturesUsage); + } + else +#endif + { + WeightmapTexturesUsage = InNewWeightmapTexturesUsage; + } +} + +void ULandscapeComponent::SetCurrentProceduralLayerGuid(const FGuid& InLayerGuid) +{ + CurrentProceduralLayerGuid = InLayerGuid; +} + +const FGuid& ULandscapeComponent::GetCurrentProceduralLayerGuid() const +{ + return CurrentProceduralLayerGuid; +} +#endif + void ALandscapeProxy::PostRegisterAllComponents() { Super::PostRegisterAllComponents(); @@ -1357,10 +1582,10 @@ void ALandscapeProxy::UnregisterAllComponents(const bool bForReregister) Super::UnregisterAllComponents(bForReregister); } -// FLandscapeWeightmapUsage serializer -FArchive& operator<<(FArchive& Ar, FLandscapeWeightmapUsage& U) + +FArchive& operator<<(FArchive& Ar, FWeightmapLayerAllocationInfo& U) { - return Ar << U.ChannelUsage[0] << U.ChannelUsage[1] << U.ChannelUsage[2] << U.ChannelUsage[3]; + return Ar << U.LayerInfo << U.WeightmapTextureChannel << U.WeightmapTextureIndex; } #if WITH_EDITORONLY_DATA @@ -1435,31 +1660,12 @@ void ALandscape::PostLoad() break; } } - - if (GetMutableDefault()->bProceduralLandscape) - { - FEditorDelegates::PreSaveWorld.AddUObject(this, &ALandscape::OnPreSaveWorld); - FEditorDelegates::PostSaveWorld.AddUObject(this, &ALandscape::OnPostSaveWorld); - } #endif } Super::PostLoad(); } -void ALandscape::BeginDestroy() -{ -#if WITH_EDITOR - if (GetMutableDefault()->bProceduralLandscape) - { - FEditorDelegates::PreSaveWorld.RemoveAll(this); - FEditorDelegates::PostSaveWorld.RemoveAll(this); - } -#endif - - Super::BeginDestroy(); -} - #if WITH_EDITOR void ALandscapeProxy::OnFeatureLevelChanged(ERHIFeatureLevel::Type NewFeatureLevel) { @@ -1525,13 +1731,6 @@ void ALandscapeProxy::Serialize(FArchive& Ar) } } } - -#if WITH_EDITOR - if (Ar.IsTransacting()) - { - Ar << WeightmapUsageMap; - } -#endif } void ALandscapeProxy::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector) @@ -1543,15 +1742,6 @@ void ALandscapeProxy::AddReferencedObjects(UObject* InThis, FReferenceCollector& #if WITH_EDITORONLY_DATA Collector.AddReferencedObjects(This->MaterialInstanceConstantMap, This); #endif - - for (auto It = This->WeightmapUsageMap.CreateIterator(); It; ++It) - { - Collector.AddReferencedObject(It.Key(), This); - Collector.AddReferencedObject(It.Value().ChannelUsage[0], This); - Collector.AddReferencedObject(It.Value().ChannelUsage[1], This); - Collector.AddReferencedObject(It.Value().ChannelUsage[2], This); - Collector.AddReferencedObject(It.Value().ChannelUsage[3], This); - } } #if WITH_EDITOR @@ -1706,9 +1896,11 @@ bool ULandscapeInfo::UpdateLayerInfoMap(ALandscapeProxy* Proxy /*= nullptr*/, bo } } - for (int32 AllocationIndex = 0; AllocationIndex < Component->WeightmapLayerAllocations.Num(); AllocationIndex++) + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(); + + for (int32 AllocationIndex = 0; AllocationIndex < ComponentWeightmapLayerAllocations.Num(); AllocationIndex++) { - ULandscapeLayerInfoObject* LayerInfo = Component->WeightmapLayerAllocations[AllocationIndex].LayerInfo; + ULandscapeLayerInfoObject* LayerInfo = ComponentWeightmapLayerAllocations[AllocationIndex].LayerInfo; if (LayerInfo) { int32 LayerInfoIndex = GetLayerInfoIndex(LayerInfo); @@ -2646,6 +2838,12 @@ void ULandscapeComponent::PostDuplicate(bool bDuplicateForPIE) } } +ULandscapeWeightmapUsage::ULandscapeWeightmapUsage(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + ClearUsage(); +} + // Generate a new guid to force a recache of all landscape derived data #define LANDSCAPE_FULL_DERIVEDDATA_VER TEXT("016D326F3A954BBA9CCDFA00CEFA31E9") @@ -2757,47 +2955,9 @@ void ALandscape::TickActor(float DeltaTime, ELevelTick TickType, FActorTickFunct Super::TickActor(DeltaTime, TickType, ThisTickFunction); #if WITH_EDITOR - UWorld* World = GetWorld(); - if (GIsEditor && World && !World->IsPlayInEditor()) + if (GIsEditor) { - if (GetMutableDefault()->bProceduralLandscape) - { - if (PreviousExperimentalLandscapeProcedural != GetMutableDefault()->bProceduralLandscape) - { - PreviousExperimentalLandscapeProcedural = GetMutableDefault()->bProceduralLandscape; - - RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All_Setup); - } - - RegenerateProceduralContent(); - } - else - { - if (PreviousExperimentalLandscapeProcedural != GetMutableDefault()->bProceduralLandscape) - { - PreviousExperimentalLandscapeProcedural = GetMutableDefault()->bProceduralLandscape; - - for (auto& ItPair : RenderDataPerHeightmap) - { - FRenderDataPerHeightmap& HeightmapRenderData = ItPair.Value; - - if (HeightmapRenderData.HeightmapsCPUReadBack != nullptr) - { - BeginReleaseResource(HeightmapRenderData.HeightmapsCPUReadBack); - } - } - - FlushRenderingCommands(); - - for (auto& ItPair : RenderDataPerHeightmap) - { - FRenderDataPerHeightmap& HeightmapRenderData = ItPair.Value; - - delete HeightmapRenderData.HeightmapsCPUReadBack; - HeightmapRenderData.HeightmapsCPUReadBack = nullptr; - } - } - } + TickProcedural(DeltaTime, TickType, ThisTickFunction); } #endif } diff --git a/Engine/Source/Runtime/Landscape/Private/LandscapeBPCustomBrush.cpp b/Engine/Source/Runtime/Landscape/Private/LandscapeBPCustomBrush.cpp index e0fb4fa8a930..3a46633cb28e 100644 --- a/Engine/Source/Runtime/Landscape/Private/LandscapeBPCustomBrush.cpp +++ b/Engine/Source/Runtime/Landscape/Private/LandscapeBPCustomBrush.cpp @@ -67,6 +67,11 @@ void ALandscapeBlueprintCustomBrush::SetIsInitialized(bool InIsInitialized) bIsInitialized = InIsInitialized; } +bool ALandscapeBlueprintCustomBrush::IsAffectingWeightmapLayer(const FName& InLayerName) const +{ + return AffectedWeightmapLayers.Contains(InLayerName); +} + void ALandscapeBlueprintCustomBrush::PostEditMove(bool bFinished) { Super::PostEditMove(bFinished); @@ -152,7 +157,7 @@ void ALandscapeBlueprintCustomBrush::PostEditChangeProperty(FPropertyChangedEven if (OwningLandscape != nullptr) { - OwningLandscape->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All); + OwningLandscape->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All, true); // For now since we dont have brush bounds, we have to force an update of all components, otherwise we will have not right layer being rendered as materials are not updated } } #endif diff --git a/Engine/Source/Runtime/Landscape/Private/LandscapeDataAccess.cpp b/Engine/Source/Runtime/Landscape/Private/LandscapeDataAccess.cpp index ae7f91bd313c..0d07b1bf7244 100644 --- a/Engine/Source/Runtime/Landscape/Private/LandscapeDataAccess.cpp +++ b/Engine/Source/Runtime/Landscape/Private/LandscapeDataAccess.cpp @@ -74,9 +74,12 @@ LANDSCAPE_API void FLandscapeComponentDataInterface::GetHeightmapTextureData(TAr LANDSCAPE_API bool FLandscapeComponentDataInterface::GetWeightmapTextureData(ULandscapeLayerInfoObject* LayerInfo, TArray& OutData) { int32 LayerIdx = INDEX_NONE; - for (int32 Idx = 0; Idx < Component->WeightmapLayerAllocations.Num(); Idx++) + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(); + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(); + + for (int32 Idx = 0; Idx < ComponentWeightmapLayerAllocations.Num(); Idx++) { - if (Component->WeightmapLayerAllocations[Idx].LayerInfo == LayerInfo) + if (ComponentWeightmapLayerAllocations[Idx].LayerInfo == LayerInfo) { LayerIdx = Idx; break; @@ -86,11 +89,11 @@ LANDSCAPE_API bool FLandscapeComponentDataInterface::GetWeightmapTextureData(ULa { return false; } - if (Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex >= Component->WeightmapTextures.Num()) + if (ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex >= ComponentWeightmapTextures.Num()) { return false; } - if (Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel >= 4) + if (ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel >= 4) { return false; } @@ -99,19 +102,19 @@ LANDSCAPE_API bool FLandscapeComponentDataInterface::GetWeightmapTextureData(ULa OutData.Empty(FMath::Square(WeightmapSize)); OutData.AddUninitialized(FMath::Square(WeightmapSize)); - FColor* WeightMipData = (FColor*)DataInterface.LockMip(Component->WeightmapTextures[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex], MipLevel); + FColor* WeightMipData = (FColor*)DataInterface.LockMip(ComponentWeightmapTextures[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex], MipLevel); // Channel remapping int32 ChannelOffsets[4] = { (int32)STRUCT_OFFSET(FColor, R), (int32)STRUCT_OFFSET(FColor, G), (int32)STRUCT_OFFSET(FColor, B), (int32)STRUCT_OFFSET(FColor, A) }; - uint8* SrcTextureData = (uint8*)WeightMipData + ChannelOffsets[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; + uint8* SrcTextureData = (uint8*)WeightMipData + ChannelOffsets[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; for (int32 i = 0; i < FMath::Square(WeightmapSize); i++) { OutData[i] = SrcTextureData[i * 4]; } - DataInterface.UnlockMip(Component->WeightmapTextures[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex], MipLevel); + DataInterface.UnlockMip(ComponentWeightmapTextures[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex], MipLevel); return true; } diff --git a/Engine/Source/Runtime/Landscape/Private/LandscapeEdit.cpp b/Engine/Source/Runtime/Landscape/Private/LandscapeEdit.cpp index 4858ba0b54d9..cf4a3c1af53f 100644 --- a/Engine/Source/Runtime/Landscape/Private/LandscapeEdit.cpp +++ b/Engine/Source/Runtime/Landscape/Private/LandscapeEdit.cpp @@ -143,7 +143,7 @@ ULandscapeMaterialInstanceConstant* ALandscapeProxy::GetLayerThumbnailMIC(UMater ULandscapeMaterialInstanceConstant* MaterialInstance = NewObject(GetTransientPackage()); MaterialInstance->bIsLayerThumbnail = true; MaterialInstance->bMobile = false; - MaterialInstance->SetParentEditorOnly(LandscapeMaterial); + MaterialInstance->SetParentEditorOnly(LandscapeMaterial, false); FStaticParameterSet StaticParameters; MaterialInstance->GetStaticParameterValues(StaticParameters); @@ -260,7 +260,7 @@ UMaterialInstanceConstant* ULandscapeComponent::GetCombinationMaterial(FMaterial CombinationMaterialInstance = LandscapeCombinationMaterialInstance; UE_LOG(LogLandscape, Log, TEXT("Looking for key %s, making new combination %s"), *LayerKey, *CombinationMaterialInstance->GetName()); Proxy->MaterialInstanceConstantMap.Add(*LayerKey, CombinationMaterialInstance); - CombinationMaterialInstance->SetParentEditorOnly(MaterialToUse); + CombinationMaterialInstance->SetParentEditorOnly(MaterialToUse, false); CombinationMaterialInstance->BasePropertyOverrides.bOverride_BlendMode = bOverrideBlendMode; if (bOverrideBlendMode) @@ -331,12 +331,16 @@ void ULandscapeComponent::UpdateMaterialInstances_Internal(FMaterialUpdateContex int8 TessellatedMaterialCount = 0; int8 MaterialIndex = 0; + TArray& WeightmapBaseLayerAllocation = GetWeightmapLayerAllocations(); + TArray& WeightmapBaseTexture = GetWeightmapTextures(); + UTexture2D* BaseHeightmap = GetHeightmap(); + for (auto It = MaterialPerLOD.CreateConstIterator(); It; ++It) { const int8 MaterialLOD = It.Value(); // Find or set a matching MIC in the Landscape's map. - UMaterialInstanceConstant* CombinationMaterialInstance = GetCombinationMaterial(&Context, WeightmapLayerAllocations, MaterialLOD, false); + UMaterialInstanceConstant* CombinationMaterialInstance = GetCombinationMaterial(&Context, WeightmapBaseLayerAllocation, MaterialLOD, false); if (CombinationMaterialInstance != nullptr) { @@ -356,24 +360,24 @@ void ULandscapeComponent::UpdateMaterialInstances_Internal(FMaterialUpdateContex FLinearColor Masks[4] = { FLinearColor(1.0f, 0.0f, 0.0f, 0.0f), FLinearColor(0.0f, 1.0f, 0.0f, 0.0f), FLinearColor(0.0f, 0.0f, 1.0f, 0.0f), FLinearColor(0.0f, 0.0f, 0.0f, 1.0f) }; // Set the layer mask - for (int32 AllocIdx = 0; AllocIdx < WeightmapLayerAllocations.Num(); AllocIdx++) + for (int32 AllocIdx = 0; AllocIdx < WeightmapBaseLayerAllocation.Num(); AllocIdx++) { - FWeightmapLayerAllocationInfo& Allocation = WeightmapLayerAllocations[AllocIdx]; + FWeightmapLayerAllocationInfo& Allocation = WeightmapBaseLayerAllocation[AllocIdx]; FName LayerName = Allocation.LayerInfo == ALandscapeProxy::VisibilityLayer ? UMaterialExpressionLandscapeVisibilityMask::ParameterName : Allocation.LayerInfo ? Allocation.LayerInfo->LayerName : NAME_None; MaterialInstance->SetVectorParameterValueEditorOnly(FName(*FString::Printf(TEXT("LayerMask_%s"), *LayerName.ToString())), Masks[Allocation.WeightmapTextureChannel]); } // Set the weightmaps - for (int32 i = 0; i < WeightmapTextures.Num(); i++) + for (int32 i = 0; i < WeightmapBaseTexture.Num(); i++) { - MaterialInstance->SetTextureParameterValueEditorOnly(FName(*FString::Printf(TEXT("Weightmap%d"), i)), WeightmapTextures[i]); + MaterialInstance->SetTextureParameterValueEditorOnly(FName(*FString::Printf(TEXT("Weightmap%d"), i)), WeightmapBaseTexture[i]); } // Set the heightmap, if needed. - if (HeightmapTexture) + if (BaseHeightmap) { - MaterialInstance->SetTextureParameterValueEditorOnly(FName(TEXT("Heightmap")), HeightmapTexture); + MaterialInstance->SetTextureParameterValueEditorOnly(FName(TEXT("Heightmap")), BaseHeightmap); } MaterialInstance->PostEditChange(); @@ -417,7 +421,10 @@ void ULandscapeComponent::UpdateMaterialInstances_Internal(FMaterialUpdateContex { const int8 MaterialLOD = It.Value(); - MobileCombinationMaterialInstances[MobileMaterialIndex] = GetCombinationMaterial(&Context, MobileWeightmapLayerAllocations, MaterialLOD, true); + UMaterialInstanceConstant* MobileCombinationMaterialInstance = GetCombinationMaterial(&Context, MobileWeightmapLayerAllocations, MaterialLOD, true); + MobileCombinationMaterialInstances[MobileMaterialIndex] = MobileCombinationMaterialInstance; + Context.AddMaterialInstance(MobileCombinationMaterialInstance); + ++MobileMaterialIndex; } } @@ -460,6 +467,8 @@ void ALandscapeProxy::UpdateAllComponentMaterialInstances() { // we're not having the material update context recreate render states because we will manually do it for only our components TArray RecreateRenderStateContexts; + RecreateRenderStateContexts.Reserve(LandscapeComponents.Num()); + for (ULandscapeComponent* Component : LandscapeComponents) { RecreateRenderStateContexts.Emplace(Component); @@ -559,6 +568,7 @@ void ULandscapeComponent::PostEditUndo() void ALandscapeProxy::FixupWeightmaps() { WeightmapUsageMap.Empty(); + for (ULandscapeComponent* Component : LandscapeComponents) { Component->FixupWeightmaps(); @@ -574,6 +584,9 @@ void ULandscapeComponent::FixupWeightmaps() if (Info) { + WeightmapTexturesUsage.Empty(); + WeightmapTexturesUsage.AddDefaulted(WeightmapTextures.Num()); + TArray LayersToDelete; bool bFixedLayerDeletion = false; @@ -635,16 +648,26 @@ void ULandscapeComponent::FixupWeightmaps() } UTexture2D* WeightmapTexture = WeightmapTextures[Allocation.WeightmapTextureIndex]; - FLandscapeWeightmapUsage& Usage = Proxy->WeightmapUsageMap.FindOrAdd(WeightmapTexture); + + ULandscapeWeightmapUsage** TempUsage = Proxy->WeightmapUsageMap.Find(WeightmapTexture); + + if (TempUsage == nullptr) + { + TempUsage = &Proxy->WeightmapUsageMap.Add(WeightmapTexture, NewObject(GetLandscapeProxy())); + (*TempUsage)->ProceduralLayerGuid.Invalidate(); + } + + ULandscapeWeightmapUsage* Usage = *TempUsage; + WeightmapTexturesUsage[Allocation.WeightmapTextureIndex] = Usage; // Keep a ref to it for faster access // Detect a shared layer allocation, caused by a previous undo or layer deletion bugs - if (Usage.ChannelUsage[Allocation.WeightmapTextureChannel] != nullptr && - Usage.ChannelUsage[Allocation.WeightmapTextureChannel] != this) + if (Usage->ChannelUsage[Allocation.WeightmapTextureChannel] != nullptr && + Usage->ChannelUsage[Allocation.WeightmapTextureChannel] != this) { FFormatNamedArguments Arguments; Arguments.Add(TEXT("LayerName"), FText::FromString(Allocation.GetLayerName().ToString())); Arguments.Add(TEXT("LandscapeName"), FText::FromString(GetName())); - Arguments.Add(TEXT("ChannelName"), FText::FromString(Usage.ChannelUsage[Allocation.WeightmapTextureChannel]->GetName())); + Arguments.Add(TEXT("ChannelName"), FText::FromString(Usage->ChannelUsage[Allocation.WeightmapTextureChannel]->GetName())); FMessageLog("MapCheck").Warning() ->AddToken(FTextToken::Create(FText::Format(LOCTEXT("MapCheck_Message_FixedUpSharedLayerWeightmap", "Fixed up shared weightmap texture for layer {LayerName} in component '{LandscapeName}' (shares with '{ChannelName}')"), Arguments))) ->AddToken(FMapErrorToken::Create(FMapErrors::FixedUpSharedLayerWeightmap)); @@ -654,7 +677,7 @@ void ULandscapeComponent::FixupWeightmaps() } else { - Usage.ChannelUsage[Allocation.WeightmapTextureChannel] = this; + Usage->ChannelUsage[Allocation.WeightmapTextureChannel] = this; } } @@ -675,7 +698,9 @@ void ULandscapeComponent::FixupWeightmaps() void ULandscapeComponent::UpdateLayerWhitelistFromPaintedLayers() { - for (const auto& Allocation : WeightmapLayerAllocations) + TArray& ComponentWeightmapLayerAllocations = GetWeightmapLayerAllocations(); + + for (const auto& Allocation : ComponentWeightmapLayerAllocations) { LayerWhitelist.AddUnique(Allocation.LayerInfo); } @@ -1147,10 +1172,13 @@ void ULandscapeComponent::UpdateCollisionLayerData(const FColor* const* const We bool bExistingLayerMismatch = false; int32 DataLayerIdx = INDEX_NONE; + TArray& ComponentWeightmapLayerAllocations = GetWeightmapLayerAllocations(true); + TArray& ComponentWeightmapsTexture = GetWeightmapTextures(true); + // Find the layers we're interested in - for (int32 AllocIdx = 0; AllocIdx < WeightmapLayerAllocations.Num(); AllocIdx++) + for (int32 AllocIdx = 0; AllocIdx < ComponentWeightmapLayerAllocations.Num(); AllocIdx++) { - FWeightmapLayerAllocationInfo& AllocInfo = WeightmapLayerAllocations[AllocIdx]; + FWeightmapLayerAllocationInfo& AllocInfo = ComponentWeightmapLayerAllocations[AllocIdx]; ULandscapeLayerInfoObject* LayerInfo = AllocInfo.LayerInfo; if (LayerInfo == ALandscapeProxy::VisibilityLayer || LayerInfo != nullptr) { @@ -1219,7 +1247,7 @@ void ULandscapeComponent::UpdateCollisionLayerData(const FColor* const* const We DominantLayerData = (uint8*)CollisionComp->DominantLayerData.Lock(LOCK_READ_WRITE); } - const int32 WeightmapSizeU = WeightmapTextures[0]->Source.GetSizeX(); + const int32 WeightmapSizeU = ComponentWeightmapsTexture[0]->Source.GetSizeX(); const int32 MipSizeU = WeightmapSizeU >> CollisionMipLevel; // Ratio to convert update region coordinate to collision mip coordinates @@ -1380,16 +1408,18 @@ void ULandscapeComponent::UpdateCollisionLayerData(const FColor* const* const We void ULandscapeComponent::UpdateCollisionLayerData() { + TArray& ComponentWeightmapsTexture = GetWeightmapTextures(true); + // Generate the dominant layer data TArray WeightmapTextureMipData; TArray> CachedWeightmapTextureMipData; - WeightmapTextureMipData.Empty(WeightmapTextures.Num()); - CachedWeightmapTextureMipData.Empty(WeightmapTextures.Num()); - for (int32 WeightmapIdx = 0; WeightmapIdx < WeightmapTextures.Num(); ++WeightmapIdx) + WeightmapTextureMipData.Empty(ComponentWeightmapsTexture.Num()); + CachedWeightmapTextureMipData.Empty(ComponentWeightmapsTexture.Num()); + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapsTexture.Num(); ++WeightmapIdx) { TArray MipData; - WeightmapTextures[WeightmapIdx]->Source.GetMipData(MipData, CollisionMipLevel); + ComponentWeightmapsTexture[WeightmapIdx]->Source.GetMipData(MipData, CollisionMipLevel); WeightmapTextureMipData.Add((FColor*)MipData.GetData()); CachedWeightmapTextureMipData.Add(MoveTemp(MipData)); } @@ -1398,10 +1428,10 @@ void ULandscapeComponent::UpdateCollisionLayerData() TArray> SimpleCollisionCachedWeightmapTextureMipData; if (SimpleCollisionMipLevel > CollisionMipLevel) { - for (int32 WeightmapIdx = 0; WeightmapIdx < WeightmapTextures.Num(); ++WeightmapIdx) + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapsTexture.Num(); ++WeightmapIdx) { TArray MipData; - WeightmapTextures[WeightmapIdx]->Source.GetMipData(MipData, SimpleCollisionMipLevel); + ComponentWeightmapsTexture[WeightmapIdx]->Source.GetMipData(MipData, SimpleCollisionMipLevel); SimpleCollisionWeightmapMipData.Add((FColor*)MipData.GetData()); SimpleCollisionCachedWeightmapTextureMipData.Add(MoveTemp(MipData)); } @@ -2338,10 +2368,12 @@ LANDSCAPE_API void ALandscapeProxy::Import( UE_LOG(LogLandscape, Log, TEXT("%s needs %d alphamaps"), *LandscapeComponent->GetName(), EditingAlphaLayerData.Num()); + TArray& ComponentWeightmapLayerAllocations = LandscapeComponent->GetWeightmapLayerAllocations(); + // Calculate weightmap weights for this component WeightValues.Empty(EditingAlphaLayerData.Num()); WeightValues.AddZeroed(EditingAlphaLayerData.Num()); - LandscapeComponent->WeightmapLayerAllocations.Empty(EditingAlphaLayerData.Num()); + ComponentWeightmapLayerAllocations.Empty(EditingAlphaLayerData.Num()); TArray> IsNoBlendArray; IsNoBlendArray.Empty(EditingAlphaLayerData.Num()); @@ -2351,7 +2383,7 @@ LANDSCAPE_API void ALandscapeProxy::Import( { // Lookup the original layer name WeightValues[WeightLayerIndex] = EditingAlphaLayerData[WeightLayerIndex].AlphaValues; - new(LandscapeComponent->WeightmapLayerAllocations) FWeightmapLayerAllocationInfo(ImportLayerInfos[EditingAlphaLayerData[WeightLayerIndex].LayerIndex].LayerInfo); + new(ComponentWeightmapLayerAllocations) FWeightmapLayerAllocationInfo(ImportLayerInfos[EditingAlphaLayerData[WeightLayerIndex].LayerIndex].LayerInfo); IsNoBlendArray[WeightLayerIndex] = ImportLayerInfos[EditingAlphaLayerData[WeightLayerIndex].LayerIndex].LayerInfo->bNoWeightBlend; } @@ -2386,7 +2418,7 @@ LANDSCAPE_API void ALandscapeProxy::Import( { // Remove the layer as it has no contribution WeightValues.RemoveAt(BelowWeightLayerIndex); - LandscapeComponent->WeightmapLayerAllocations.RemoveAt(BelowWeightLayerIndex); + ComponentWeightmapLayerAllocations.RemoveAt(BelowWeightLayerIndex); IsNoBlendArray.RemoveAt(BelowWeightLayerIndex); // The current layer has been re-numbered @@ -2507,18 +2539,23 @@ LANDSCAPE_API void ALandscapeProxy::Import( } } + TArray& ComponentWeightmapLayerAllocations = LandscapeComponent->GetWeightmapLayerAllocations(); + TArray& ComponentWeightmapTextures = LandscapeComponent->GetWeightmapTextures(); + TArray& ComponentWeightmapTexturesUsage = LandscapeComponent->GetWeightmapTexturesUsage(); + if (BestAllocationIndex != -1) { FWeightmapTextureAllocation& Allocation = TextureAllocations[BestAllocationIndex]; - FLandscapeWeightmapUsage& WeightmapUsage = WeightmapUsageMap.FindChecked(Allocation.Texture); + ULandscapeWeightmapUsage* WeightmapUsage = WeightmapUsageMap.FindChecked(Allocation.Texture); + ComponentWeightmapTexturesUsage.Add(WeightmapUsage); UE_LOG(LogLandscape, Log, TEXT(" ==> Storing %d channels starting at %s[%d]"), RemainingLayers, *Allocation.Texture->GetName(), Allocation.ChannelsInUse); for (int32 i = 0; i < RemainingLayers; i++) { - LandscapeComponent->WeightmapLayerAllocations[LayerIndex + i].WeightmapTextureIndex = LandscapeComponent->WeightmapTextures.Num(); - LandscapeComponent->WeightmapLayerAllocations[LayerIndex + i].WeightmapTextureChannel = Allocation.ChannelsInUse; - WeightmapUsage.ChannelUsage[Allocation.ChannelsInUse] = LandscapeComponent; + ComponentWeightmapLayerAllocations[LayerIndex + i].WeightmapTextureIndex = ComponentWeightmapTextures.Num(); + ComponentWeightmapLayerAllocations[LayerIndex + i].WeightmapTextureChannel = Allocation.ChannelsInUse; + WeightmapUsage->ChannelUsage[Allocation.ChannelsInUse] = LandscapeComponent; switch (Allocation.ChannelsInUse) { case 1: @@ -2539,7 +2576,7 @@ LANDSCAPE_API void ALandscapeProxy::Import( } LayerIndex += RemainingLayers; - LandscapeComponent->WeightmapTextures.Add(Allocation.Texture); + ComponentWeightmapTextures.Add(Allocation.Texture); } else { @@ -2549,39 +2586,40 @@ LANDSCAPE_API void ALandscapeProxy::Import( const int32 ThisAllocationLayers = FMath::Min(RemainingLayers, 4); new(TextureAllocations) FWeightmapTextureAllocation(ComponentX, ComponentY, ThisAllocationLayers, WeightmapTexture, MipData); - FLandscapeWeightmapUsage& WeightmapUsage = WeightmapUsageMap.Add(WeightmapTexture, FLandscapeWeightmapUsage()); + ULandscapeWeightmapUsage* WeightmapUsage = WeightmapUsageMap.Add(WeightmapTexture, NewObject(this)); + ComponentWeightmapTexturesUsage.Add(WeightmapUsage); UE_LOG(LogLandscape, Log, TEXT(" ==> Storing %d channels in new texture %s"), ThisAllocationLayers, *WeightmapTexture->GetName()); WeightmapTextureDataPointers.Add((uint8*)&MipData->R); - LandscapeComponent->WeightmapLayerAllocations[LayerIndex + 0].WeightmapTextureIndex = LandscapeComponent->WeightmapTextures.Num(); - LandscapeComponent->WeightmapLayerAllocations[LayerIndex + 0].WeightmapTextureChannel = 0; - WeightmapUsage.ChannelUsage[0] = LandscapeComponent; + ComponentWeightmapLayerAllocations[LayerIndex + 0].WeightmapTextureIndex = ComponentWeightmapTextures.Num(); + ComponentWeightmapLayerAllocations[LayerIndex + 0].WeightmapTextureChannel = 0; + WeightmapUsage->ChannelUsage[0] = LandscapeComponent; if (ThisAllocationLayers > 1) { WeightmapTextureDataPointers.Add((uint8*)&MipData->G); - LandscapeComponent->WeightmapLayerAllocations[LayerIndex + 1].WeightmapTextureIndex = LandscapeComponent->WeightmapTextures.Num(); - LandscapeComponent->WeightmapLayerAllocations[LayerIndex + 1].WeightmapTextureChannel = 1; - WeightmapUsage.ChannelUsage[1] = LandscapeComponent; + ComponentWeightmapLayerAllocations[LayerIndex + 1].WeightmapTextureIndex = ComponentWeightmapTextures.Num(); + ComponentWeightmapLayerAllocations[LayerIndex + 1].WeightmapTextureChannel = 1; + WeightmapUsage->ChannelUsage[1] = LandscapeComponent; if (ThisAllocationLayers > 2) { WeightmapTextureDataPointers.Add((uint8*)&MipData->B); - LandscapeComponent->WeightmapLayerAllocations[LayerIndex + 2].WeightmapTextureIndex = LandscapeComponent->WeightmapTextures.Num(); - LandscapeComponent->WeightmapLayerAllocations[LayerIndex + 2].WeightmapTextureChannel = 2; - WeightmapUsage.ChannelUsage[2] = LandscapeComponent; + ComponentWeightmapLayerAllocations[LayerIndex + 2].WeightmapTextureIndex = ComponentWeightmapTextures.Num(); + ComponentWeightmapLayerAllocations[LayerIndex + 2].WeightmapTextureChannel = 2; + WeightmapUsage->ChannelUsage[2] = LandscapeComponent; if (ThisAllocationLayers > 3) { WeightmapTextureDataPointers.Add((uint8*)&MipData->A); - LandscapeComponent->WeightmapLayerAllocations[LayerIndex + 3].WeightmapTextureIndex = LandscapeComponent->WeightmapTextures.Num(); - LandscapeComponent->WeightmapLayerAllocations[LayerIndex + 3].WeightmapTextureChannel = 3; - WeightmapUsage.ChannelUsage[3] = LandscapeComponent; + ComponentWeightmapLayerAllocations[LayerIndex + 3].WeightmapTextureIndex = ComponentWeightmapTextures.Num(); + ComponentWeightmapLayerAllocations[LayerIndex + 3].WeightmapTextureChannel = 3; + WeightmapUsage->ChannelUsage[3] = LandscapeComponent; } } } - LandscapeComponent->WeightmapTextures.Add(WeightmapTexture); + ComponentWeightmapTextures.Add(WeightmapTexture); LayerIndex += ThisAllocationLayers; } @@ -2782,9 +2820,6 @@ LANDSCAPE_API void ALandscapeProxy::Import( if (GetMutableDefault()->bProceduralLandscape) { SetupProceduralLayers(NumComponentsX, NumComponentsY); - - FEditorDelegates::PreSaveWorld.AddUObject(GetLandscapeActor(), &ALandscape::OnPreSaveWorld); - FEditorDelegates::PostSaveWorld.AddUObject(GetLandscapeActor(), &ALandscape::OnPostSaveWorld); } if (GetLevel()->bIsVisible) @@ -2878,10 +2913,11 @@ bool ALandscapeProxy::ExportToRawMesh(int32 InExportLOD, FMeshDescription& OutRa // Check if there are any holes const int32 VisThreshold = 170; TArray VisDataMap; + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(); - for (int32 AllocIdx = 0; AllocIdx < Component->WeightmapLayerAllocations.Num(); AllocIdx++) + for (int32 AllocIdx = 0; AllocIdx < ComponentWeightmapLayerAllocations.Num(); AllocIdx++) { - FWeightmapLayerAllocationInfo& AllocInfo = Component->WeightmapLayerAllocations[AllocIdx]; + FWeightmapLayerAllocationInfo& AllocInfo = ComponentWeightmapLayerAllocations[AllocIdx]; if (AllocInfo.LayerInfo == ALandscapeProxy::VisibilityLayer) { CDI.GetWeightmapTextureData(AllocInfo.LayerInfo, VisDataMap); @@ -3931,7 +3967,9 @@ void ALandscapeProxy::PostEditChangeProperty(FPropertyChangedEvent& PropertyChan bool bRemovedAnyLayers = false; for (ULandscapeComponent* Component : LandscapeComponents) { - int32 NumNullLayers = Algo::CountIf(Component->WeightmapLayerAllocations, [](const FWeightmapLayerAllocationInfo& Allocation) { return Allocation.LayerInfo == nullptr; }); + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(false); + + int32 NumNullLayers = Algo::CountIf(ComponentWeightmapLayerAllocations, [](const FWeightmapLayerAllocationInfo& Allocation) { return Allocation.LayerInfo == nullptr; }); if (NumNullLayers > 0) { FLandscapeEditDataInterface LandscapeEdit(Info); @@ -4497,14 +4535,18 @@ void ULandscapeInfo::ClearSelectedRegion(bool bIsComponentwise /*= true*/) } } -void ULandscapeComponent::ReallocateWeightmaps(FLandscapeEditDataInterface* DataInterface) +void ULandscapeComponent::ReallocateWeightmaps(FLandscapeEditDataInterface* DataInterface, bool InCanUseCurrentEditingWeightmap, bool InSaveToTransactionBuffer, bool InInitPlatformDataAsync, TArray* OutNewCreatedTextures) { ALandscapeProxy* Proxy = GetLandscapeProxy(); + TArray& ComponentWeightmapLayerAllocations = GetWeightmapLayerAllocations(InCanUseCurrentEditingWeightmap); + TArray& ComponentWeightmapTextures = GetWeightmapTextures(InCanUseCurrentEditingWeightmap); + TArray& ComponentWeightmapTexturesUsage = GetWeightmapTexturesUsage(InCanUseCurrentEditingWeightmap); + int32 NeededNewChannels = 0; - for (int32 LayerIdx = 0; LayerIdx < WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { - if (WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex == 255) + if (ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex == 255) { NeededNewChannels++; } @@ -4516,18 +4558,20 @@ void ULandscapeComponent::ReallocateWeightmaps(FLandscapeEditDataInterface* Data return; } - Modify(); - //Landscape->Modify(); - Proxy->Modify(); + if (InSaveToTransactionBuffer) + { + Modify(); + Proxy->Modify(); + } // UE_LOG(LogLandscape, Log, TEXT("----------------------")); // UE_LOG(LogLandscape, Log, TEXT("Component %s needs %d layers (%d new)"), *GetName(), WeightmapLayerAllocations.Num(), NeededNewChannels); // See if our existing textures have sufficient space int32 ExistingTexAvailableChannels = 0; - for (int32 TexIdx = 0; TexIdx < WeightmapTextures.Num(); TexIdx++) + for (int32 TexIdx = 0; TexIdx < ComponentWeightmapTextures.Num(); TexIdx++) { - FLandscapeWeightmapUsage* Usage = Proxy->WeightmapUsageMap.Find(WeightmapTextures[TexIdx]); + ULandscapeWeightmapUsage* Usage = ComponentWeightmapTexturesUsage[TexIdx]; check(Usage); ExistingTexAvailableChannels += Usage->FreeChannelCount(); @@ -4543,36 +4587,45 @@ void ULandscapeComponent::ReallocateWeightmaps(FLandscapeEditDataInterface* Data // UE_LOG(LogLandscape, Log, TEXT("Existing texture has available channels")); // Allocate using our existing textures' spare channels. - for (int32 TexIdx = 0; TexIdx < WeightmapTextures.Num(); TexIdx++) + int32 CurrentAlloc = 0; + for (int32 TexIdx = 0; TexIdx < ComponentWeightmapTextures.Num(); TexIdx++) { - FLandscapeWeightmapUsage* Usage = Proxy->WeightmapUsageMap.Find(WeightmapTextures[TexIdx]); + ULandscapeWeightmapUsage* Usage = ComponentWeightmapTexturesUsage[TexIdx]; for (int32 ChanIdx = 0; ChanIdx < 4; ChanIdx++) { if (Usage->ChannelUsage[ChanIdx] == nullptr) { - for (int32 LayerIdx = 0; LayerIdx < WeightmapLayerAllocations.Num(); LayerIdx++) + // Find next allocation to treat + for (; CurrentAlloc < ComponentWeightmapLayerAllocations.Num(); ++CurrentAlloc) { - FWeightmapLayerAllocationInfo& AllocInfo = WeightmapLayerAllocations[LayerIdx]; + FWeightmapLayerAllocationInfo& AllocInfo = ComponentWeightmapLayerAllocations[CurrentAlloc]; + if (AllocInfo.WeightmapTextureIndex == 255) { - // Zero out the data for this texture channel - if (DataInterface) - { - DataInterface->ZeroTextureChannel(WeightmapTextures[TexIdx], ChanIdx); - } - - AllocInfo.WeightmapTextureIndex = TexIdx; - AllocInfo.WeightmapTextureChannel = ChanIdx; - Usage->ChannelUsage[ChanIdx] = this; - NeededNewChannels--; - - if (NeededNewChannels == 0) - { - return; - } + break; } } + + FWeightmapLayerAllocationInfo& AllocInfo = ComponentWeightmapLayerAllocations[CurrentAlloc]; + check(AllocInfo.WeightmapTextureIndex == 255); + + // Zero out the data for this texture channel + if (DataInterface) + { + DataInterface->ZeroTextureChannel(ComponentWeightmapTextures[TexIdx], ChanIdx); + } + + AllocInfo.WeightmapTextureIndex = TexIdx; + AllocInfo.WeightmapTextureChannel = ChanIdx; + Usage->ChannelUsage[ChanIdx] = this; + + NeededNewChannels--; + + if (NeededNewChannels == 0) + { + return; + } } } } @@ -4583,15 +4636,16 @@ void ULandscapeComponent::ReallocateWeightmaps(FLandscapeEditDataInterface* Data // UE_LOG(LogLandscape, Log, TEXT("Reallocating.")); // We are totally reallocating the weightmap - int32 TotalNeededChannels = WeightmapLayerAllocations.Num(); + int32 TotalNeededChannels = ComponentWeightmapLayerAllocations.Num(); int32 CurrentLayer = 0; TArray NewWeightmapTextures; + TArray NewComponentWeightmapTexturesUsage; while (TotalNeededChannels > 0) { // UE_LOG(LogLandscape, Log, TEXT("Still need %d channels"), TotalNeededChannels); UTexture2D* CurrentWeightmapTexture = nullptr; - FLandscapeWeightmapUsage* CurrentWeightmapUsage = nullptr; + ULandscapeWeightmapUsage* CurrentWeightmapUsage = nullptr; if (TotalNeededChannels < 4) { @@ -4599,22 +4653,34 @@ void ULandscapeComponent::ReallocateWeightmaps(FLandscapeEditDataInterface* Data // see if we can find a suitable existing weightmap texture with sufficient channels int32 BestDistanceSquared = MAX_int32; - for (TMap::TIterator It(Proxy->WeightmapUsageMap); It; ++It) + for (auto& ItPair : Proxy->WeightmapUsageMap) { - FLandscapeWeightmapUsage* TryWeightmapUsage = &It.Value(); - if (TryWeightmapUsage->FreeChannelCount() >= TotalNeededChannels) + ULandscapeWeightmapUsage* TryWeightmapUsage = ItPair.Value; + // + FGuid ProceduralLayerGuidToSeek = InCanUseCurrentEditingWeightmap ? CurrentProceduralLayerGuid : FGuid(); + + if (TryWeightmapUsage->FreeChannelCount() >= TotalNeededChannels && TryWeightmapUsage->ProceduralLayerGuid == ProceduralLayerGuidToSeek) { - // See if this candidate is closer than any others we've found - for (int32 ChanIdx = 0; ChanIdx < 4; ChanIdx++) + if (TryWeightmapUsage->FreeChannelCount() == 4) { - if (TryWeightmapUsage->ChannelUsage[ChanIdx] != nullptr) + CurrentWeightmapTexture = ItPair.Key; + CurrentWeightmapUsage = TryWeightmapUsage; + break; + } + else + { + // See if this candidate is closer than any others we've found + for (int32 ChanIdx = 0; ChanIdx < 4; ChanIdx++) { - int32 TryDistanceSquared = (TryWeightmapUsage->ChannelUsage[ChanIdx]->GetSectionBase() - GetSectionBase()).SizeSquared(); - if (TryDistanceSquared < BestDistanceSquared) + if (TryWeightmapUsage->ChannelUsage[ChanIdx] != nullptr) { - CurrentWeightmapTexture = It.Key(); - CurrentWeightmapUsage = TryWeightmapUsage; - BestDistanceSquared = TryDistanceSquared; + int32 TryDistanceSquared = (TryWeightmapUsage->ChannelUsage[ChanIdx]->GetSectionBase() - GetSectionBase()).SizeSquared(); + if (TryDistanceSquared < BestDistanceSquared) + { + CurrentWeightmapTexture = ItPair.Key; + CurrentWeightmapUsage = TryWeightmapUsage; + BestDistanceSquared = TryDistanceSquared; + } } } } @@ -4633,16 +4699,32 @@ void ULandscapeComponent::ReallocateWeightmaps(FLandscapeEditDataInterface* Data // We need a new weightmap texture CurrentWeightmapTexture = GetLandscapeProxy()->CreateLandscapeTexture(WeightmapSize, WeightmapSize, TEXTUREGROUP_Terrain_Weightmap, TSF_BGRA8); + // Alloc dummy mips - CreateEmptyTextureMips(CurrentWeightmapTexture); - CurrentWeightmapTexture->PostEditChange(); + CreateEmptyTextureMips(CurrentWeightmapTexture, true); + + if (InInitPlatformDataAsync) + { + CurrentWeightmapTexture->BeginCachePlatformData(); + CurrentWeightmapTexture->ClearAllCachedCookedPlatformData(); + } + else + { + CurrentWeightmapTexture->PostEditChange(); + } + + if (OutNewCreatedTextures != nullptr) + { + OutNewCreatedTextures->Add(CurrentWeightmapTexture); + } // Store it in the usage map - CurrentWeightmapUsage = &Proxy->WeightmapUsageMap.Add(CurrentWeightmapTexture, FLandscapeWeightmapUsage()); - + CurrentWeightmapUsage = Proxy->WeightmapUsageMap.Add(CurrentWeightmapTexture, NewObject(GetLandscapeProxy())); + CurrentWeightmapUsage->ProceduralLayerGuid = InCanUseCurrentEditingWeightmap ? CurrentProceduralLayerGuid : FGuid(); // UE_LOG(LogLandscape, Log, TEXT("Making a new texture %s"), *CurrentWeightmapTexture->GetName()); } + NewComponentWeightmapTexturesUsage.Add(CurrentWeightmapUsage); NewWeightmapTextures.Add(CurrentWeightmapTexture); for (int32 ChanIdx = 0; ChanIdx < 4 && TotalNeededChannels > 0; ChanIdx++) @@ -4652,7 +4734,7 @@ void ULandscapeComponent::ReallocateWeightmaps(FLandscapeEditDataInterface* Data if (CurrentWeightmapUsage->ChannelUsage[ChanIdx] == nullptr) { // Use this allocation - FWeightmapLayerAllocationInfo& AllocInfo = WeightmapLayerAllocations[CurrentLayer]; + FWeightmapLayerAllocationInfo& AllocInfo = ComponentWeightmapLayerAllocations[CurrentLayer]; if (AllocInfo.WeightmapTextureIndex == 255) { @@ -4665,7 +4747,7 @@ void ULandscapeComponent::ReallocateWeightmaps(FLandscapeEditDataInterface* Data } else { - UTexture2D* OldWeightmapTexture = WeightmapTextures[AllocInfo.WeightmapTextureIndex]; + UTexture2D* OldWeightmapTexture = ComponentWeightmapTextures[AllocInfo.WeightmapTextureIndex]; // Copy the data if (ensure(DataInterface != nullptr)) // it's not safe to skip the copy @@ -4676,7 +4758,7 @@ void ULandscapeComponent::ReallocateWeightmaps(FLandscapeEditDataInterface* Data } // Remove the old allocation - FLandscapeWeightmapUsage* OldWeightmapUsage = Proxy->WeightmapUsageMap.Find(OldWeightmapTexture); + ULandscapeWeightmapUsage* OldWeightmapUsage = ComponentWeightmapTexturesUsage[AllocInfo.WeightmapTextureIndex]; OldWeightmapUsage->ChannelUsage[AllocInfo.WeightmapTextureChannel] = nullptr; } @@ -4690,15 +4772,12 @@ void ULandscapeComponent::ReallocateWeightmaps(FLandscapeEditDataInterface* Data } } - // Replace the weightmap textures - WeightmapTextures = MoveTemp(NewWeightmapTextures); - if (DataInterface) { // Update the mipmaps for the textures we edited - for (int32 Idx = 0; Idx < WeightmapTextures.Num(); Idx++) + for (int32 Idx = 0; Idx < NewWeightmapTextures.Num(); Idx++) { - UTexture2D* WeightmapTexture = WeightmapTextures[Idx]; + UTexture2D* WeightmapTexture = NewWeightmapTextures[Idx]; FLandscapeTextureDataInfo* WeightmapDataInfo = DataInterface->GetTextureDataInfo(WeightmapTexture); int32 NumMips = WeightmapTexture->Source.GetNumMips(); @@ -4712,17 +4791,21 @@ void ULandscapeComponent::ReallocateWeightmaps(FLandscapeEditDataInterface* Data ULandscapeComponent::UpdateWeightmapMips(NumSubsections, SubsectionSizeQuads, WeightmapTexture, WeightmapTextureMipData, 0, 0, MAX_int32, MAX_int32, WeightmapDataInfo); } } + + // Replace the weightmap textures + SetWeightmapTextures(MoveTemp(NewWeightmapTextures), InCanUseCurrentEditingWeightmap); + SetWeightmapTexturesUsage(MoveTemp(NewComponentWeightmapTexturesUsage), InCanUseCurrentEditingWeightmap); } void ALandscapeProxy::RemoveInvalidWeightmaps() { if (GIsEditor) { - for (TMap< UTexture2D*, struct FLandscapeWeightmapUsage >::TIterator It(WeightmapUsageMap); It; ++It) + for (TMap< UTexture2D*, ULandscapeWeightmapUsage* >::TIterator It(WeightmapUsageMap); It; ++It) { UTexture2D* Tex = It.Key(); - FLandscapeWeightmapUsage& Usage = It.Value(); - if (Usage.FreeChannelCount() == 4) // Invalid Weight-map + ULandscapeWeightmapUsage* Usage = It.Value(); + if (Usage->FreeChannelCount() == 4) // Invalid Weight-map { if (Tex) { @@ -4731,7 +4814,8 @@ void ALandscapeProxy::RemoveInvalidWeightmaps() Tex->MarkPackageDirty(); Tex->ClearFlags(RF_Standalone); } - WeightmapUsageMap.Remove(Tex); + + It.RemoveCurrent(); } } @@ -4746,16 +4830,20 @@ void ALandscapeProxy::RemoveInvalidWeightmaps() void ULandscapeComponent::RemoveInvalidWeightmaps() { + TArray& ComponentWeightmapLayerAllocations = GetWeightmapLayerAllocations(); + TArray& ComponentWeightmapTextures = GetWeightmapTextures(); + TArray& ComponentWeightmapTexturesUsage = GetWeightmapTexturesUsage(); + // Adjust WeightmapTextureIndex index for other layers TSet UnUsedTextureIndices; { TSet UsedTextureIndices; - for (int32 LayerIdx = 0; LayerIdx < WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { - UsedTextureIndices.Add(WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex); + UsedTextureIndices.Add(ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex); } - for (int32 WeightIdx = 0; WeightIdx < WeightmapTextures.Num(); ++WeightIdx) + for (int32 WeightIdx = 0; WeightIdx < ComponentWeightmapTextures.Num(); ++WeightIdx) { if (!UsedTextureIndices.Contains(WeightIdx)) { @@ -4768,16 +4856,18 @@ void ULandscapeComponent::RemoveInvalidWeightmaps() for (int32 UnusedIndex : UnUsedTextureIndices) { int32 WeightmapTextureIndexToRemove = UnusedIndex - RemovedTextures; - WeightmapTextures[WeightmapTextureIndexToRemove]->SetFlags(RF_Transactional); - WeightmapTextures[WeightmapTextureIndexToRemove]->Modify(); - WeightmapTextures[WeightmapTextureIndexToRemove]->MarkPackageDirty(); - WeightmapTextures[WeightmapTextureIndexToRemove]->ClearFlags(RF_Standalone); - WeightmapTextures.RemoveAt(WeightmapTextureIndexToRemove); + ComponentWeightmapTextures[WeightmapTextureIndexToRemove]->SetFlags(RF_Transactional); + ComponentWeightmapTextures[WeightmapTextureIndexToRemove]->Modify(); + ComponentWeightmapTextures[WeightmapTextureIndexToRemove]->MarkPackageDirty(); + ComponentWeightmapTextures[WeightmapTextureIndexToRemove]->ClearFlags(RF_Standalone); + ComponentWeightmapTextures.RemoveAt(WeightmapTextureIndexToRemove); + + ComponentWeightmapTexturesUsage.RemoveAt(WeightmapTextureIndexToRemove); // Adjust WeightmapTextureIndex index for other layers - for (int32 LayerIdx = 0; LayerIdx < WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { - FWeightmapLayerAllocationInfo& Allocation = WeightmapLayerAllocations[LayerIdx]; + FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx]; if (Allocation.WeightmapTextureIndex > WeightmapTextureIndexToRemove) { @@ -4899,7 +4989,7 @@ void ULandscapeComponent::InitWeightmapData(TArray& new (WeightmapLayerAllocations)FWeightmapLayerAllocationInfo(LayerInfos[Idx]); } - ReallocateWeightmaps(nullptr); + ReallocateWeightmaps(); check(WeightmapLayerAllocations.Num() > 0 && WeightmapTextures.Num() > 0); @@ -4959,6 +5049,8 @@ void ULandscapeComponent::InitWeightmapData(TArray& LODIndexToMaterialIndex.Empty(1); LODIndexToMaterialIndex.Add(0); + + // TODO: need to update procedural? } #define MAX_LANDSCAPE_EXPORT_COMPONENTS_NUM 16 diff --git a/Engine/Source/Runtime/Landscape/Private/LandscapeEditInterface.cpp b/Engine/Source/Runtime/Landscape/Private/LandscapeEditInterface.cpp index 4a8730b7f865..2be36d6ca787 100644 --- a/Engine/Source/Runtime/Landscape/Private/LandscapeEditInterface.cpp +++ b/Engine/Source/Runtime/Landscape/Private/LandscapeEditInterface.cpp @@ -22,6 +22,7 @@ LandscapeEditInterface.cpp: Landscape editing interface #include "LandscapeRender.h" #include "ComponentReregisterContext.h" #include "Algo/Transform.h" +#include "Settings/EditorExperimentalSettings.h" // Channel remapping extern const size_t ChannelOffsets[4] = {STRUCT_OFFSET(FColor,R), STRUCT_OFFSET(FColor,G), STRUCT_OFFSET(FColor,B), STRUCT_OFFSET(FColor,A)}; @@ -1502,9 +1503,12 @@ void FLandscapeEditDataInterface::GetHeightDataFast(const int32 X1, const int32 void ULandscapeComponent::DeleteLayer(ULandscapeLayerInfoObject* LayerInfo, FLandscapeEditDataInterface& LandscapeEdit) { ULandscapeComponent* Component = this; + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(true); + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(true); + TArray& ComponentWeightmapTexturesUsage = Component->GetWeightmapTexturesUsage(true); // Find the index for this layer in this component. - const int32 DeleteLayerIdx = Component->WeightmapLayerAllocations.IndexOfByPredicate( + const int32 DeleteLayerIdx = ComponentWeightmapLayerAllocations.IndexOfByPredicate( [LayerInfo](const FWeightmapLayerAllocationInfo& Allocation) { return Allocation.LayerInfo == LayerInfo; }); if (DeleteLayerIdx == INDEX_NONE) { @@ -1512,14 +1516,14 @@ void ULandscapeComponent::DeleteLayer(ULandscapeLayerInfoObject* LayerInfo, FLan return; } - FWeightmapLayerAllocationInfo& DeleteLayerAllocation = Component->WeightmapLayerAllocations[DeleteLayerIdx]; + FWeightmapLayerAllocationInfo& DeleteLayerAllocation = ComponentWeightmapLayerAllocations[DeleteLayerIdx]; int32 DeleteLayerWeightmapTextureIndex = DeleteLayerAllocation.WeightmapTextureIndex; // See if we'll be able to remove the texture completely. bool bCanRemoveLayerTexture = true; - for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { - FWeightmapLayerAllocationInfo& Allocation = Component->WeightmapLayerAllocations[LayerIdx]; + FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx]; // check if we will be able to remove the texture also if (LayerIdx != DeleteLayerIdx && Allocation.WeightmapTextureIndex == DeleteLayerWeightmapTextureIndex) @@ -1535,18 +1539,18 @@ void ULandscapeComponent::DeleteLayer(ULandscapeLayerInfoObject* LayerInfo, FLan { // Lock data for all the weightmaps TArray TexDataInfos; - for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); WeightmapIdx++) + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++) { - TexDataInfos.Add(LandscapeEdit.GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx])); + TexDataInfos.Add(LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx])); } TArray LayerNoWeightBlends; // Array of NoWeightBlend flags TArray LayerDataPtrs; // Pointers to all layers' data // Get the data for each layer - for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { - FWeightmapLayerAllocationInfo& Allocation = Component->WeightmapLayerAllocations[LayerIdx]; + FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx]; LayerDataPtrs.Add((uint8*)TexDataInfos[Allocation.WeightmapTextureIndex]->GetMipData(0) + ChannelOffsets[Allocation.WeightmapTextureChannel]); // Find the layer info and record if it is a bNoWeightBlend layer. @@ -1573,7 +1577,7 @@ void ULandscapeComponent::DeleteLayer(ULandscapeLayerInfoObject* LayerInfo, FLan // Calculate the sum of other layer weights int32 OtherLayerWeightSum = 0; - for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { if (LayerIdx != DeleteLayerIdx && LayerNoWeightBlends[LayerIdx] == false) { @@ -1586,7 +1590,7 @@ void ULandscapeComponent::DeleteLayer(ULandscapeLayerInfoObject* LayerInfo, FLan // Set the first other weight-blend layer we can find to 255 to avoid a black hole // This isn't ideal but it's the best option // There's nothing we can easily do if this was the only weight-blend layer on this component - for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { if (LayerIdx != DeleteLayerIdx && LayerNoWeightBlends[LayerIdx] == false) { @@ -1599,7 +1603,7 @@ void ULandscapeComponent::DeleteLayer(ULandscapeLayerInfoObject* LayerInfo, FLan else { // Adjust other layer weights - for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { if (LayerIdx != DeleteLayerIdx && LayerNoWeightBlends[LayerIdx] == false) { @@ -1614,7 +1618,7 @@ void ULandscapeComponent::DeleteLayer(ULandscapeLayerInfoObject* LayerInfo, FLan } // Update all the textures and mips - for (int32 Idx = 0; Idx < Component->WeightmapTextures.Num(); Idx++) + for (int32 Idx = 0; Idx < ComponentWeightmapTextures.Num(); Idx++) { if (bCanRemoveLayerTexture && Idx == DeleteLayerWeightmapTextureIndex) { @@ -1622,7 +1626,7 @@ void ULandscapeComponent::DeleteLayer(ULandscapeLayerInfoObject* LayerInfo, FLan continue; } - UTexture2D* WeightmapTexture = Component->WeightmapTextures[Idx]; + UTexture2D* WeightmapTexture = ComponentWeightmapTextures[Idx]; FLandscapeTextureDataInfo* WeightmapDataInfo = TexDataInfos[Idx]; const int32 NumMips = WeightmapTexture->Source.GetNumMips(); @@ -1644,36 +1648,36 @@ void ULandscapeComponent::DeleteLayer(ULandscapeLayerInfoObject* LayerInfo, FLan Component->Modify(); Proxy->Modify(); - FLandscapeWeightmapUsage* Usage = Proxy->WeightmapUsageMap.Find(Component->WeightmapTextures[DeleteLayerAllocation.WeightmapTextureIndex]); + ULandscapeWeightmapUsage* Usage = ComponentWeightmapTexturesUsage.IsValidIndex(DeleteLayerAllocation.WeightmapTextureIndex) ? ComponentWeightmapTexturesUsage[DeleteLayerAllocation.WeightmapTextureIndex] : nullptr; if (Usage) // can be null if WeightmapUsageMap hasn't been built yet { Usage->ChannelUsage[DeleteLayerAllocation.WeightmapTextureChannel] = nullptr; } // Remove the layer - Component->WeightmapLayerAllocations.RemoveAt(DeleteLayerIdx); + ComponentWeightmapLayerAllocations.RemoveAt(DeleteLayerIdx); // If this layer was the last usage for this channel in this layer, we can remove it. if (bCanRemoveLayerTexture) { - Component->WeightmapTextures[DeleteLayerWeightmapTextureIndex]->SetFlags(RF_Transactional); - Component->WeightmapTextures[DeleteLayerWeightmapTextureIndex]->Modify(); - Component->WeightmapTextures[DeleteLayerWeightmapTextureIndex]->MarkPackageDirty(); - Component->WeightmapTextures[DeleteLayerWeightmapTextureIndex]->ClearFlags(RF_Standalone); + ComponentWeightmapTextures[DeleteLayerWeightmapTextureIndex]->SetFlags(RF_Transactional); + ComponentWeightmapTextures[DeleteLayerWeightmapTextureIndex]->Modify(); + ComponentWeightmapTextures[DeleteLayerWeightmapTextureIndex]->MarkPackageDirty(); + ComponentWeightmapTextures[DeleteLayerWeightmapTextureIndex]->ClearFlags(RF_Standalone); - Component->WeightmapTextures.RemoveAt(DeleteLayerWeightmapTextureIndex); + ComponentWeightmapTextures.RemoveAt(DeleteLayerWeightmapTextureIndex); // Adjust WeightmapTextureIndex index for other layers - for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { - FWeightmapLayerAllocationInfo& Allocation = Component->WeightmapLayerAllocations[LayerIdx]; + FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx]; if (Allocation.WeightmapTextureIndex > DeleteLayerWeightmapTextureIndex) { Allocation.WeightmapTextureIndex--; } - check(Allocation.WeightmapTextureIndex < Component->WeightmapTextures.Num()); + check(Allocation.WeightmapTextureIndex < ComponentWeightmapTextures.Num()); } } @@ -1684,16 +1688,16 @@ void ULandscapeComponent::DeleteLayer(ULandscapeLayerInfoObject* LayerInfo, FLan // Update dominant layer info stored in collision component TArray CollisionWeightmapMipData; - for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); WeightmapIdx++) + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++) { - CollisionWeightmapMipData.Add((FColor*)LandscapeEdit.GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx])->GetMipData(Component->CollisionMipLevel)); + CollisionWeightmapMipData.Add((FColor*)LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx])->GetMipData(Component->CollisionMipLevel)); } TArray SimpleCollisionWeightmapMipData; if (Component->SimpleCollisionMipLevel > Component->CollisionMipLevel) { - for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); WeightmapIdx++) + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++) { - SimpleCollisionWeightmapMipData.Add((FColor*)LandscapeEdit.GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx])->GetMipData(Component->SimpleCollisionMipLevel)); + SimpleCollisionWeightmapMipData.Add((FColor*)LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx])->GetMipData(Component->SimpleCollisionMipLevel)); } } Component->UpdateCollisionLayerData( @@ -1733,19 +1737,23 @@ void ULandscapeComponent::FillLayer(ULandscapeLayerInfoObject* LayerInfo, FLands const bool bFillLayerIsNoWeightBlend = LayerInfo->bNoWeightBlend; bool bClearOtherWeightBlendLayers = !bFillLayerIsNoWeightBlend; + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(true); + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(true); + TArray& ComponentWeightmapTexturesUsage = Component->GetWeightmapTexturesUsage(true); + // Find the index for this layer in this component. - int32 FillLayerIdx = Component->WeightmapLayerAllocations.IndexOfByPredicate( + int32 FillLayerIdx = ComponentWeightmapLayerAllocations.IndexOfByPredicate( [LayerInfo](const FWeightmapLayerAllocationInfo& Allocation) { return Allocation.LayerInfo == LayerInfo; }); // if the layer isn't used on this component yet but is a weight-blend layer, then simply steal the allocation of another weight-blend layer! if (FillLayerIdx == INDEX_NONE && !bFillLayerIsNoWeightBlend) { - FillLayerIdx = Component->WeightmapLayerAllocations.IndexOfByPredicate( + FillLayerIdx = ComponentWeightmapLayerAllocations.IndexOfByPredicate( [](const FWeightmapLayerAllocationInfo& Allocation) { return !Allocation.LayerInfo || !Allocation.LayerInfo->bNoWeightBlend; }); if (FillLayerIdx != INDEX_NONE) { - Component->WeightmapLayerAllocations[FillLayerIdx].LayerInfo = LayerInfo; + ComponentWeightmapLayerAllocations[FillLayerIdx].LayerInfo = LayerInfo; } else { @@ -1757,9 +1765,14 @@ void ULandscapeComponent::FillLayer(ULandscapeLayerInfoObject* LayerInfo, FLands // if the layer is still not found then we are forced to make a new allocation if (FillLayerIdx == INDEX_NONE) { - FillLayerIdx = Component->WeightmapLayerAllocations.Num(); - Component->WeightmapLayerAllocations.Add(FWeightmapLayerAllocationInfo(LayerInfo)); + FillLayerIdx = ComponentWeightmapLayerAllocations.Num(); + ComponentWeightmapLayerAllocations.Add(FWeightmapLayerAllocationInfo(LayerInfo)); Component->ReallocateWeightmaps(&LandscapeEdit); + + if (GetMutableDefault()->bProceduralLandscape) + { + GetLandscapeActor()->RequestProceduralContentUpdate(EProceduralContentUpdateFlag::Weightmap_Render); + } } check(FillLayerIdx != INDEX_NONE); @@ -1772,8 +1785,8 @@ void ULandscapeComponent::FillLayer(ULandscapeLayerInfoObject* LayerInfo, FLands const int32 WeightmapOffsetX = Component->WeightmapScaleBias.Z * (float)SizeU; const int32 WeightmapOffsetY = Component->WeightmapScaleBias.W * (float)SizeV; - FWeightmapLayerAllocationInfo& FillLayerAllocation = Component->WeightmapLayerAllocations[FillLayerIdx]; - uint8* LayerData = (uint8*)LandscapeEdit.GetTextureDataInfo(Component->WeightmapTextures[FillLayerAllocation.WeightmapTextureIndex])->GetMipData(0); + FWeightmapLayerAllocationInfo& FillLayerAllocation = ComponentWeightmapLayerAllocations[FillLayerIdx]; + uint8* LayerData = (uint8*)LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[FillLayerAllocation.WeightmapTextureIndex])->GetMipData(0); for (int32 Y = 0; Y < SizeV; ++Y) { @@ -1789,19 +1802,19 @@ void ULandscapeComponent::FillLayer(ULandscapeLayerInfoObject* LayerInfo, FLands // clear other layers if (bClearOtherWeightBlendLayers) { - for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); ++LayerIdx) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); ++LayerIdx) { if (LayerIdx == FillLayerIdx) { continue; } - FWeightmapLayerAllocationInfo& Allocation = Component->WeightmapLayerAllocations[LayerIdx]; + FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx]; if (Allocation.LayerInfo->bNoWeightBlend) { continue; } - FLandscapeWeightmapUsage* Usage = Proxy->WeightmapUsageMap.Find(Component->WeightmapTextures[Allocation.WeightmapTextureIndex]); + ULandscapeWeightmapUsage* Usage = ComponentWeightmapTexturesUsage[Allocation.WeightmapTextureIndex]; if (Usage) // can be null if WeightmapUsageMap hasn't been built yet { Usage->ChannelUsage[Allocation.WeightmapTextureChannel] = nullptr; @@ -1810,27 +1823,27 @@ void ULandscapeComponent::FillLayer(ULandscapeLayerInfoObject* LayerInfo, FLands Allocation.WeightmapTextureIndex = 255; } - Component->WeightmapLayerAllocations.RemoveAll( + ComponentWeightmapLayerAllocations.RemoveAll( [](const FWeightmapLayerAllocationInfo& Allocation) { return Allocation.WeightmapTextureIndex == 255; }); // remove any textures we're no longer using - for (int32 TextureIdx = 0; TextureIdx < Component->WeightmapTextures.Num(); ++TextureIdx) + for (int32 TextureIdx = 0; TextureIdx < ComponentWeightmapTextures.Num(); ++TextureIdx) { - if (!Component->WeightmapLayerAllocations.ContainsByPredicate( + if (!ComponentWeightmapLayerAllocations.ContainsByPredicate( [TextureIdx](const FWeightmapLayerAllocationInfo& Allocation) { return Allocation.WeightmapTextureIndex == TextureIdx; })) { - Component->WeightmapTextures[TextureIdx]->Modify(); + ComponentWeightmapTextures[TextureIdx]->Modify(); - for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); ++LayerIdx) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); ++LayerIdx) { - FWeightmapLayerAllocationInfo& Allocation = Component->WeightmapLayerAllocations[LayerIdx]; + FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx]; if (Allocation.WeightmapTextureIndex > TextureIdx) { --Allocation.WeightmapTextureIndex; } } - Component->WeightmapTextures.RemoveAt(TextureIdx--); + ComponentWeightmapTextures.RemoveAt(TextureIdx--); } } } @@ -1840,9 +1853,9 @@ void ULandscapeComponent::FillLayer(ULandscapeLayerInfoObject* LayerInfo, FLands // this can also happen with normal painting I believe // update mips - for (int32 TextureIdx = 0; TextureIdx < Component->WeightmapTextures.Num(); ++TextureIdx) + for (int32 TextureIdx = 0; TextureIdx < ComponentWeightmapTextures.Num(); ++TextureIdx) { - UTexture2D* WeightmapTexture = WeightmapTextures[TextureIdx]; + UTexture2D* WeightmapTexture = ComponentWeightmapTextures[TextureIdx]; FLandscapeTextureDataInfo* WeightmapDataInfo = LandscapeEdit.GetTextureDataInfo(WeightmapTexture); const int32 NumMips = WeightmapTexture->Source.GetNumMips(); @@ -1867,16 +1880,16 @@ void ULandscapeComponent::FillLayer(ULandscapeLayerInfoObject* LayerInfo, FLands // Update dominant layer info stored in collision component TArray CollisionWeightmapMipData; - for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); WeightmapIdx++) + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++) { - CollisionWeightmapMipData.Add((FColor*)LandscapeEdit.GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx])->GetMipData(Component->CollisionMipLevel)); + CollisionWeightmapMipData.Add((FColor*)LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx])->GetMipData(Component->CollisionMipLevel)); } TArray SimpleCollisionWeightmapMipData; if (Component->SimpleCollisionMipLevel > Component->CollisionMipLevel) { - for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); WeightmapIdx++) + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++) { - SimpleCollisionWeightmapMipData.Add((FColor*)LandscapeEdit.GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx])->GetMipData(Component->SimpleCollisionMipLevel)); + SimpleCollisionWeightmapMipData.Add((FColor*)LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx])->GetMipData(Component->SimpleCollisionMipLevel)); } } Component->UpdateCollisionLayerData( @@ -1920,8 +1933,20 @@ void FLandscapeEditDataInterface::FillEmptyLayers(ULandscapeLayerInfoObject* Lay for (auto& XYComponentPair : LandscapeInfo->XYtoComponentMap) { ULandscapeComponent* Component = XYComponentPair.Value; + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(true); - if (Component->WeightmapLayerAllocations.Num() == 0) + bool LayerNeedFilling = true; + + for (const FWeightmapLayerAllocationInfo& Alloc : ComponentWeightmapLayerAllocations) + { + if (Alloc.LayerInfo != nullptr) + { + LayerNeedFilling = false; + break; + } + } + + if (LayerNeedFilling) { Component->FillLayer(LayerInfo, *this); } @@ -1944,11 +1969,15 @@ void ULandscapeComponent::ReplaceLayer(ULandscapeLayerInfoObject* FromLayerInfo, return; } + TArray& ComponentWeightmapLayerAllocations = GetWeightmapLayerAllocations(true); + TArray& ComponentWeightmapTextures = GetWeightmapTextures(true); + TArray& ComponentWeightmapTexturesUsage = GetWeightmapTexturesUsage(true); + // Find the index for this layer in this component. int32 FromLayerIdx = INDEX_NONE; - for (int32 LayerIdx = 0; LayerIdx < WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { - FWeightmapLayerAllocationInfo& Allocation = WeightmapLayerAllocations[LayerIdx]; + FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx]; if (Allocation.LayerInfo == FromLayerInfo) { FromLayerIdx = LayerIdx; @@ -1964,9 +1993,9 @@ void ULandscapeComponent::ReplaceLayer(ULandscapeLayerInfoObject* FromLayerInfo, // Find the index for this layer in this component. int32 ToLayerIdx = INDEX_NONE; - for (int32 LayerIdx = 0; LayerIdx < WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { - FWeightmapLayerAllocationInfo& Allocation = WeightmapLayerAllocations[LayerIdx]; + FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx]; if (Allocation.LayerInfo == ToLayerInfo) { ToLayerIdx = LayerIdx; @@ -1975,20 +2004,20 @@ void ULandscapeComponent::ReplaceLayer(ULandscapeLayerInfoObject* FromLayerInfo, if (ToLayerIdx == INDEX_NONE) { // Layer not used for this component, so do trivial replace. - WeightmapLayerAllocations[FromLayerIdx].LayerInfo = ToLayerInfo; + ComponentWeightmapLayerAllocations[FromLayerIdx].LayerInfo = ToLayerInfo; bMerging = false; } - FWeightmapLayerAllocationInfo& FromLayerAllocation = WeightmapLayerAllocations[FromLayerIdx]; + FWeightmapLayerAllocationInfo& FromLayerAllocation = ComponentWeightmapLayerAllocations[FromLayerIdx]; // See if we'll be able to remove the texture completely. bool bCanRemoveLayerTexture = false; if (bMerging) { bCanRemoveLayerTexture = true; - for (int32 LayerIdx = 0; LayerIdx < WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { - FWeightmapLayerAllocationInfo& Allocation = WeightmapLayerAllocations[LayerIdx]; + FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx]; // check if we will be able to remove the texture also if (LayerIdx != FromLayerIdx && Allocation.WeightmapTextureIndex == FromLayerAllocation.WeightmapTextureIndex) @@ -2007,11 +2036,11 @@ void ULandscapeComponent::ReplaceLayer(ULandscapeLayerInfoObject* FromLayerInfo, if (bMerging) { - FWeightmapLayerAllocationInfo& ToLayerAllocation = WeightmapLayerAllocations[ToLayerIdx]; + FWeightmapLayerAllocationInfo& ToLayerAllocation = ComponentWeightmapLayerAllocations[ToLayerIdx]; // Lock data for all the weightmaps - FLandscapeTextureDataInfo* FromTexDataInfo = LandscapeEdit.GetTextureDataInfo(WeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]); - FLandscapeTextureDataInfo* ToTexDataInfo = LandscapeEdit.GetTextureDataInfo(WeightmapTextures[ToLayerAllocation.WeightmapTextureIndex]); + FLandscapeTextureDataInfo* FromTexDataInfo = LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]); + FLandscapeTextureDataInfo* ToTexDataInfo = LandscapeEdit.GetTextureDataInfo(ComponentWeightmapTextures[ToLayerAllocation.WeightmapTextureIndex]); check(FromTexDataInfo->GetMipSizeX(0) == FromTexDataInfo->GetMipSizeY(0)); check(ToTexDataInfo->GetMipSizeX(0) == ToTexDataInfo->GetMipSizeY(0)); @@ -2029,7 +2058,7 @@ void ULandscapeComponent::ReplaceLayer(ULandscapeLayerInfoObject* FromLayerInfo, // Update all mips if (!bCanRemoveLayerTexture) { - UTexture2D* WeightmapTexture = WeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]; + UTexture2D* WeightmapTexture = ComponentWeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]; FLandscapeTextureDataInfo* WeightmapDataInfo = FromTexDataInfo; const int32 NumMips = WeightmapTexture->Source.GetNumMips(); @@ -2047,7 +2076,7 @@ void ULandscapeComponent::ReplaceLayer(ULandscapeLayerInfoObject* FromLayerInfo, if (FromTexDataInfo != ToTexDataInfo) { - UTexture2D* WeightmapTexture = WeightmapTextures[ToLayerAllocation.WeightmapTextureIndex]; + UTexture2D* WeightmapTexture = ComponentWeightmapTextures[ToLayerAllocation.WeightmapTextureIndex]; FLandscapeTextureDataInfo* WeightmapDataInfo = ToTexDataInfo; const int32 NumMips = WeightmapTexture->Source.GetNumMips(); @@ -2074,7 +2103,7 @@ void ULandscapeComponent::ReplaceLayer(ULandscapeLayerInfoObject* FromLayerInfo, { ALandscapeProxy* Proxy = GetLandscapeProxy(); // Mark the channel as unallocated, so we can reuse it later - FLandscapeWeightmapUsage* Usage = Proxy->WeightmapUsageMap.Find(WeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]); + ULandscapeWeightmapUsage* Usage = ComponentWeightmapTexturesUsage[FromLayerAllocation.WeightmapTextureIndex]; //check(Usage); if (Usage) { @@ -2084,34 +2113,34 @@ void ULandscapeComponent::ReplaceLayer(ULandscapeLayerInfoObject* FromLayerInfo, // If this layer was the last usage for this texture, we can remove it. if (bCanRemoveLayerTexture) { - WeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]->SetFlags(RF_Transactional); - WeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]->Modify(); - WeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]->MarkPackageDirty(); - WeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]->ClearFlags(RF_Standalone); + ComponentWeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]->SetFlags(RF_Transactional); + ComponentWeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]->Modify(); + ComponentWeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]->MarkPackageDirty(); + ComponentWeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]->ClearFlags(RF_Standalone); - WeightmapTextures.RemoveAt(FromLayerAllocation.WeightmapTextureIndex); + ComponentWeightmapTextures.RemoveAt(FromLayerAllocation.WeightmapTextureIndex); // Adjust WeightmapTextureIndex index for other layers - for (int32 LayerIdx = 0; LayerIdx < WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { if (LayerIdx == FromLayerIdx) { continue; } - FWeightmapLayerAllocationInfo& Allocation = WeightmapLayerAllocations[LayerIdx]; + FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx]; if (Allocation.WeightmapTextureIndex > FromLayerAllocation.WeightmapTextureIndex) { Allocation.WeightmapTextureIndex--; } - check(Allocation.WeightmapTextureIndex < WeightmapTextures.Num()); + check(Allocation.WeightmapTextureIndex < ComponentWeightmapTextures.Num()); } } // Remove the layer - WeightmapLayerAllocations.RemoveAt(FromLayerIdx); + ComponentWeightmapLayerAllocations.RemoveAt(FromLayerIdx); // Update the shaders for this component UpdateMaterialInstances(); @@ -2129,18 +2158,20 @@ void FLandscapeEditDataInterface::ReplaceLayer(ULandscapeLayerInfoObject* FromLa ULandscapeComponent* Component = It.Value(); Component->ReplaceLayer(FromLayerInfo, ToLayerInfo, *this); + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(true); + // Update dominant layer info stored in collision component TArray CollisionWeightmapMipData; - for( int32 WeightmapIdx=0;WeightmapIdx < Component->WeightmapTextures.Num();WeightmapIdx++ ) + for( int32 WeightmapIdx=0;WeightmapIdx < ComponentWeightmapTextures.Num();WeightmapIdx++ ) { - CollisionWeightmapMipData.Add((FColor*)GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx])->GetMipData(Component->CollisionMipLevel)); + CollisionWeightmapMipData.Add((FColor*)GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx])->GetMipData(Component->CollisionMipLevel)); } TArray SimpleCollisionWeightmapMipData; if (Component->SimpleCollisionMipLevel > Component->CollisionMipLevel) { - for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); WeightmapIdx++) + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++) { - SimpleCollisionWeightmapMipData.Add((FColor*)GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx])->GetMipData(Component->SimpleCollisionMipLevel)); + SimpleCollisionWeightmapMipData.Add((FColor*)GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx])->GetMipData(Component->SimpleCollisionMipLevel)); } } Component->UpdateCollisionLayerData( @@ -2316,24 +2347,28 @@ bool DeleteLayerIfAllZero(ULandscapeComponent* const Component, const uint8* con Component->Modify(); Proxy->Modify(); + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(true); + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(true); + TArray& ComponentWeightmapTexturesUsage = Component->GetWeightmapTexturesUsage(true); + // Mark the channel as unallocated, so we can reuse it later - const int32 DeleteLayerWeightmapTextureIndex = Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex; - FLandscapeWeightmapUsage& Usage = Proxy->WeightmapUsageMap.FindChecked(Component->WeightmapTextures[DeleteLayerWeightmapTextureIndex]); - Usage.ChannelUsage[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel] = NULL; + const int32 DeleteLayerWeightmapTextureIndex = ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex; + ULandscapeWeightmapUsage& Usage = *ComponentWeightmapTexturesUsage[DeleteLayerWeightmapTextureIndex]; + Usage.ChannelUsage[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel] = NULL; // Remove the layer as it's totally painted away. - Component->WeightmapLayerAllocations.RemoveAt(LayerIdx); + ComponentWeightmapLayerAllocations.RemoveAt(LayerIdx); // Check if the weightmap texture used by the layer we just removed is used by any other layer, and if so, remove the texture too - bool bCanRemoveLayerTexture = !Component->WeightmapLayerAllocations.ContainsByPredicate([DeleteLayerWeightmapTextureIndex](const FWeightmapLayerAllocationInfo& Allocation){ return Allocation.WeightmapTextureIndex == DeleteLayerWeightmapTextureIndex; }); + bool bCanRemoveLayerTexture = !ComponentWeightmapLayerAllocations.ContainsByPredicate([DeleteLayerWeightmapTextureIndex](const FWeightmapLayerAllocationInfo& Allocation){ return Allocation.WeightmapTextureIndex == DeleteLayerWeightmapTextureIndex; }); if (bCanRemoveLayerTexture) { - Component->WeightmapTextures[DeleteLayerWeightmapTextureIndex]->MarkPackageDirty(); - Component->WeightmapTextures[DeleteLayerWeightmapTextureIndex]->ClearFlags(RF_Standalone); - Component->WeightmapTextures.RemoveAt(DeleteLayerWeightmapTextureIndex); + ComponentWeightmapTextures[DeleteLayerWeightmapTextureIndex]->MarkPackageDirty(); + ComponentWeightmapTextures[DeleteLayerWeightmapTextureIndex]->ClearFlags(RF_Standalone); + ComponentWeightmapTextures.RemoveAt(DeleteLayerWeightmapTextureIndex); // Adjust WeightmapTextureChannel index for other layers - for (auto It = Component->WeightmapLayerAllocations.CreateIterator(); It; ++It) + for (auto It = ComponentWeightmapLayerAllocations.CreateIterator(); It; ++It) { FWeightmapLayerAllocationInfo& Allocation = *It; if (Allocation.WeightmapTextureIndex > DeleteLayerWeightmapTextureIndex) @@ -2429,6 +2464,8 @@ inline TMap FLandscapeEditDataInterfac TMap LayerInfluenceMap; ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindChecked(FIntPoint(ComponentIndexX,ComponentIndexY)); + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(true); + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(true); // used if InOptionalLayerDataPtrs is null TArray> InternalTexDataInfos; @@ -2436,21 +2473,21 @@ inline TMap FLandscapeEditDataInterfac TArrayView LayerDataPtrs; if (InOptionalLayerDataPtrs) { - check(InOptionalLayerDataPtrs->Num() == Component->WeightmapLayerAllocations.Num()); + check(InOptionalLayerDataPtrs->Num() == ComponentWeightmapLayerAllocations.Num()); LayerDataPtrs = InOptionalLayerDataPtrs.GetValue(); } else { - InternalTexDataInfos.AddUninitialized(Component->WeightmapTextures.Num()); - for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); ++WeightmapIdx) + InternalTexDataInfos.AddUninitialized(ComponentWeightmapTextures.Num()); + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); ++WeightmapIdx) { - InternalTexDataInfos[WeightmapIdx] = GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx]); + InternalTexDataInfos[WeightmapIdx] = GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx]); } - InternalLayerDataPtrs.AddUninitialized(Component->WeightmapLayerAllocations.Num()); - for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) + InternalLayerDataPtrs.AddUninitialized(ComponentWeightmapLayerAllocations.Num()); + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { - const FWeightmapLayerAllocationInfo& Allocation = Component->WeightmapLayerAllocations[LayerIdx]; + const FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx]; InternalLayerDataPtrs[LayerIdx] = (uint8*)InternalTexDataInfos[Allocation.WeightmapTextureIndex]->GetMipData(0) + ChannelOffsets[Allocation.WeightmapTextureChannel]; } @@ -2459,9 +2496,9 @@ inline TMap FLandscapeEditDataInterfac const int32 ScanlineSize = (SubsectionSizeQuads + 1) * ComponentNumSubsections * 4; - for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { - const FWeightmapLayerAllocationInfo& Allocation = Component->WeightmapLayerAllocations[LayerIdx]; + const FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx]; if (Allocation.LayerInfo->bNoWeightBlend) { continue; @@ -2622,7 +2659,8 @@ void FLandscapeEditDataInterface::SetAlphaData(ULandscapeLayerInfoObject* const Component->Modify(); - int32 UpdateLayerIdx = Component->WeightmapLayerAllocations.IndexOfByPredicate([LayerInfo](const FWeightmapLayerAllocationInfo& Allocation){ return Allocation.LayerInfo == LayerInfo; }); + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(true); + int32 UpdateLayerIdx = ComponentWeightmapLayerAllocations.IndexOfByPredicate([LayerInfo](const FWeightmapLayerAllocationInfo& Allocation){ return Allocation.LayerInfo == LayerInfo; }); // Need allocation for weightmap if (UpdateLayerIdx == INDEX_NONE) @@ -2631,13 +2669,13 @@ void FLandscapeEditDataInterface::SetAlphaData(ULandscapeLayerInfoObject* const // if we can't allocate a layer, then there is nothing to paint if (PaintingRestriction == ELandscapeLayerPaintingRestriction::ExistingOnly || - (PaintingRestriction == ELandscapeLayerPaintingRestriction::UseMaxLayers && LayerLimit > 0 && Component->WeightmapLayerAllocations.Num() >= LayerLimit)) + (PaintingRestriction == ELandscapeLayerPaintingRestriction::UseMaxLayers && LayerLimit > 0 && ComponentWeightmapLayerAllocations.Num() >= LayerLimit)) { continue; } - UpdateLayerIdx = Component->WeightmapLayerAllocations.Num(); - new (Component->WeightmapLayerAllocations) FWeightmapLayerAllocationInfo(LayerInfo); + UpdateLayerIdx = ComponentWeightmapLayerAllocations.Num(); + new (ComponentWeightmapLayerAllocations) FWeightmapLayerAllocationInfo(LayerInfo); Component->ReallocateWeightmaps(this); Component->UpdateMaterialInstances(); @@ -2649,23 +2687,24 @@ void FLandscapeEditDataInterface::SetAlphaData(ULandscapeLayerInfoObject* const // Lock data for all the weightmaps TexDataInfos.Reset(); - TexDataInfos.AddUninitialized(Component->WeightmapTextures.Num()); + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(true); + TexDataInfos.AddUninitialized(ComponentWeightmapTextures.Num()); - for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); ++WeightmapIdx) + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); ++WeightmapIdx) { - TexDataInfos[WeightmapIdx] = GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx]); + TexDataInfos[WeightmapIdx] = GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx]); } LayerDataPtrs.Reset(); // Pointers to all layers' data - LayerDataPtrs.AddUninitialized(Component->WeightmapLayerAllocations.Num()); + LayerDataPtrs.AddUninitialized(ComponentWeightmapLayerAllocations.Num()); LayerNoWeightBlends.Reset(); // NoWeightBlend flags - LayerNoWeightBlends.AddUninitialized(Component->WeightmapLayerAllocations.Num()); + LayerNoWeightBlends.AddUninitialized(ComponentWeightmapLayerAllocations.Num()); LayerEditDataAllZero.Reset(); // Whether the data we are editing for this layer is all zero - LayerEditDataAllZero.AddUninitialized(Component->WeightmapLayerAllocations.Num()); + LayerEditDataAllZero.AddUninitialized(ComponentWeightmapLayerAllocations.Num()); - for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { - FWeightmapLayerAllocationInfo& Allocation = Component->WeightmapLayerAllocations[LayerIdx]; + FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx]; if (Allocation.LayerInfo != nullptr) // only take into account valid layer { @@ -2746,7 +2785,7 @@ void FLandscapeEditDataInterface::SetAlphaData(ULandscapeLayerInfoObject* const int32 MaxWeight = INT_MIN; // Adjust other layers' weights accordingly - for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { uint8& ExistingWeight = LayerDataPtrs[LayerIdx][TexDataIndex]; @@ -2772,7 +2811,7 @@ void FLandscapeEditDataInterface::SetAlphaData(ULandscapeLayerInfoObject* const OtherLayerWeightSum = 0; // Normalize - for (int32 LayerIdx = 0; LayerIdxWeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { uint8& ExistingWeight = LayerDataPtrs[LayerIdx][TexDataIndex]; @@ -2798,7 +2837,7 @@ void FLandscapeEditDataInterface::SetAlphaData(ULandscapeLayerInfoObject* const else { // Adjust other layers' weights accordingly - for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { // Exclude bNoWeightBlend layers if (LayerIdx != UpdateLayerIdx && LayerNoWeightBlends[LayerIdx] == false) @@ -2815,7 +2854,7 @@ void FLandscapeEditDataInterface::SetAlphaData(ULandscapeLayerInfoObject* const const ULandscapeLayerInfoObject* ReplacementLayer = ChooseReplacementLayer(LayerInfo, ComponentIndexX, SubIndexX, SubX, ComponentIndexY, SubIndexY, SubY, LayerInfluenceCache, LayerDataPtrs); if (ReplacementLayer) { - const int32 ReplacementLayerIndex = Component->WeightmapLayerAllocations.IndexOfByPredicate([&](const FWeightmapLayerAllocationInfo& AllocationInfo) { return AllocationInfo.LayerInfo == ReplacementLayer; }); + const int32 ReplacementLayerIndex = ComponentWeightmapLayerAllocations.IndexOfByPredicate([&](const FWeightmapLayerAllocationInfo& AllocationInfo) { return AllocationInfo.LayerInfo == ReplacementLayer; }); LayerDataPtrs[ReplacementLayerIndex][TexDataIndex] = 255 - NewWeight; LayerEditDataAllZero[ReplacementLayerIndex] = false; @@ -2836,7 +2875,7 @@ void FLandscapeEditDataInterface::SetAlphaData(ULandscapeLayerInfoObject* const } else { - for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { uint8& Weight = LayerDataPtrs[LayerIdx][TexDataIndex]; @@ -2879,7 +2918,7 @@ void FLandscapeEditDataInterface::SetAlphaData(ULandscapeLayerInfoObject* const const int32 TexY1 = (SubsectionSizeQuads+1) * SubIndexY + SubY1; const int32 TexX2 = (SubsectionSizeQuads+1) * SubIndexX + SubX2; const int32 TexY2 = (SubsectionSizeQuads+1) * SubIndexY + SubY2; - for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); WeightmapIdx++) + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++) { if (TexDataInfos[WeightmapIdx] != NULL) { @@ -2891,10 +2930,10 @@ void FLandscapeEditDataInterface::SetAlphaData(ULandscapeLayerInfoObject* const // Update mipmaps CollisionWeightmapMipData.Reset(); - CollisionWeightmapMipData.AddUninitialized(Component->WeightmapTextures.Num()); - for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); WeightmapIdx++) + CollisionWeightmapMipData.AddUninitialized(ComponentWeightmapTextures.Num()); + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++) { - UTexture2D* const WeightmapTexture = Component->WeightmapTextures[WeightmapIdx]; + UTexture2D* const WeightmapTexture = ComponentWeightmapTextures[WeightmapIdx]; const int32 NumMips = WeightmapTexture->Source.GetNumMips(); WeightmapTextureMipData.Reset(); @@ -2912,7 +2951,7 @@ void FLandscapeEditDataInterface::SetAlphaData(ULandscapeLayerInfoObject* const if (Component->SimpleCollisionMipLevel > Component->CollisionMipLevel) { - for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); WeightmapIdx++) + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++) { SimpleCollisionWeightmapMipData.Add((FColor*)TexDataInfos[WeightmapIdx]->GetMipData(Component->SimpleCollisionMipLevel)); } @@ -2928,7 +2967,7 @@ void FLandscapeEditDataInterface::SetAlphaData(ULandscapeLayerInfoObject* const // Check if we need to remove weightmap allocations for layers that were completely painted away bool bRemovedLayer = false; - for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { if (LayerEditDataAllZero[LayerIdx]) { @@ -3017,9 +3056,11 @@ void FLandscapeEditDataInterface::SetAlphaData(const TSet& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(true); + for (ULandscapeLayerInfoObject* LayerInfo : DirtyLayerInfos) { - const bool bFound = Component->WeightmapLayerAllocations.ContainsByPredicate([LayerInfo](const FWeightmapLayerAllocationInfo& Allocation){ return Allocation.LayerInfo == LayerInfo; }); + const bool bFound = ComponentWeightmapLayerAllocations.ContainsByPredicate([LayerInfo](const FWeightmapLayerAllocationInfo& Allocation){ return Allocation.LayerInfo == LayerInfo; }); if (!bFound) { NeedAllocationInfos.Add(LayerInfo); @@ -3033,7 +3074,7 @@ void FLandscapeEditDataInterface::SetAlphaData(const TSetWeightmapLayerAllocations.Num() >= LayerLimit)) + ComponentWeightmapLayerAllocations.Num() >= LayerLimit)) { // nothing to paint to this component due to layer limit continue; @@ -3045,11 +3086,11 @@ void FLandscapeEditDataInterface::SetAlphaData(const TSet 0 && Component->WeightmapLayerAllocations.Num() >= LayerLimit) + LayerLimit > 0 && ComponentWeightmapLayerAllocations.Num() >= LayerLimit) { break; } - Component->WeightmapLayerAllocations.Emplace(LayerInfoNeedingAllocation); + ComponentWeightmapLayerAllocations.Emplace(LayerInfoNeedingAllocation); } Component->ReallocateWeightmaps(this); Component->UpdateMaterialInstances(); @@ -3062,22 +3103,23 @@ void FLandscapeEditDataInterface::SetAlphaData(const TSetWeightmapTextures.Num()); + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(true); + TexDataInfos.AddUninitialized(ComponentWeightmapTextures.Num()); - for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); ++WeightmapIdx) + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); ++WeightmapIdx) { - TexDataInfos[WeightmapIdx] = GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx]); + TexDataInfos[WeightmapIdx] = GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx]); } LayerDataInfos.Reset(); // Pointers to all layers' data - LayerDataInfos.AddUninitialized(Component->WeightmapLayerAllocations.Num()); + LayerDataInfos.AddUninitialized(ComponentWeightmapLayerAllocations.Num()); LayerEditDataAllZero.Reset(); // Whether the data we are editing for this layer is all zero - LayerEditDataAllZero.AddUninitialized(Component->WeightmapLayerAllocations.Num()); + LayerEditDataAllZero.AddUninitialized(ComponentWeightmapLayerAllocations.Num()); - for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { - FWeightmapLayerAllocationInfo& Allocation = Component->WeightmapLayerAllocations[LayerIdx]; - const int32 LayerDataIdx = LandscapeInfo->GetLayerInfoIndex(Component->WeightmapLayerAllocations[LayerIdx].LayerInfo); + FWeightmapLayerAllocationInfo& Allocation = ComponentWeightmapLayerAllocations[LayerIdx]; + const int32 LayerDataIdx = LandscapeInfo->GetLayerInfoIndex(ComponentWeightmapLayerAllocations[LayerIdx].LayerInfo); check(LayerDataIdx != INDEX_NONE); LayerDataInfos[LayerIdx].InDataPtr = Data + LayerDataIdx; LayerDataInfos[LayerIdx].TexDataPtr = (uint8*)TexDataInfos[Allocation.WeightmapTextureIndex]->GetMipData(0) + ChannelOffsets[Allocation.WeightmapTextureChannel]; @@ -3131,7 +3173,7 @@ void FLandscapeEditDataInterface::SetAlphaData(const TSetWeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { // this is equivalent to saying if (DirtyLayerInfos.Contains(Allocation.LayerInfo)) // which is what we really mean here, but this is quicker @@ -3155,7 +3197,7 @@ void FLandscapeEditDataInterface::SetAlphaData(const TSetWeightmapTextures.Num(); WeightmapIdx++) + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++) { if (TexDataInfos[WeightmapIdx] != NULL) { @@ -3167,10 +3209,10 @@ void FLandscapeEditDataInterface::SetAlphaData(const TSetWeightmapTextures.Num()); - for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); WeightmapIdx++) + CollisionWeightmapMipData.AddUninitialized(ComponentWeightmapTextures.Num()); + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++) { - UTexture2D* const WeightmapTexture = Component->WeightmapTextures[WeightmapIdx]; + UTexture2D* const WeightmapTexture = ComponentWeightmapTextures[WeightmapIdx]; const int32 NumMips = WeightmapTexture->Source.GetNumMips(); WeightmapTextureMipData.Reset(); @@ -3188,7 +3230,7 @@ void FLandscapeEditDataInterface::SetAlphaData(const TSetSimpleCollisionMipLevel > Component->CollisionMipLevel) { - for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); WeightmapIdx++) + for (int32 WeightmapIdx = 0; WeightmapIdx < ComponentWeightmapTextures.Num(); WeightmapIdx++) { SimpleCollisionWeightmapMipData.Add((FColor*)TexDataInfos[WeightmapIdx]->GetMipData(Component->SimpleCollisionMipLevel)); } @@ -3204,7 +3246,7 @@ void FLandscapeEditDataInterface::SetAlphaData(const TSetWeightmapLayerAllocations.Num(); LayerIdx++) + for (int32 LayerIdx = 0; LayerIdx < ComponentWeightmapLayerAllocations.Num(); LayerIdx++) { if (LayerEditDataAllZero[LayerIdx]) { @@ -3256,16 +3298,19 @@ void FLandscapeEditDataInterface::GetWeightDataTemplFast(ULandscapeLayerInfoObje uint8 WeightmapChannelOffset = 0; TArray TexDataInfos; // added for whole weight case... + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(true); + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(true); + if (LayerInfo != NULL) { - for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) + for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) + if(ComponentWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { - WeightmapTexture = Component->WeightmapTextures[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; + WeightmapTexture = ComponentWeightmapTextures[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; TexDataInfo = GetTextureDataInfo(WeightmapTexture); WeightmapTextureData = (uint8*)TexDataInfo->GetMipData(0); - WeightmapChannelOffset = ChannelOffsets[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; + WeightmapChannelOffset = ChannelOffsets[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } @@ -3273,9 +3318,9 @@ void FLandscapeEditDataInterface::GetWeightDataTemplFast(ULandscapeLayerInfoObje else { // Lock data for all the weightmaps - for( int32 WeightmapIdx=0;WeightmapIdx < Component->WeightmapTextures.Num();WeightmapIdx++ ) + for( int32 WeightmapIdx=0;WeightmapIdx < ComponentWeightmapTextures.Num();WeightmapIdx++ ) { - TexDataInfos.Add(GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx])); + TexDataInfos.Add(GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx])); } } @@ -3335,12 +3380,13 @@ void FLandscapeEditDataInterface::GetWeightDataTemplFast(ULandscapeLayerInfoObje else // Whole weight map case... { StoreData.PreInit(LandscapeInfo->Layers.Num()); - for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) + + for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex; - UTexture2D* ComponentWeightmapTexture = Component->WeightmapTextures[Idx]; + int32 Idx = ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex; + UTexture2D* ComponentWeightmapTexture = ComponentWeightmapTextures[Idx]; uint8* ComponentWeightmapTextureData = (uint8*)TexDataInfos[Idx]->GetMipData(0); - uint8 ComponentWeightmapChannelOffset = ChannelOffsets[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; + uint8 ComponentWeightmapChannelOffset = ChannelOffsets[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; // Find the texture data corresponding to this vertex int32 SizeU = ComponentWeightmapTexture->Source.GetSizeX(); @@ -3355,7 +3401,7 @@ void FLandscapeEditDataInterface::GetWeightDataTemplFast(ULandscapeLayerInfoObje // Find index in LayerInfos { - int32 LayerInfoIdx = LandscapeInfo->GetLayerInfoIndex(Component->WeightmapLayerAllocations[LayerIdx].LayerInfo); + int32 LayerInfoIdx = LandscapeInfo->GetLayerInfoIndex(ComponentWeightmapLayerAllocations[LayerIdx].LayerInfo); if (LayerInfoIdx != INDEX_NONE) { StoreData.Store(LandscapeX, LandscapeY, Weight, LayerInfoIdx); @@ -3378,14 +3424,17 @@ uint8 FLandscapeEditDataInterface::GetWeightMapData(const ULandscapeComponent* C { if (LayerInfo != NULL) { - for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) + const TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(true); + const TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(true); + + for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) + if(ComponentWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { - Texture = Component->WeightmapTextures[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; + Texture = ComponentWeightmapTextures[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(Texture); TextureData = (uint8*)TexDataInfo->GetMipData(0); - Offset = ChannelOffsets[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; + Offset = ChannelOffsets[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } @@ -3463,16 +3512,20 @@ void FLandscapeEditDataInterface::GetWeightDataTempl(ULandscapeLayerInfoObject* if( Component ) { + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(true); + if (LayerInfo != NULL) { - for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(true); + + for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) + if(ComponentWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { - WeightmapTexture = Component->WeightmapTextures[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; + WeightmapTexture = ComponentWeightmapTextures[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; TexDataInfo = GetTextureDataInfo(WeightmapTexture); WeightmapTextureData = (uint8*)TexDataInfo->GetMipData(0); - WeightmapChannelOffset = ChannelOffsets[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; + WeightmapChannelOffset = ChannelOffsets[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } @@ -3480,9 +3533,9 @@ void FLandscapeEditDataInterface::GetWeightDataTempl(ULandscapeLayerInfoObject* else { // Lock data for all the weightmaps - for( int32 WeightmapIdx=0;WeightmapIdx < Component->WeightmapTextures.Num();WeightmapIdx++ ) + for( int32 WeightmapIdx=0;WeightmapIdx < ComponentWeightmapTextures.Num();WeightmapIdx++ ) { - TexDataInfos.Add(GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx])); + TexDataInfos.Add(GetTextureDataInfo(ComponentWeightmapTextures[WeightmapIdx])); } } ComponentDataExist[ComponentIndexXY] = true; @@ -3530,14 +3583,17 @@ void FLandscapeEditDataInterface::GetWeightDataTempl(ULandscapeLayerInfoObject* NoBorderX1 = false; if (LayerInfo != NULL) { - for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) + TArray& BorderWeightmapLayerAllocations = BorderComponent[0]->GetWeightmapLayerAllocations(true); + TArray& BorderWeightmapTextures = BorderComponent[0]->GetWeightmapTextures(true); + + for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) + if(BorderWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { - NeighborWeightmapTexture[0] = BorderComponent[0]->WeightmapTextures[BorderComponent[0]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; + NeighborWeightmapTexture[0] = BorderWeightmapTextures[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; NeighborTexDataInfo[0] = GetTextureDataInfo(NeighborWeightmapTexture[0]); NeighborWeightmapTextureData[0] = (uint8*)NeighborTexDataInfo[0]->GetMipData(0); - NeighborWeightmapChannelOffset[0] = ChannelOffsets[BorderComponent[0]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; + NeighborWeightmapChannelOffset[0] = ChannelOffsets[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } @@ -3558,14 +3614,17 @@ void FLandscapeEditDataInterface::GetWeightDataTempl(ULandscapeLayerInfoObject* NoBorderX2 = false; if (LayerInfo != NULL) { - for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) + TArray& BorderWeightmapLayerAllocations = BorderComponent[1]->GetWeightmapLayerAllocations(true); + TArray& BorderWeightmapTextures = BorderComponent[1]->GetWeightmapTextures(true); + + for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) + if(BorderWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { - NeighborWeightmapTexture[1] = BorderComponent[1]->WeightmapTextures[BorderComponent[1]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; + NeighborWeightmapTexture[1] = BorderWeightmapTextures[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; NeighborTexDataInfo[1] = GetTextureDataInfo(NeighborWeightmapTexture[1]); NeighborWeightmapTextureData[1] = (uint8*)NeighborTexDataInfo[1]->GetMipData(0); - NeighborWeightmapChannelOffset[1] = ChannelOffsets[BorderComponent[1]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; + NeighborWeightmapChannelOffset[1] = ChannelOffsets[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } @@ -3585,14 +3644,17 @@ void FLandscapeEditDataInterface::GetWeightDataTempl(ULandscapeLayerInfoObject* NoBorderY1[ComponentIndexXX] = false; if (LayerInfo != NULL) { - for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) + TArray& BorderWeightmapLayerAllocations = BorderComponent[2]->GetWeightmapLayerAllocations(true); + TArray& BorderWeightmapTextures = BorderComponent[2]->GetWeightmapTextures(true); + + for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) + if(BorderWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { - NeighborWeightmapTexture[2] = BorderComponent[2]->WeightmapTextures[BorderComponent[2]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; + NeighborWeightmapTexture[2] = BorderWeightmapTextures[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; NeighborTexDataInfo[2] = GetTextureDataInfo(NeighborWeightmapTexture[2]); NeighborWeightmapTextureData[2] = (uint8*)NeighborTexDataInfo[2]->GetMipData(0); - NeighborWeightmapChannelOffset[2] = ChannelOffsets[BorderComponent[2]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; + NeighborWeightmapChannelOffset[2] = ChannelOffsets[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } @@ -3607,14 +3669,17 @@ void FLandscapeEditDataInterface::GetWeightDataTempl(ULandscapeLayerInfoObject* { if (LayerInfo != NULL) { - for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) + TArray& BorderWeightmapLayerAllocations = BorderComponent[2]->GetWeightmapLayerAllocations(true); + TArray& BorderWeightmapTextures = BorderComponent[2]->GetWeightmapTextures(true); + + for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) + if(BorderWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { - NeighborWeightmapTexture[2] = BorderComponent[2]->WeightmapTextures[BorderComponent[2]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; + NeighborWeightmapTexture[2] = BorderWeightmapTextures[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; NeighborTexDataInfo[2] = GetTextureDataInfo(NeighborWeightmapTexture[2]); NeighborWeightmapTextureData[2] = (uint8*)NeighborTexDataInfo[2]->GetMipData(0); - NeighborWeightmapChannelOffset[2] = ChannelOffsets[BorderComponent[2]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; + NeighborWeightmapChannelOffset[2] = ChannelOffsets[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } @@ -3633,14 +3698,17 @@ void FLandscapeEditDataInterface::GetWeightDataTempl(ULandscapeLayerInfoObject* NoBorderY2[ComponentIndexXX] = false; if (LayerInfo != NULL) { - for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) + TArray& BorderWeightmapLayerAllocations = BorderComponent[3]->GetWeightmapLayerAllocations(true); + TArray& BorderWeightmapTextures = BorderComponent[3]->GetWeightmapTextures(true); + + for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) + if(BorderWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { - NeighborWeightmapTexture[3] = BorderComponent[3]->WeightmapTextures[BorderComponent[3]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; + NeighborWeightmapTexture[3] = BorderWeightmapTextures[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; NeighborTexDataInfo[3] = GetTextureDataInfo(NeighborWeightmapTexture[3]); NeighborWeightmapTextureData[3] = (uint8*)NeighborTexDataInfo[3]->GetMipData(0); - NeighborWeightmapChannelOffset[3] = ChannelOffsets[BorderComponent[3]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; + NeighborWeightmapChannelOffset[3] = ChannelOffsets[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } @@ -3656,14 +3724,17 @@ void FLandscapeEditDataInterface::GetWeightDataTempl(ULandscapeLayerInfoObject* { if (LayerInfo != NULL) { - for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) + TArray& BorderWeightmapLayerAllocations = BorderComponent[3]->GetWeightmapLayerAllocations(true); + TArray& BorderWeightmapTextures = BorderComponent[3]->GetWeightmapTextures(true); + + for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) + if(BorderWeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { - NeighborWeightmapTexture[3] = BorderComponent[3]->WeightmapTextures[BorderComponent[3]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; + NeighborWeightmapTexture[3] = BorderWeightmapTextures[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; NeighborTexDataInfo[3] = GetTextureDataInfo(NeighborWeightmapTexture[3]); NeighborWeightmapTextureData[3] = (uint8*)NeighborTexDataInfo[3]->GetMipData(0); - NeighborWeightmapChannelOffset[3] = ChannelOffsets[BorderComponent[3]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; + NeighborWeightmapChannelOffset[3] = ChannelOffsets[BorderWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } @@ -3925,12 +3996,16 @@ void FLandscapeEditDataInterface::GetWeightDataTempl(ULandscapeLayerInfoObject* else // Whole weight map case... no interpolation now... { StoreData.PreInit(LandscapeInfo->Layers.Num()); - for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) + + TArray& ComponentWeightmapLayerAllocations = Component->GetWeightmapLayerAllocations(true); + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(true); + + for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex; - UTexture2D* ComponentWeightmapTexture = Component->WeightmapTextures[Idx]; + int32 Idx = ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex; + UTexture2D* ComponentWeightmapTexture = ComponentWeightmapTextures[Idx]; uint8* ComponentWeightmapTextureData = (uint8*)TexDataInfos[Idx]->GetMipData(0); - uint8 ComponentWeightmapChannelOffset = ChannelOffsets[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; + uint8 ComponentWeightmapChannelOffset = ChannelOffsets[ComponentWeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; // Find the texture data corresponding to this vertex int32 SizeU = ComponentWeightmapTexture->Source.GetSizeX(); @@ -3945,7 +4020,7 @@ void FLandscapeEditDataInterface::GetWeightDataTempl(ULandscapeLayerInfoObject* // Find index in LayerInfos { - int32 LayerInfoIdx = LandscapeInfo->GetLayerInfoIndex(Component->WeightmapLayerAllocations[LayerIdx].LayerInfo); + int32 LayerInfoIdx = LandscapeInfo->GetLayerInfoIndex(ComponentWeightmapLayerAllocations[LayerIdx].LayerInfo); if (LayerInfoIdx != INDEX_NONE) { StoreData.Store(LandscapeX, LandscapeY, Weight, LayerInfoIdx); @@ -4446,6 +4521,9 @@ void FLandscapeEditDataInterface::SetXYOffsetDataTempl(int32 X1, int32 Y1, int32 XYOffsetTexture = Component->GetLandscapeProxy()->CreateLandscapeTexture(WeightmapSize, WeightmapSize, TEXTUREGROUP_Terrain_Weightmap, TSF_BGRA8); // Alloc dummy mips ULandscapeComponent::CreateEmptyTextureMips(XYOffsetTexture, true); + + // TODO: we flag somewhere that we need to use XYOffset Factory, so we can recompute shaders based on this change + XYOffsetTexture->PostEditChange(); //FlushRenderingCommands(); @@ -5277,4 +5355,4 @@ FLandscapeTextureDataInfo::~FLandscapeTextureDataInfo() Texture->ClearFlags(RF_Transactional); } -#endif // WITH_EDITOR +#endif // WITH_EDITOR \ No newline at end of file diff --git a/Engine/Source/Runtime/Landscape/Private/LandscapeEditProcedural.cpp b/Engine/Source/Runtime/Landscape/Private/LandscapeEditProcedural.cpp index 6948c7e823e3..6a79ea8dbb7a 100644 --- a/Engine/Source/Runtime/Landscape/Private/LandscapeEditProcedural.cpp +++ b/Engine/Source/Runtime/Landscape/Private/LandscapeEditProcedural.cpp @@ -12,90 +12,28 @@ LandscapeEditProcedural.cpp: Landscape editing procedural mode #include "LandscapeComponent.h" #include "LandscapeLayerInfoObject.h" #include "LandscapeDataAccess.h" -#include "LandscapeRender.h" -#include "LandscapeRenderMobile.h" +#include "LandscapePrivate.h" + +#include "Shader.h" +#include "GlobalShader.h" +#include "ShaderParameterUtils.h" +#include "Engine/TextureRenderTarget2D.h" #if WITH_EDITOR #include "LandscapeEditorModule.h" #include "ComponentRecreateRenderStateContext.h" -#include "Interfaces/ITargetPlatform.h" -#include "Engine/TextureRenderTarget2D.h" -#include "ScopedTransaction.h" -#include "Editor.h" #include "Settings/EditorExperimentalSettings.h" -#endif - -#include "Shader.h" -#include "GlobalShader.h" -#include "RendererInterface.h" -#include "PipelineStateCache.h" -#include "MaterialShaderType.h" -#include "EngineModule.h" -#include "ShaderParameterUtils.h" #include "LandscapeBPCustomBrush.h" +#include "Materials/MaterialInstanceConstant.h" +#include "LandscapeMaterialInstanceConstant.h" +#include "Materials/MaterialExpressionLandscapeVisibilityMask.h" +#include "ShaderCompiler.h" +#include "Algo/Count.h" +#endif #define LOCTEXT_NAMESPACE "Landscape" -void ALandscapeProxy::BeginDestroy() -{ - Super::BeginDestroy(); - -#if WITH_EDITORONLY_DATA - if (GetMutableDefault()->bProceduralLandscape) - { - for (auto& ItPair : RenderDataPerHeightmap) - { - FRenderDataPerHeightmap& HeightmapRenderData = ItPair.Value; - - if (HeightmapRenderData.HeightmapsCPUReadBack != nullptr) - { - BeginReleaseResource(HeightmapRenderData.HeightmapsCPUReadBack); - } - } - - ReleaseResourceFence.BeginFence(); - } -#endif -} - -bool ALandscapeProxy::IsReadyForFinishDestroy() -{ - bool bReadyForFinishDestroy = Super::IsReadyForFinishDestroy(); - -#if WITH_EDITORONLY_DATA - if (GetMutableDefault()->bProceduralLandscape) - { - if (bReadyForFinishDestroy) - { - bReadyForFinishDestroy = ReleaseResourceFence.IsFenceComplete(); - } - } -#endif - - return bReadyForFinishDestroy; -} - -void ALandscapeProxy::FinishDestroy() -{ - Super::FinishDestroy(); - -#if WITH_EDITORONLY_DATA - if (GetMutableDefault()->bProceduralLandscape) - { - check(ReleaseResourceFence.IsFenceComplete()); - - for (auto& ItPair : RenderDataPerHeightmap) - { - FRenderDataPerHeightmap& HeightmapRenderData = ItPair.Value; - - delete HeightmapRenderData.HeightmapsCPUReadBack; - HeightmapRenderData.HeightmapsCPUReadBack = nullptr; - } - } -#endif -} - -#if WITH_EDITOR +ENGINE_API extern bool GDisableAutomaticTextureMaterialUpdateDependencies; static TAutoConsoleVariable CVarOutputProceduralDebugDrawCallName( TEXT("landscape.OutputProceduralDebugDrawCallName"), @@ -107,6 +45,16 @@ static TAutoConsoleVariable CVarOutputProceduralRTContent( 0, TEXT("This will output the content of render target. This is used for debugging only.")); +static TAutoConsoleVariable CVarOutputProceduralWeightmapsRTContent( + TEXT("landscape.OutputProceduralWeightmapsRTContent"), + 0, + TEXT("This will output the content of render target used for weightmap. This is used for debugging only.")); + +DECLARE_GPU_STAT_NAMED(LandscapeProceduralRender, TEXT("Landscape Procedural Render")); +DECLARE_GPU_STAT_NAMED(LandscapeProceduralCopy, TEXT("Landscape Procedural Copy")); + +// Vertex format and vertex buffer + struct FLandscapeProceduralVertex { FVector2D Position; @@ -120,7 +68,6 @@ struct FLandscapeProceduralTriangle FLandscapeProceduralVertex V2; }; -/** The filter vertex declaration resource type. */ class FLandscapeProceduralVertexDeclaration : public FRenderResource { public: @@ -144,7 +91,6 @@ public: } }; - class FLandscapeProceduralVertexBuffer : public FVertexBuffer { public: @@ -176,7 +122,7 @@ private: TArray TriangleList; }; - +// Custom Pixel and Vertex shaders class FLandscapeProceduralVS : public FGlobalShader { @@ -186,7 +132,7 @@ public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { - return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4) && !IsConsolePlatform(Parameters.Platform); + return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && !IsConsolePlatform(Parameters.Platform) && !IsMetalPlatform(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) @@ -227,13 +173,13 @@ struct FLandscapeHeightmapProceduralShaderParameters , ReadHeightmap2(nullptr) , HeightmapSize(0, 0) , ApplyLayerModifiers(false) - , LayerWeight(1.0f) + , LayerAlpha(1.0f) , LayerVisible(true) , OutputAsDelta(false) , GenerateNormals(false) , GridSize(0.0f, 0.0f, 0.0f) - , CurrentMipHeightmapSize(0, 0) - , ParentMipHeightmapSize(0, 0) + , CurrentMipSize(0, 0) + , ParentMipSize(0, 0) , CurrentMipComponentVertexCount(0) {} @@ -241,13 +187,13 @@ struct FLandscapeHeightmapProceduralShaderParameters UTexture* ReadHeightmap2; FIntPoint HeightmapSize; bool ApplyLayerModifiers; - float LayerWeight; + float LayerAlpha; bool LayerVisible; bool OutputAsDelta; bool GenerateNormals; FVector GridSize; - FIntPoint CurrentMipHeightmapSize; - FIntPoint ParentMipHeightmapSize; + FIntPoint CurrentMipSize; + FIntPoint ParentMipSize; int32 CurrentMipComponentVertexCount; }; @@ -258,7 +204,7 @@ public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { - return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4) && !IsConsolePlatform(Parameters.Platform); + return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && !IsConsolePlatform(Parameters.Platform) && !IsMetalPlatform(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) @@ -268,13 +214,14 @@ public: FLandscapeHeightmapProceduralPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { - ReadHeightmapTexture1Param.Bind(Initializer.ParameterMap, TEXT("ReadHeightmapTexture1")); - ReadHeightmapTexture2Param.Bind(Initializer.ParameterMap, TEXT("ReadHeightmapTexture2")); - ReadHeightmapTexture1SamplerParam.Bind(Initializer.ParameterMap, TEXT("ReadHeightmapTexture1Sampler")); - ReadHeightmapTexture2SamplerParam.Bind(Initializer.ParameterMap, TEXT("ReadHeightmapTexture2Sampler")); + ReadTexture1Param.Bind(Initializer.ParameterMap, TEXT("ReadTexture1")); + ReadTexture2Param.Bind(Initializer.ParameterMap, TEXT("ReadTexture2")); + ReadTexture1SamplerParam.Bind(Initializer.ParameterMap, TEXT("ReadTexture1Sampler")); + ReadTexture2SamplerParam.Bind(Initializer.ParameterMap, TEXT("ReadTexture2Sampler")); + LayerInfoParam.Bind(Initializer.ParameterMap, TEXT("LayerInfo")); OutputConfigParam.Bind(Initializer.ParameterMap, TEXT("OutputConfig")); - TextureSizeParam.Bind(Initializer.ParameterMap, TEXT("HeightmapTextureSize")); + TextureSizeParam.Bind(Initializer.ParameterMap, TEXT("TextureSize")); LandscapeGridScaleParam.Bind(Initializer.ParameterMap, TEXT("LandscapeGridScale")); ComponentVertexCountParam.Bind(Initializer.ParameterMap, TEXT("CurrentMipComponentVertexCount")); } @@ -284,14 +231,10 @@ public: void SetParameters(FRHICommandList& RHICmdList, const FLandscapeHeightmapProceduralShaderParameters& InParams) { - SetTextureParameter(RHICmdList, GetPixelShader(), ReadHeightmapTexture1Param, ReadHeightmapTexture1SamplerParam, TStaticSamplerState::GetRHI(), InParams.ReadHeightmap1->Resource->TextureRHI); + SetTextureParameter(RHICmdList, GetPixelShader(), ReadTexture1Param, ReadTexture1SamplerParam, TStaticSamplerState::GetRHI(), InParams.ReadHeightmap1->Resource->TextureRHI); + SetTextureParameter(RHICmdList, GetPixelShader(), ReadTexture2Param, ReadTexture2SamplerParam, TStaticSamplerState::GetRHI(), InParams.ReadHeightmap2 != nullptr ? InParams.ReadHeightmap2->Resource->TextureRHI : GWhiteTexture->TextureRHI); - if (InParams.ReadHeightmap2 != nullptr) - { - SetTextureParameter(RHICmdList, GetPixelShader(), ReadHeightmapTexture2Param, ReadHeightmapTexture2SamplerParam, TStaticSamplerState::GetRHI(), InParams.ReadHeightmap2->Resource->TextureRHI); - } - - FVector2D LayerInfo(InParams.LayerWeight, InParams.LayerVisible ? 1.0f : 0.0f); + FVector2D LayerInfo(InParams.LayerAlpha, InParams.LayerVisible ? 1.0f : 0.0f); FVector4 OutputConfig(InParams.ApplyLayerModifiers ? 1.0f : 0.0f, InParams.OutputAsDelta ? 1.0f : 0.0f, InParams.ReadHeightmap2 ? 1.0f : 0.0f, InParams.GenerateNormals ? 1.0f : 0.0f); FVector2D TextureSize(InParams.HeightmapSize.X, InParams.HeightmapSize.Y); @@ -305,10 +248,10 @@ public: virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FShader::Serialize(Ar); - Ar << ReadHeightmapTexture1Param; - Ar << ReadHeightmapTexture2Param; - Ar << ReadHeightmapTexture1SamplerParam; - Ar << ReadHeightmapTexture2SamplerParam; + Ar << ReadTexture1Param; + Ar << ReadTexture2Param; + Ar << ReadTexture1SamplerParam; + Ar << ReadTexture2SamplerParam; Ar << LayerInfoParam; Ar << OutputConfigParam; Ar << TextureSizeParam; @@ -318,10 +261,10 @@ public: } private: - FShaderResourceParameter ReadHeightmapTexture1Param; - FShaderResourceParameter ReadHeightmapTexture2Param; - FShaderResourceParameter ReadHeightmapTexture1SamplerParam; - FShaderResourceParameter ReadHeightmapTexture2SamplerParam; + FShaderResourceParameter ReadTexture1Param; + FShaderResourceParameter ReadTexture2Param; + FShaderResourceParameter ReadTexture1SamplerParam; + FShaderResourceParameter ReadTexture2SamplerParam; FShaderParameter LayerInfoParam; FShaderParameter OutputConfigParam; FShaderParameter TextureSizeParam; @@ -329,7 +272,7 @@ private: FShaderParameter ComponentVertexCountParam; }; -IMPLEMENT_GLOBAL_SHADER(FLandscapeHeightmapProceduralPS, "/Engine/Private/LandscapeProceduralPS.usf", "PSMain", SF_Pixel); +IMPLEMENT_GLOBAL_SHADER(FLandscapeHeightmapProceduralPS, "/Engine/Private/LandscapeProceduralPS.usf", "PSHeightmapMain", SF_Pixel); class FLandscapeHeightmapMipsProceduralPS : public FGlobalShader { @@ -338,7 +281,7 @@ public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { - return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4) && !IsConsolePlatform(Parameters.Platform); + return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && !IsConsolePlatform(Parameters.Platform) && !IsMetalPlatform(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) @@ -348,10 +291,10 @@ public: FLandscapeHeightmapMipsProceduralPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { - ReadHeightmapTexture1Param.Bind(Initializer.ParameterMap, TEXT("ReadHeightmapTexture1")); - ReadHeightmapTexture1SamplerParam.Bind(Initializer.ParameterMap, TEXT("ReadHeightmapTexture1Sampler")); - CurrentMipHeightmapSizeParam.Bind(Initializer.ParameterMap, TEXT("CurrentMipTextureSize")); - ParentMipHeightmapSizeParam.Bind(Initializer.ParameterMap, TEXT("ParentMipTextureSize")); + ReadTexture1Param.Bind(Initializer.ParameterMap, TEXT("ReadTexture1")); + ReadTexture1SamplerParam.Bind(Initializer.ParameterMap, TEXT("ReadTexture1Sampler")); + CurrentMipSizeParam.Bind(Initializer.ParameterMap, TEXT("CurrentMipTextureSize")); + ParentMipSizeParam.Bind(Initializer.ParameterMap, TEXT("ParentMipTextureSize")); CurrentMipComponentVertexCountParam.Bind(Initializer.ParameterMap, TEXT("CurrentMipComponentVertexCount")); } @@ -360,129 +303,803 @@ public: void SetParameters(FRHICommandList& RHICmdList, const FLandscapeHeightmapProceduralShaderParameters& InParams) { - SetTextureParameter(RHICmdList, GetPixelShader(), ReadHeightmapTexture1Param, ReadHeightmapTexture1SamplerParam, TStaticSamplerState::GetRHI(), InParams.ReadHeightmap1->Resource->TextureRHI); + SetTextureParameter(RHICmdList, GetPixelShader(), ReadTexture1Param, ReadTexture1SamplerParam, TStaticSamplerState::GetRHI(), InParams.ReadHeightmap1->Resource->TextureRHI); - SetShaderValue(RHICmdList, GetPixelShader(), CurrentMipHeightmapSizeParam, FVector2D(InParams.CurrentMipHeightmapSize.X, InParams.CurrentMipHeightmapSize.Y)); - SetShaderValue(RHICmdList, GetPixelShader(), ParentMipHeightmapSizeParam, FVector2D(InParams.ParentMipHeightmapSize.X, InParams.ParentMipHeightmapSize.Y)); + SetShaderValue(RHICmdList, GetPixelShader(), CurrentMipSizeParam, FVector2D(InParams.CurrentMipSize.X, InParams.CurrentMipSize.Y)); + SetShaderValue(RHICmdList, GetPixelShader(), ParentMipSizeParam, FVector2D(InParams.ParentMipSize.X, InParams.ParentMipSize.Y)); SetShaderValue(RHICmdList, GetPixelShader(), CurrentMipComponentVertexCountParam, (float)InParams.CurrentMipComponentVertexCount); } virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FShader::Serialize(Ar); - Ar << ReadHeightmapTexture1Param; - Ar << ReadHeightmapTexture1SamplerParam; - Ar << CurrentMipHeightmapSizeParam; - Ar << ParentMipHeightmapSizeParam; + Ar << ReadTexture1Param; + Ar << ReadTexture1SamplerParam; + Ar << CurrentMipSizeParam; + Ar << ParentMipSizeParam; Ar << CurrentMipComponentVertexCountParam; return bShaderHasOutdatedParameters; } private: - FShaderResourceParameter ReadHeightmapTexture1Param; - FShaderResourceParameter ReadHeightmapTexture1SamplerParam; - FShaderParameter CurrentMipHeightmapSizeParam; - FShaderParameter ParentMipHeightmapSizeParam; + FShaderResourceParameter ReadTexture1Param; + FShaderResourceParameter ReadTexture1SamplerParam; + FShaderParameter CurrentMipSizeParam; + FShaderParameter ParentMipSizeParam; FShaderParameter CurrentMipComponentVertexCountParam; }; -IMPLEMENT_GLOBAL_SHADER(FLandscapeHeightmapMipsProceduralPS, "/Engine/Private/LandscapeProceduralPS.usf", "PSMainMips", SF_Pixel); +IMPLEMENT_GLOBAL_SHADER(FLandscapeHeightmapMipsProceduralPS, "/Engine/Private/LandscapeProceduralPS.usf", "PSHeightmapMainMips", SF_Pixel); -/** The filter vertex declaration resource type. */ - - -DECLARE_GPU_STAT_NAMED(LandscapeProceduralRender, TEXT("Landscape Procedural Render")); - -class FLandscapeProceduralCopyResource_RenderThread +struct FLandscapeWeightmapProceduralShaderParameters { -public: - FLandscapeProceduralCopyResource_RenderThread(UTexture* InHeightmapRTRead, UTexture* InCopyResolveTarget, FTextureResource* InCopyResolveTargetCPUResource, FIntPoint InComponentSectionBase, int32 InSubSectionSizeQuad, int32 InNumSubSections, int32 InCurrentMip) - : SourceResource(InHeightmapRTRead != nullptr ? InHeightmapRTRead->Resource : nullptr) - , CopyResolveTargetResource(InCopyResolveTarget != nullptr ? InCopyResolveTarget->Resource : nullptr) - , CopyResolveTargetCPUResource(InCopyResolveTargetCPUResource) - , CurrentMip(InCurrentMip) - , ComponentSectionBase(InComponentSectionBase) - , SubSectionSizeQuad(InSubSectionSizeQuad) - , NumSubSections(InNumSubSections) - , SourceDebugName(SourceResource != nullptr ? InHeightmapRTRead->GetName() : TEXT("")) - , CopyResolveDebugName(InCopyResolveTarget != nullptr ? InCopyResolveTarget->GetName() : TEXT("")) + FLandscapeWeightmapProceduralShaderParameters() + : ReadWeightmap1(nullptr) + , ReadWeightmap2(nullptr) + , ApplyLayerModifiers(false) + , LayerAlpha(1.0f) + , LayerVisible(true) + , OutputAsDelta(false) + , OutputAsSubstractive(false) + , OutputAsNormalized(false) + , CurrentMipSize(0, 0) + , ParentMipSize(0, 0) + , CurrentMipComponentVertexCount(0) {} - void CopyToResolveTarget(FRHICommandListImmediate& InRHICmdList) + UTexture* ReadWeightmap1; + UTexture* ReadWeightmap2; + bool ApplyLayerModifiers; + float LayerAlpha; + bool LayerVisible; + bool OutputAsDelta; + bool OutputAsSubstractive; + bool OutputAsNormalized; + FIntPoint CurrentMipSize; + FIntPoint ParentMipSize; + int32 CurrentMipComponentVertexCount; +}; + +class FLandscapeWeightmapProceduralPS : public FGlobalShader +{ + DECLARE_GLOBAL_SHADER(FLandscapeWeightmapProceduralPS); +public: + + static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { - if (SourceResource == nullptr || CopyResolveTargetResource == nullptr) + return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && !IsConsolePlatform(Parameters.Platform) && !IsMetalPlatform(Parameters.Platform); + } + + static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) + { + } + + FLandscapeWeightmapProceduralPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) + : FGlobalShader(Initializer) + { + ReadTexture1Param.Bind(Initializer.ParameterMap, TEXT("ReadTexture1")); + ReadTexture2Param.Bind(Initializer.ParameterMap, TEXT("ReadTexture2")); + ReadTexture1SamplerParam.Bind(Initializer.ParameterMap, TEXT("ReadTexture1Sampler")); + ReadTexture2SamplerParam.Bind(Initializer.ParameterMap, TEXT("ReadTexture2Sampler")); + LayerInfoParam.Bind(Initializer.ParameterMap, TEXT("LayerInfo")); + OutputConfigParam.Bind(Initializer.ParameterMap, TEXT("OutputConfig")); + ComponentVertexCountParam.Bind(Initializer.ParameterMap, TEXT("CurrentMipComponentVertexCount")); + } + + FLandscapeWeightmapProceduralPS() + {} + + void SetParameters(FRHICommandList& RHICmdList, const FLandscapeWeightmapProceduralShaderParameters& InParams) + { + SetTextureParameter(RHICmdList, GetPixelShader(), ReadTexture1Param, ReadTexture1SamplerParam, TStaticSamplerState::GetRHI(), InParams.ReadWeightmap1->Resource->TextureRHI); + SetTextureParameter(RHICmdList, GetPixelShader(), ReadTexture2Param, ReadTexture2SamplerParam, TStaticSamplerState::GetRHI(), InParams.ReadWeightmap2 != nullptr ? InParams.ReadWeightmap2->Resource->TextureRHI : GWhiteTexture->TextureRHI); + + FVector2D LayerInfo(InParams.LayerAlpha, InParams.LayerVisible ? 1.0f : 0.0f); + FVector4 OutputConfig(InParams.ApplyLayerModifiers ? 1.0f : 0.0f, InParams.OutputAsSubstractive ? 1.0f : 0.0f, InParams.ReadWeightmap2 != nullptr ? 1.0f : 0.0f, InParams.OutputAsNormalized ? 1.0f : 0.0f); + + SetShaderValue(RHICmdList, GetPixelShader(), LayerInfoParam, LayerInfo); + SetShaderValue(RHICmdList, GetPixelShader(), OutputConfigParam, OutputConfig); + SetShaderValue(RHICmdList, GetPixelShader(), ComponentVertexCountParam, (float)InParams.CurrentMipComponentVertexCount); + } + + virtual bool Serialize(FArchive& Ar) override + { + bool bShaderHasOutdatedParameters = FShader::Serialize(Ar); + Ar << ReadTexture1Param; + Ar << ReadTexture2Param; + Ar << ReadTexture1SamplerParam; + Ar << ReadTexture2SamplerParam; + Ar << LayerInfoParam; + Ar << OutputConfigParam; + Ar << ComponentVertexCountParam; + return bShaderHasOutdatedParameters; + } + +private: + FShaderResourceParameter ReadTexture1Param; + FShaderResourceParameter ReadTexture2Param; + FShaderResourceParameter ReadTexture1SamplerParam; + FShaderResourceParameter ReadTexture2SamplerParam; + FShaderParameter LayerInfoParam; + FShaderParameter OutputConfigParam; + FShaderParameter ComponentVertexCountParam; +}; + +IMPLEMENT_GLOBAL_SHADER(FLandscapeWeightmapProceduralPS, "/Engine/Private/LandscapeProceduralPS.usf", "PSWeightmapMain", SF_Pixel); + +class FLandscapeWeightmapMipsProceduralPS : public FGlobalShader +{ + DECLARE_GLOBAL_SHADER(FLandscapeWeightmapMipsProceduralPS); +public: + + static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) + { + return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && !IsConsolePlatform(Parameters.Platform) && !IsMetalPlatform(Parameters.Platform); + } + + static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) + { + } + + FLandscapeWeightmapMipsProceduralPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) + : FGlobalShader(Initializer) + { + ReadTexture1Param.Bind(Initializer.ParameterMap, TEXT("ReadTexture1")); + ReadTexture1SamplerParam.Bind(Initializer.ParameterMap, TEXT("ReadTexture1Sampler")); + CurrentMipSizeParam.Bind(Initializer.ParameterMap, TEXT("CurrentMipTextureSize")); + ParentMipSizeParam.Bind(Initializer.ParameterMap, TEXT("ParentMipTextureSize")); + CurrentMipComponentVertexCountParam.Bind(Initializer.ParameterMap, TEXT("CurrentMipComponentVertexCount")); + } + + FLandscapeWeightmapMipsProceduralPS() + {} + + void SetParameters(FRHICommandList& RHICmdList, const FLandscapeWeightmapProceduralShaderParameters& InParams) + { + SetTextureParameter(RHICmdList, GetPixelShader(), ReadTexture1Param, ReadTexture1SamplerParam, TStaticSamplerState::GetRHI(), InParams.ReadWeightmap1->Resource->TextureRHI); + + SetShaderValue(RHICmdList, GetPixelShader(), CurrentMipSizeParam, FVector2D(InParams.CurrentMipSize.X, InParams.CurrentMipSize.Y)); + SetShaderValue(RHICmdList, GetPixelShader(), ParentMipSizeParam, FVector2D(InParams.ParentMipSize.X, InParams.ParentMipSize.Y)); + SetShaderValue(RHICmdList, GetPixelShader(), CurrentMipComponentVertexCountParam, (float)InParams.CurrentMipComponentVertexCount); + } + + virtual bool Serialize(FArchive& Ar) override + { + bool bShaderHasOutdatedParameters = FShader::Serialize(Ar); + Ar << ReadTexture1Param; + Ar << ReadTexture1SamplerParam; + Ar << CurrentMipSizeParam; + Ar << ParentMipSizeParam; + Ar << CurrentMipComponentVertexCountParam; + + return bShaderHasOutdatedParameters; + } + +private: + FShaderResourceParameter ReadTexture1Param; + FShaderResourceParameter ReadTexture1SamplerParam; + FShaderParameter CurrentMipSizeParam; + FShaderParameter ParentMipSizeParam; + FShaderParameter CurrentMipComponentVertexCountParam; +}; + +IMPLEMENT_GLOBAL_SHADER(FLandscapeWeightmapMipsProceduralPS, "/Engine/Private/LandscapeProceduralPS.usf", "PSWeightmapMainMips", SF_Pixel); + +// Custom Resources + +class FLandscapeTexture2DResource : public FTextureResource +{ +public: + FLandscapeTexture2DResource(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, uint32 InNumMips, bool InNeedUAV) + : SizeX(InSizeX) + , SizeY(InSizeY) + , Format(InFormat) + , NumMips(InNumMips) + , CreateUAV(InNeedUAV) + {} + + virtual uint32 GetSizeX() const override + { + return SizeX; + } + + virtual uint32 GetSizeY() const override + { + return SizeY; + } + + /** Called when the resource is initialized. This is only called by the rendering thread. */ + virtual void InitRHI() override + { + FTextureResource::InitRHI(); + + FRHIResourceCreateInfo CreateInfo; + uint32 Flags = TexCreate_NoTiling | TexCreate_OfflineProcessed; + + if (CreateUAV) { - return; + Flags |= TexCreate_UAV; } - SCOPE_CYCLE_COUNTER(STAT_LandscapeRegenerateProceduralHeightmaps_RenderThread); - SCOPED_DRAW_EVENTF(InRHICmdList, LandscapeProceduralCopy, TEXT("LS Copy %s -> %s, Mip: %d"), *SourceDebugName, *CopyResolveDebugName, CurrentMip); - SCOPED_GPU_STAT(InRHICmdList, LandscapeProceduralRender); + TextureRHI = RHICreateTexture2D(SizeX, SizeY, Format, NumMips, 1, Flags, CreateInfo); - FIntPoint SourceReadTextureSize(SourceResource->GetSizeX(), SourceResource->GetSizeY()); - FIntPoint CopyResolveWriteTextureSize(CopyResolveTargetResource->GetSizeX() >> CurrentMip, CopyResolveTargetResource->GetSizeY() >> CurrentMip); + if (CreateUAV) + { + TextureUAV = RHICreateUnorderedAccessView(TextureRHI, 0); + } + } + + FUnorderedAccessViewRHIRef TextureUAV; + +private: + uint32 SizeX; + uint32 SizeY; + EPixelFormat Format; + uint32 NumMips; + bool CreateUAV; +}; + +class FLandscapeTexture2DArrayResource : public FTextureResource +{ +public: + FLandscapeTexture2DArrayResource(uint32 InSizeX, uint32 InSizeY, uint32 InSizeZ, EPixelFormat InFormat, uint32 InNumMips, bool InNeedUAV) + : SizeX(InSizeX) + , SizeY(InSizeY) + , SizeZ(InSizeZ) + , Format(InFormat) + , NumMips(InNumMips) + , CreateUAV(InNeedUAV) + {} + + virtual uint32 GetSizeX() const override + { + return SizeX; + } + + virtual uint32 GetSizeY() const override + { + return SizeY; + } + + virtual uint32 GetSizeZ() const + { + return SizeZ; + } + + /** Called when the resource is initialized. This is only called by the rendering thread. */ + virtual void InitRHI() override + { + FTextureResource::InitRHI(); + + FRHIResourceCreateInfo CreateInfo; + uint32 Flags = TexCreate_NoTiling | TexCreate_OfflineProcessed; + + if (CreateUAV) + { + Flags |= TexCreate_UAV; + } + + TextureRHI = RHICreateTexture2DArray(SizeX, SizeY, SizeZ, Format, NumMips, Flags, CreateInfo); + + if (CreateUAV) + { + TextureUAV = RHICreateUnorderedAccessView(TextureRHI, 0); + } + } + + virtual void ReleaseRHI() override + { + FTextureResource::ReleaseRHI(); + + TextureUAV.SafeRelease(); + } + + FUnorderedAccessViewRHIRef TextureUAV; + +private: + uint32 SizeX; + uint32 SizeY; + uint32 SizeZ; + EPixelFormat Format; + uint32 NumMips; + bool CreateUAV; +}; + +// Compute shaders data + +int32 GLandscapeWeightmapThreadGroupSizeX = 16; +int32 GLandscapeWeightmapThreadGroupSizeY = 16; + +struct FLandscapeProceduralWeightmapExtractLayersComponentData +{ + FIntPoint ComponentVertexPosition; // Section base converted to vertex instead of quad + uint32 DestinationPaintLayerIndex; // correspond to which layer info object index the data should be stored in the texture 2d array + uint32 WeightmapChannelToProcess; // correspond to which RGBA channel to process + FIntPoint AtlasTexturePositionOutput; // This represent the location we will write layer information +}; + +class FLandscapeProceduralWeightmapExtractLayersComputeShaderResource : public FRenderResource +{ +public: + FLandscapeProceduralWeightmapExtractLayersComputeShaderResource(const TArray& InComponentsData) + : OriginalComponentsData(InComponentsData) + , ComponentsDataCount(OriginalComponentsData.Num()) + {} + + ~FLandscapeProceduralWeightmapExtractLayersComputeShaderResource() + { + ComponentsData.SafeRelease(); + ComponentsDataSRV.SafeRelease(); + } + + /** Called when the resource is initialized. This is only called by the rendering thread. */ + virtual void InitDynamicRHI() override + { + FRHIResourceCreateInfo CreateInfo; + ComponentsData = RHICreateStructuredBuffer(sizeof(FLandscapeProceduralWeightmapExtractLayersComponentData), OriginalComponentsData.Num() * sizeof(FLandscapeProceduralWeightmapExtractLayersComponentData), BUF_ShaderResource | BUF_Volatile, CreateInfo); + ComponentsDataSRV = RHICreateShaderResourceView(ComponentsData); + + uint8* Buffer = (uint8*)RHILockStructuredBuffer(ComponentsData, 0, OriginalComponentsData.Num() * sizeof(FLandscapeProceduralWeightmapExtractLayersComponentData), RLM_WriteOnly); + FMemory::Memcpy(Buffer, OriginalComponentsData.GetData(), OriginalComponentsData.Num() * sizeof(FLandscapeProceduralWeightmapExtractLayersComponentData)); + RHIUnlockStructuredBuffer(ComponentsData); + } + + virtual void ReleaseDynamicRHI() override + { + ComponentsData.SafeRelease(); + ComponentsDataSRV.SafeRelease(); + } + + int32 GetComponentsDataCount() const + { + return ComponentsDataCount; + } + +private: + friend class FLandscapeProceduralWeightmapExtractLayersCS; + + FStructuredBufferRHIRef ComponentsData; + FShaderResourceViewRHIRef ComponentsDataSRV; + TArray OriginalComponentsData; + int32 ComponentsDataCount; +}; + +struct FLandscapeWeightmapProceduralWeightmapExtractLayersComputeShaderParameters +{ + FLandscapeWeightmapProceduralWeightmapExtractLayersComputeShaderParameters() + : ComponentWeightmapResource(nullptr) + , ComputeShaderResource(nullptr) + , AtlasWeightmapsPerLayer(nullptr) + , ComponentSize(0) + {} + + FLandscapeTexture2DResource* ComponentWeightmapResource; + FLandscapeProceduralWeightmapExtractLayersComputeShaderResource* ComputeShaderResource; + FLandscapeTexture2DArrayResource* AtlasWeightmapsPerLayer; + uint32 ComponentSize; +}; + +class FLandscapeProceduralWeightmapExtractLayersCS : public FGlobalShader +{ + DECLARE_GLOBAL_SHADER(FLandscapeProceduralWeightmapExtractLayersCS); +public: + + static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) + { + return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && !IsConsolePlatform(Parameters.Platform) && !IsMetalPlatform(Parameters.Platform); + } + + static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) + { + OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), GLandscapeWeightmapThreadGroupSizeX); + OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), GLandscapeWeightmapThreadGroupSizeY); + } + + FLandscapeProceduralWeightmapExtractLayersCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) + : FGlobalShader(Initializer) + { + ComponentWeightmapParam.Bind(Initializer.ParameterMap, TEXT("InComponentWeightMaps")); + AtlasPaintListsParam.Bind(Initializer.ParameterMap, TEXT("OutAtlasPaintLayers")); + ComponentsDataParam.Bind(Initializer.ParameterMap, TEXT("InExtractLayersComponentsData")); + ComponentSizeParam.Bind(Initializer.ParameterMap, TEXT("ComponentSize")); + } + + FLandscapeProceduralWeightmapExtractLayersCS() + {} + + void SetParameters(FRHICommandList& RHICmdList, const FLandscapeWeightmapProceduralWeightmapExtractLayersComputeShaderParameters& InParams) + { + SetTextureParameter(RHICmdList, GetComputeShader(), ComponentWeightmapParam, InParams.ComponentWeightmapResource->TextureRHI); + SetUAVParameter(RHICmdList, GetComputeShader(), AtlasPaintListsParam, InParams.AtlasWeightmapsPerLayer->TextureUAV); + SetSRVParameter(RHICmdList, GetComputeShader(), ComponentsDataParam, InParams.ComputeShaderResource->ComponentsDataSRV); + SetShaderValue(RHICmdList, GetComputeShader(), ComponentSizeParam, InParams.ComponentSize); + } + + void UnsetParameters(FRHICommandList& RHICmdList) + { + SetUAVParameter(RHICmdList, GetComputeShader(), AtlasPaintListsParam, FUnorderedAccessViewRHIParamRef()); + } + + virtual bool Serialize(FArchive& Ar) override + { + bool bShaderHasOutdatedParameters = FShader::Serialize(Ar); + Ar << ComponentWeightmapParam; + Ar << AtlasPaintListsParam; + Ar << ComponentsDataParam; + Ar << ComponentSizeParam; + + return bShaderHasOutdatedParameters; + } + +private: + FShaderResourceParameter ComponentWeightmapParam; + FShaderResourceParameter AtlasPaintListsParam; + FShaderResourceParameter ComponentsDataParam; + FShaderParameter ComponentSizeParam; +}; + +IMPLEMENT_GLOBAL_SHADER(FLandscapeProceduralWeightmapExtractLayersCS, "/Engine/Private/LandscapeProceduralCS.usf", "ComputeWeightmapPerPaintLayer", SF_Compute); + +class FLandscapeProceduralWeightmapExtractLayersCSDispatch_RenderThread +{ +public: + FLandscapeProceduralWeightmapExtractLayersCSDispatch_RenderThread(const FLandscapeWeightmapProceduralWeightmapExtractLayersComputeShaderParameters& InShaderParams) + : ShaderParams(InShaderParams) + {} + + void ExtractLayers(FRHICommandListImmediate& InRHICmdList) + { + SCOPE_CYCLE_COUNTER(STAT_LandscapeRegenerateProcedural_RenderThread); + SCOPED_DRAW_EVENTF(InRHICmdList, LandscapeProceduralRender, TEXT("ExtractLayers")); + + TShaderMapRef ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel)); + InRHICmdList.SetComputeShader(ComputeShader->GetComputeShader()); + ComputeShader->SetParameters(InRHICmdList, ShaderParams); + + uint32 ThreadGroupCountX = FMath::CeilToInt((float)ShaderParams.ComponentSize / (float)GLandscapeWeightmapThreadGroupSizeX); + uint32 ThreadGroupCountY = FMath::CeilToInt((float)ShaderParams.ComponentSize / (float)GLandscapeWeightmapThreadGroupSizeY); + check(ThreadGroupCountX > 0 && ThreadGroupCountY > 0); + + DispatchComputeShader(InRHICmdList, *ComputeShader, ThreadGroupCountX, ThreadGroupCountY, ShaderParams.ComputeShaderResource->GetComponentsDataCount()); + ComputeShader->UnsetParameters(InRHICmdList); + ShaderParams.ComputeShaderResource->ReleaseResource(); + delete ShaderParams.ComputeShaderResource; + } + +private: + FLandscapeWeightmapProceduralWeightmapExtractLayersComputeShaderParameters ShaderParams; +}; + +struct FLandscapeProceduralWeightmapPackLayersComponentData +{ + int32 ComponentVertexPositionX[4]; // Section base converted to vertex instead of quad + int32 ComponentVertexPositionY[4]; // Section base converted to vertex instead of quad + int32 SourcePaintLayerIndex[4]; // correspond to which layer info object index the data should be loaded from the texture 2d array + int32 WeightmapChannelToProcess[4]; // correspond to which RGBA channel to process +}; + +class FLandscapeProceduralWeightmapPackLayersComputeShaderResource : public FRenderResource +{ +public: + FLandscapeProceduralWeightmapPackLayersComputeShaderResource(const TArray& InComponentsData, const TArray& InWeightmapWeightBlendModeData, const TArray& InTextureOutputOffset) + : OriginalComponentsData(InComponentsData) + , ComponentsDataCount(OriginalComponentsData.Num()) + , OriginalWeightmapWeightBlendModeData(InWeightmapWeightBlendModeData) + , OriginalTextureOutputOffset(InTextureOutputOffset) + {} + + ~FLandscapeProceduralWeightmapPackLayersComputeShaderResource() + { + ComponentsData.SafeRelease(); + ComponentsDataSRV.SafeRelease(); + WeightmapWeightBlendModeSRV.SafeRelease(); + WeightmapTextureOutputOffsetSRV.SafeRelease(); + } + + /** Called when the resource is initialized. This is only called by the rendering thread. */ + virtual void InitDynamicRHI() override + { + FRHIResourceCreateInfo CreateInfo; + uint32 ComponentsDataMemSize = OriginalComponentsData.Num() * sizeof(FLandscapeProceduralWeightmapPackLayersComponentData); + ComponentsData = RHICreateStructuredBuffer(sizeof(FLandscapeProceduralWeightmapPackLayersComponentData), ComponentsDataMemSize, BUF_ShaderResource | BUF_Volatile, CreateInfo); + ComponentsDataSRV = RHICreateShaderResourceView(ComponentsData); + + uint8* Buffer = (uint8*)RHILockStructuredBuffer(ComponentsData, 0, ComponentsDataMemSize, RLM_WriteOnly); + FMemory::Memcpy(Buffer, OriginalComponentsData.GetData(), ComponentsDataMemSize); + RHIUnlockStructuredBuffer(ComponentsData); + + FRHIResourceCreateInfo WeightBlendCreateInfo; + uint32 WeightBlendMemSize = OriginalWeightmapWeightBlendModeData.Num() * sizeof(float); + WeightmapWeightBlendMode = RHICreateVertexBuffer(WeightBlendMemSize, BUF_ShaderResource | BUF_Volatile | BUF_Dynamic, WeightBlendCreateInfo); + WeightmapWeightBlendModeSRV = RHICreateShaderResourceView(WeightmapWeightBlendMode, sizeof(float), PF_R32_FLOAT); + + void* WeightmapWeightBlendModePtr = RHILockVertexBuffer(WeightmapWeightBlendMode, 0, WeightBlendMemSize, RLM_WriteOnly); + FMemory::Memcpy(WeightmapWeightBlendModePtr, OriginalWeightmapWeightBlendModeData.GetData(), WeightBlendMemSize); + RHIUnlockVertexBuffer(WeightmapWeightBlendMode); + + FRHIResourceCreateInfo TextureOutputOffsetCreateInfo; + uint32 TextureOutputOffsetMemSize = OriginalTextureOutputOffset.Num() * sizeof(FVector2D); + WeightmapTextureOutputOffset = RHICreateVertexBuffer(TextureOutputOffsetMemSize, BUF_ShaderResource | BUF_Volatile | BUF_Dynamic, TextureOutputOffsetCreateInfo); + WeightmapTextureOutputOffsetSRV = RHICreateShaderResourceView(WeightmapTextureOutputOffset, sizeof(FVector2D), PF_G32R32F); + + void* TextureOutputOffsetPtr = RHILockVertexBuffer(WeightmapTextureOutputOffset, 0, TextureOutputOffsetMemSize, RLM_WriteOnly); + FMemory::Memcpy(TextureOutputOffsetPtr, OriginalTextureOutputOffset.GetData(), TextureOutputOffsetMemSize); + RHIUnlockVertexBuffer(WeightmapTextureOutputOffset); + } + + virtual void ReleaseDynamicRHI() override + { + ComponentsData.SafeRelease(); + ComponentsDataSRV.SafeRelease(); + WeightmapWeightBlendModeSRV.SafeRelease(); + WeightmapTextureOutputOffsetSRV.SafeRelease(); + } + + int32 GetComponentsDataCount() const + { + return ComponentsDataCount; + } + +private: + friend class FLandscapeProceduralWeightmapPackLayersCS; + + FStructuredBufferRHIRef ComponentsData; + FShaderResourceViewRHIRef ComponentsDataSRV; + TArray OriginalComponentsData; + int32 ComponentsDataCount; + + TArray OriginalWeightmapWeightBlendModeData; + FVertexBufferRHIRef WeightmapWeightBlendMode; + FShaderResourceViewRHIRef WeightmapWeightBlendModeSRV; + + TArray OriginalTextureOutputOffset; + FVertexBufferRHIRef WeightmapTextureOutputOffset; + FShaderResourceViewRHIRef WeightmapTextureOutputOffsetSRV; +}; + +struct FLandscapeProceduralWeightmapPackLayersComputeShaderParameters +{ + FLandscapeProceduralWeightmapPackLayersComputeShaderParameters() + : ComponentWeightmapResource(nullptr) + , ComputeShaderResource(nullptr) + , AtlasWeightmapsPerLayer(nullptr) + , ComponentSize(0) + {} + + FLandscapeTexture2DResource* ComponentWeightmapResource; + FLandscapeProceduralWeightmapPackLayersComputeShaderResource* ComputeShaderResource; + FLandscapeTexture2DArrayResource* AtlasWeightmapsPerLayer; + uint32 ComponentSize; +}; + +class FLandscapeProceduralWeightmapPackLayersCS : public FGlobalShader +{ + DECLARE_GLOBAL_SHADER(FLandscapeProceduralWeightmapPackLayersCS); +public: + + static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) + { + return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && !IsConsolePlatform(Parameters.Platform) && !IsMetalPlatform(Parameters.Platform); + } + + static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) + { + OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), GLandscapeWeightmapThreadGroupSizeX); + OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), GLandscapeWeightmapThreadGroupSizeY); + } + + FLandscapeProceduralWeightmapPackLayersCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) + : FGlobalShader(Initializer) + { + ComponentWeightmapParam.Bind(Initializer.ParameterMap, TEXT("OutComponentWeightMaps")); + AtlasPaintListsParam.Bind(Initializer.ParameterMap, TEXT("InAtlasPaintLayers")); + ComponentsDataParam.Bind(Initializer.ParameterMap, TEXT("InPackLayersComponentsData")); + ComponentSizeParam.Bind(Initializer.ParameterMap, TEXT("ComponentSize")); + WeightmapWeightBlendModeParam.Bind(Initializer.ParameterMap, TEXT("InWeightmapWeightBlendMode")); + WeightmapTextureOutputOffsetParam.Bind(Initializer.ParameterMap, TEXT("InWeightmapTextureOutputOffset")); + } + + FLandscapeProceduralWeightmapPackLayersCS() + {} + + void SetParameters(FRHICommandList& RHICmdList, const FLandscapeProceduralWeightmapPackLayersComputeShaderParameters& InParams) + { + SetUAVParameter(RHICmdList, GetComputeShader(), ComponentWeightmapParam, InParams.ComponentWeightmapResource->TextureUAV); + SetTextureParameter(RHICmdList, GetComputeShader(), AtlasPaintListsParam, InParams.AtlasWeightmapsPerLayer->TextureRHI); + SetSRVParameter(RHICmdList, GetComputeShader(), ComponentsDataParam, InParams.ComputeShaderResource->ComponentsDataSRV); + SetShaderValue(RHICmdList, GetComputeShader(), ComponentSizeParam, InParams.ComponentSize); + SetSRVParameter(RHICmdList, GetComputeShader(), WeightmapWeightBlendModeParam, InParams.ComputeShaderResource->WeightmapWeightBlendModeSRV); + SetSRVParameter(RHICmdList, GetComputeShader(), WeightmapTextureOutputOffsetParam, InParams.ComputeShaderResource->WeightmapTextureOutputOffsetSRV); + } + + void UnsetParameters(FRHICommandList& RHICmdList) + { + SetUAVParameter(RHICmdList, GetComputeShader(), ComponentWeightmapParam, FUnorderedAccessViewRHIParamRef()); + } + + virtual bool Serialize(FArchive& Ar) override + { + bool bShaderHasOutdatedParameters = FShader::Serialize(Ar); + Ar << ComponentWeightmapParam; + Ar << AtlasPaintListsParam; + Ar << ComponentsDataParam; + Ar << ComponentSizeParam; + Ar << WeightmapWeightBlendModeParam; + Ar << WeightmapTextureOutputOffsetParam; + return bShaderHasOutdatedParameters; + } + +private: + FShaderResourceParameter ComponentWeightmapParam; + FShaderResourceParameter AtlasPaintListsParam; + FShaderResourceParameter ComponentsDataParam; + FShaderParameter ComponentSizeParam; + FShaderResourceParameter WeightmapWeightBlendModeParam; + FShaderResourceParameter WeightmapTextureOutputOffsetParam; +}; + +IMPLEMENT_GLOBAL_SHADER(FLandscapeProceduralWeightmapPackLayersCS, "/Engine/Private/LandscapeProceduralCS.usf", "PackPaintLayerToWeightmap", SF_Compute); + +class FLandscapeProceduralWeightmapPackLayerCSDispatch_RenderThread +{ +public: + FLandscapeProceduralWeightmapPackLayerCSDispatch_RenderThread(const FLandscapeProceduralWeightmapPackLayersComputeShaderParameters& InShaderParams) + : ShaderParams(InShaderParams) + {} + + void PackLayers(FRHICommandListImmediate& InRHICmdList) + { + SCOPE_CYCLE_COUNTER(STAT_LandscapeRegenerateProcedural_RenderThread); + SCOPED_DRAW_EVENTF(InRHICmdList, LandscapeProceduralRender, TEXT("PackLayers")); + + TShaderMapRef ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel)); + InRHICmdList.SetComputeShader(ComputeShader->GetComputeShader()); + ComputeShader->SetParameters(InRHICmdList, ShaderParams); + + uint32 ThreadGroupCountX = FMath::CeilToInt((float)ShaderParams.ComponentSize / (float)GLandscapeWeightmapThreadGroupSizeX); + uint32 ThreadGroupCountY = FMath::CeilToInt((float)ShaderParams.ComponentSize / (float)GLandscapeWeightmapThreadGroupSizeY); + check(ThreadGroupCountX > 0 && ThreadGroupCountY > 0); + + DispatchComputeShader(InRHICmdList, *ComputeShader, ThreadGroupCountX, ThreadGroupCountY, ShaderParams.ComputeShaderResource->GetComponentsDataCount()); + ComputeShader->UnsetParameters(InRHICmdList); + ShaderParams.ComputeShaderResource->ReleaseResource(); + delete ShaderParams.ComputeShaderResource; + } + +private: + FLandscapeProceduralWeightmapPackLayersComputeShaderParameters ShaderParams; +}; + +// Copy texture render command + +class FLandscapeProceduralCopyTexture_RenderThread +{ +public: + FLandscapeProceduralCopyTexture_RenderThread(const FString& InSourceResourceDebugName, FTextureResource* InSourceResource, const FString& InDestResourceDebugName, FTextureResource* InDestResource, FTextureResource* InDestCPUResource, + const FIntPoint& InFirstComponentSectionBase, int32 InSubSectionSizeQuad, int32 InNumSubSections, uint8 InSourceCurrentMip, uint8 InDestCurrentMip, uint32 InSourceArrayIndex, uint32 InDestArrayIndex) + : SourceResource(InSourceResource) + , DestResource(InDestResource) + , DestCPUResource(InDestCPUResource) + , SourceMip(InSourceCurrentMip) + , DestMip(InDestCurrentMip) + , SourceArrayIndex(InSourceArrayIndex) + , DestArrayIndex(InDestArrayIndex) + , ComponentSectionBase(InFirstComponentSectionBase) + , SubSectionSizeQuad(InSubSectionSizeQuad) + , NumSubSections(InNumSubSections) + , SourceDebugName(InSourceResourceDebugName) + , DestResourceDebugName(InDestResourceDebugName) + {} + + void Copy(FRHICommandListImmediate& InRHICmdList) + { + SCOPE_CYCLE_COUNTER(STAT_LandscapeRegenerateProcedural_RenderThread); + SCOPED_DRAW_EVENTF(InRHICmdList, LandscapeProceduralCopy, TEXT("LS Copy %s -> %s, Mip (%d -> %d), Array Index (%d -> %d)"), *SourceDebugName, *DestResourceDebugName, SourceMip, DestMip, SourceArrayIndex, DestArrayIndex); + SCOPED_GPU_STAT(InRHICmdList, LandscapeProceduralCopy); + + FIntPoint SourceSize(SourceResource->GetSizeX(), SourceResource->GetSizeY()); // SourceResource is always proper size, as it's always the good MIP we want to copy from + FIntPoint DestSize(DestResource->GetSizeX() >> DestMip, DestResource->GetSizeY() >> DestMip); int32 LocalComponentSizeQuad = SubSectionSizeQuad * NumSubSections; - FVector2D HeightmapPositionOffset(FMath::RoundToInt(ComponentSectionBase.X / LocalComponentSizeQuad), FMath::RoundToInt(ComponentSectionBase.Y / LocalComponentSizeQuad)); + FVector2D PositionOffset(FMath::RoundToInt(ComponentSectionBase.X / LocalComponentSizeQuad), FMath::RoundToInt(ComponentSectionBase.Y / LocalComponentSizeQuad)); - FResolveParams Params; - Params.SourceArrayIndex = 0; - Params.DestArrayIndex = CurrentMip; + FRHICopyTextureInfo Params; + Params.NumSlices = 1; + Params.Size.Z = 1; + Params.SourceSliceIndex = SourceArrayIndex; + Params.DestSliceIndex = DestArrayIndex; + Params.SourceMipIndex = 0; // In my case, always assume we copy from mip 0 to something else as in my case each mips will be stored into individual texture/RT + Params.DestMipIndex = DestMip; - if (SourceReadTextureSize.X <= CopyResolveWriteTextureSize.X) + if (SourceSize.X <= DestSize.X) { - Params.Rect.X1 = 0; - Params.Rect.X2 = SourceReadTextureSize.X; - Params.DestRect.X1 = FMath::RoundToInt(HeightmapPositionOffset.X * (((SubSectionSizeQuad + 1) * NumSubSections) >> CurrentMip)); + Params.SourcePosition.X = 0; + Params.Size.X = SourceSize.X; + Params.DestPosition.X = FMath::RoundToInt(PositionOffset.X * (((SubSectionSizeQuad + 1) * NumSubSections) >> DestMip)); } else { - Params.Rect.X1 = FMath::RoundToInt(HeightmapPositionOffset.X * (((SubSectionSizeQuad + 1) * NumSubSections) >> CurrentMip)); - Params.Rect.X2 = Params.Rect.X1 + CopyResolveWriteTextureSize.X; - Params.DestRect.X1 = 0; + Params.SourcePosition.X = FMath::RoundToInt(PositionOffset.X * (((SubSectionSizeQuad + 1) * NumSubSections) >> SourceMip)); + Params.Size.X = DestSize.X; + Params.DestPosition.X = 0; } - if (SourceReadTextureSize.Y <= CopyResolveWriteTextureSize.Y) + if (SourceSize.Y <= DestSize.Y) { - Params.Rect.Y1 = 0; - Params.Rect.Y2 = SourceReadTextureSize.Y; - Params.DestRect.Y1 = FMath::RoundToInt(HeightmapPositionOffset.Y * (((SubSectionSizeQuad + 1) * NumSubSections) >> CurrentMip)); + Params.SourcePosition.Y = 0; + Params.Size.Y = SourceSize.Y; + Params.DestPosition.Y = FMath::RoundToInt(PositionOffset.Y * (((SubSectionSizeQuad + 1) * NumSubSections) >> DestMip)); } else { - Params.Rect.Y1 = FMath::RoundToInt(HeightmapPositionOffset.Y * (((SubSectionSizeQuad + 1) * NumSubSections) >> CurrentMip)); - Params.Rect.Y2 = Params.Rect.Y1 + CopyResolveWriteTextureSize.Y; - Params.DestRect.Y1 = 0; - } + Params.SourcePosition.Y = FMath::RoundToInt(PositionOffset.Y * (((SubSectionSizeQuad + 1) * NumSubSections) >> SourceMip)); + Params.Size.Y = DestSize.Y; + Params.DestPosition.Y = 0; + } - InRHICmdList.CopyToResolveTarget(SourceResource->TextureRHI, CopyResolveTargetResource->TextureRHI, Params); + InRHICmdList.CopyTexture(SourceResource->TextureRHI, DestResource->TextureRHI, Params); - if (CopyResolveTargetCPUResource != nullptr) + if (DestCPUResource != nullptr) { - InRHICmdList.CopyToResolveTarget(SourceResource->TextureRHI, CopyResolveTargetCPUResource->TextureRHI, Params); + InRHICmdList.CopyTexture(SourceResource->TextureRHI, DestCPUResource->TextureRHI, Params); } } private: FTextureResource* SourceResource; - FTextureResource* CopyResolveTargetResource; - FTextureResource* CopyResolveTargetCPUResource; - FIntPoint ReadRenderTargetSize; - int32 CurrentMip; + FTextureResource* DestResource; + FTextureResource* DestCPUResource; + uint8 SourceMip; + uint8 DestMip; + uint32 SourceArrayIndex; + uint32 DestArrayIndex; FIntPoint ComponentSectionBase; int32 SubSectionSizeQuad; int32 NumSubSections; FString SourceDebugName; - FString CopyResolveDebugName; + FString DestResourceDebugName; }; -class FLandscapeHeightmapProceduralRender_RenderThread +// Clear command + +class LandscapeProceduralWeightmapClear_RenderThread +{ +public: + LandscapeProceduralWeightmapClear_RenderThread(const FString& InDebugName, FTextureRenderTargetResource* InTextureResourceToClear) + : DebugName(InDebugName) + , RenderTargetResource(InTextureResourceToClear) + {} + + virtual ~LandscapeProceduralWeightmapClear_RenderThread() + {} + + void Clear(FRHICommandListImmediate& InRHICmdList) + { + SCOPE_CYCLE_COUNTER(STAT_LandscapeRegenerateProcedural_RenderThread); + SCOPED_DRAW_EVENTF(InRHICmdList, LandscapeProceduralRender, TEXT("%s"), DebugName.Len() > 0 ? *DebugName : TEXT("LandscapeProceduralClear")); + SCOPED_GPU_STAT(InRHICmdList, LandscapeProceduralRender); + + check(IsInRenderingThread()); + + FRHIRenderPassInfo RPInfo(RenderTargetResource->TextureRHI, ERenderTargetActions::Clear_Store); + InRHICmdList.BeginRenderPass(RPInfo, TEXT("Clear")); + InRHICmdList.EndRenderPass(); + } + + FString DebugName; + FTextureRenderTargetResource* RenderTargetResource; +}; + +// Render command + +template +class FLandscapeProceduralRender_RenderThread { public: - FLandscapeHeightmapProceduralRender_RenderThread(const FString& InDebugName, UTextureRenderTarget2D* InWriteRenderTarget, const FIntPoint& InWriteRenderTargetSize, const FIntPoint& InReadRenderTargetSize, const FMatrix& InProjectionMatrix, - const FLandscapeHeightmapProceduralShaderParameters& InShaderParams, int32 InCurrentMip, const TArray& InTriangleList) + FLandscapeProceduralRender_RenderThread(const FString& InDebugName, UTextureRenderTarget2D* InWriteRenderTarget, const FIntPoint& InWriteRenderTargetSize, const FIntPoint& InReadRenderTargetSize, const FMatrix& InProjectionMatrix, + const ShaderDataType& InShaderParams, uint8 InCurrentMip, const TArray& InTriangleList) : RenderTargetResource(InWriteRenderTarget->GameThread_GetRenderTargetResource()) , WriteRenderTargetSize(InWriteRenderTargetSize) , ReadRenderTargetSize(InReadRenderTargetSize) @@ -495,15 +1112,15 @@ public: VertexBufferResource.Init(InTriangleList); } - virtual ~FLandscapeHeightmapProceduralRender_RenderThread() + virtual ~FLandscapeProceduralRender_RenderThread() {} void Render(FRHICommandListImmediate& InRHICmdList, bool InClearRT) { - SCOPE_CYCLE_COUNTER(STAT_LandscapeRegenerateProceduralHeightmaps_RenderThread); - SCOPED_DRAW_EVENTF(InRHICmdList, LandscapeProceduralHeightmapRender, TEXT("%s"), DebugName.Len() > 0 ? *DebugName : TEXT("LandscapeProceduralHeightmapRender")); + SCOPE_CYCLE_COUNTER(STAT_LandscapeRegenerateProcedural_RenderThread); + SCOPED_DRAW_EVENTF(InRHICmdList, LandscapeProceduralRender, TEXT("%s"), DebugName.Len() > 0 ? *DebugName : TEXT("LandscapeProceduralRender")); SCOPED_GPU_STAT(InRHICmdList, LandscapeProceduralRender); - INC_DWORD_STAT(STAT_LandscapeRegenerateProceduralHeightmapsDrawCalls); + INC_DWORD_STAT(STAT_LandscapeRegenerateProceduralDrawCalls); check(IsInRenderingThread()); @@ -531,19 +1148,18 @@ public: GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = VertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.PrimitiveType = PT_TriangleList; - //RT0ColorWriteMask, RT0ColorBlendOp, RT0ColorSrcBlend, RT0ColorDestBlend, RT0AlphaBlendOp, RT0AlphaSrcBlend, RT0AlphaDestBlend, GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); FRHIRenderPassInfo RenderPassInfo(ViewFamily.RenderTarget->GetRenderTargetTexture(), CurrentMip == 0 ? ERenderTargetActions::Clear_Store : ERenderTargetActions::Load_Store, nullptr, 0, 0); - InRHICmdList.BeginRenderPass(RenderPassInfo, TEXT("DrawProceduralHeightmaps")); + InRHICmdList.BeginRenderPass(RenderPassInfo, TEXT("DrawProcedural")); if (CurrentMip == 0) { // Setup Shaders TShaderMapRef VertexShader(GetGlobalShaderMap(View->GetFeatureLevel())); - TShaderMapRef PixelShader(GetGlobalShaderMap(View->GetFeatureLevel())); + TShaderMapRef PixelShader(GetGlobalShaderMap(View->GetFeatureLevel())); GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader); @@ -561,7 +1177,7 @@ public: { // Setup Shaders TShaderMapRef VertexShader(GetGlobalShaderMap(View->GetFeatureLevel())); - TShaderMapRef PixelShader(GetGlobalShaderMap(View->GetFeatureLevel())); + TShaderMapRef PixelShader(GetGlobalShaderMap(View->GetFeatureLevel())); GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader); @@ -593,21 +1209,24 @@ private: FIntPoint WriteRenderTargetSize; FIntPoint ReadRenderTargetSize; FMatrix ProjectionMatrix; - FLandscapeHeightmapProceduralShaderParameters ShaderParams; + ShaderDataType ShaderParams; FLandscapeProceduralVertexBuffer VertexBufferResource; int32 PrimitiveCount; FLandscapeProceduralVertexDeclaration VertexDeclaration; FString DebugName; - int32 CurrentMip; + uint8 CurrentMip; }; +typedef FLandscapeProceduralRender_RenderThread LandscapeProceduralHeightmapRender_RenderThread; +typedef FLandscapeProceduralRender_RenderThread LandscapeProceduralWeightmapRender_RenderThread; + +#if WITH_EDITOR void ALandscapeProxy::SetupProceduralLayers(int32 InNumComponentsX, int32 InNumComponentsY) { ALandscape* Landscape = GetLandscapeActor(); check(Landscape); ULandscapeInfo* Info = GetLandscapeInfo(); - if (Info == nullptr) { return; @@ -615,62 +1234,24 @@ void ALandscapeProxy::SetupProceduralLayers(int32 InNumComponentsX, int32 InNumC TArray AllLandscapes; AllLandscapes.Add(Landscape); - for (auto& It : Info->Proxies) { AllLandscapes.Add(It); } - // TEMP STUFF START - bool Layer1Exist = false; - FProceduralLayer Layer1; - Layer1.Name = TEXT("Layer1"); - - bool Layer2Exist = false; - FProceduralLayer Layer2; - Layer2.Name = TEXT("Layer2"); - - for (int32 i = 0; i < Landscape->ProceduralLayers.Num(); ++i) + // Make sure we have at least 1 layer + if (Landscape->ProceduralLayers.Num() == 0) { - if (Landscape->ProceduralLayers[i].Name == Layer1.Name) - { - Layer1Exist = true; - } - - if (Landscape->ProceduralLayers[i].Name == Layer2.Name) - { - Layer2Exist = true; - } - } - - if (!Layer1Exist) - { - Landscape->ProceduralLayers.Add(Layer1); - - for (ALandscapeProxy* LandscapeProxy : AllLandscapes) - { - LandscapeProxy->ProceduralLayersData.Add(Layer1.Name, FProceduralLayerData()); - } - } - - if (!Layer2Exist) - { - Landscape->ProceduralLayers.Add(Layer2); - - for (ALandscapeProxy* LandscapeProxy : AllLandscapes) - { - LandscapeProxy->ProceduralLayersData.Add(Layer2.Name, FProceduralLayerData()); - } + Landscape->CreateProceduralLayer(FName(TEXT("Layer")), false); } - ///// TEMP STUFF END + + // TODO: When using Change Component Size, we will call Setup again, currently all the existing data will get collapsed into the layer 1, it should keep the layers data. int32 NumComponentsX = InNumComponentsX; int32 NumComponentsY = InNumComponentsY; bool GenerateComponentCounts = NumComponentsX == INDEX_NONE || NumComponentsY == INDEX_NONE; FIntPoint MaxSectionBase(0, 0); - uint32 UpdateFlags = 0; - // Setup all Heightmap data for (ALandscapeProxy* LandscapeProxy : AllLandscapes) { @@ -714,20 +1295,21 @@ void ALandscapeProxy::SetupProceduralLayers(int32 InNumComponentsX, int32 InNumC if (Landscape->HeightmapRTList.Num() == 0) { - Landscape->HeightmapRTList.Init(nullptr, EHeightmapRTType::Count); + Landscape->HeightmapRTList.Init(nullptr, EHeightmapRTType::HeightmapRT_Count); int32 CurrentMipSizeX = ((SubsectionSizeQuads + 1) * NumSubsections) * NumComponentsX; int32 CurrentMipSizeY = ((SubsectionSizeQuads + 1) * NumSubsections) * NumComponentsY; - for (int32 i = 0; i < EHeightmapRTType::Count; ++i) + for (int32 i = 0; i < EHeightmapRTType::HeightmapRT_Count; ++i) { Landscape->HeightmapRTList[i] = NewObject(Landscape->GetOutermost()); check(Landscape->HeightmapRTList[i]); Landscape->HeightmapRTList[i]->RenderTargetFormat = RTF_RGBA8; Landscape->HeightmapRTList[i]->AddressX = TextureAddress::TA_Clamp; Landscape->HeightmapRTList[i]->AddressY = TextureAddress::TA_Clamp; + Landscape->HeightmapRTList[i]->ClearColor = FLinearColor(0.0f, 0.0f, 0.0f, 0.0f); - if (i < LandscapeSizeMip1) // Landscape size RT + if (i < EHeightmapRTType::HeightmapRT_Mip1) // Landscape size RT { Landscape->HeightmapRTList[i]->InitAutoFormat(FMath::RoundUpToPowerOfTwo(TotalVertexCountX), FMath::RoundUpToPowerOfTwo(TotalVertexCountY)); } @@ -751,8 +1333,6 @@ void ALandscapeProxy::SetupProceduralLayers(int32 InNumComponentsX, int32 InNumC TArray VertexNormals; TArray EmptyHeightmapData; - UpdateFlags |= EProceduralContentUpdateFlag::Heightmap_Render; - // Setup all Heightmap data for (ALandscapeProxy* LandscapeProxy : AllLandscapes) { @@ -782,12 +1362,10 @@ void ALandscapeProxy::SetupProceduralLayers(int32 InNumComponentsX, int32 InNumC int32 MipSizeU = Heightmap->Source.GetSizeX(); int32 MipSizeV = Heightmap->Source.GetSizeY(); - UpdateFlags |= EProceduralContentUpdateFlag::Heightmap_ResolveToTexture | EProceduralContentUpdateFlag::Heightmap_BoundsAndCollision; - // Copy data from Heightmap to first layer, after that all other layer will get init to empty layer if (FirstLayer) { - int32 MipIndex = 0; + uint8 MipIndex = 0; TArray MipData; MipData.Reserve(MipSizeU*MipSizeV * sizeof(FColor)); @@ -948,19 +1526,224 @@ void ALandscapeProxy::SetupProceduralLayers(int32 InNumComponentsX, int32 InNumC } } - // Setup all Weightmap data - // TODO + // Weightmaps handling + if (Landscape->WeightmapRTList.Num() == 0) + { + Landscape->WeightmapRTList.Init(nullptr, EWeightmapRTType::WeightmapRT_Count); + + int32 CurrentMipSizeX = ((SubsectionSizeQuads + 1) * NumSubsections) * NumComponentsX; + int32 CurrentMipSizeY = ((SubsectionSizeQuads + 1) * NumSubsections) * NumComponentsY; + + for (int32 i = 0; i < EWeightmapRTType::WeightmapRT_Count; ++i) + { + Landscape->WeightmapRTList[i] = NewObject(Landscape->GetOutermost()); + + check(Landscape->WeightmapRTList[i]); + Landscape->WeightmapRTList[i]->AddressX = TextureAddress::TA_Clamp; + Landscape->WeightmapRTList[i]->AddressY = TextureAddress::TA_Clamp; + Landscape->WeightmapRTList[i]->ClearColor = FLinearColor(0.0f, 0.0f, 0.0f, 0.0f); + Landscape->WeightmapRTList[i]->RenderTargetFormat = RTF_RGBA8; + + if (i < EWeightmapRTType::WeightmapRT_Mip0) // Landscape size RT, only create the number of layer we have + { + Landscape->WeightmapRTList[i]->RenderTargetFormat = i == WeightmapRT_Scratch_RGBA ? RTF_RGBA8 : RTF_R8; + Landscape->WeightmapRTList[i]->InitAutoFormat(FMath::RoundUpToPowerOfTwo(TotalVertexCountX), FMath::RoundUpToPowerOfTwo(TotalVertexCountY)); + } + else // Mips + { + Landscape->WeightmapRTList[i]->InitAutoFormat(FMath::RoundUpToPowerOfTwo(CurrentMipSizeX), FMath::RoundUpToPowerOfTwo(CurrentMipSizeY)); + + CurrentMipSizeX >>= 1; + CurrentMipSizeY >>= 1; + } + + Landscape->WeightmapRTList[i]->UpdateResourceImmediate(true); + + // Only generate required mips RT + if (CurrentMipSizeX == NumComponentsX && CurrentMipSizeY == NumComponentsY) + { + break; + } + } + } + + TArray ComponentsToCleanup; + + for (ALandscapeProxy* LandscapeProxy : AllLandscapes) + { + bool FirstLayer = true; + + for (auto& ItLayerDataPair : LandscapeProxy->ProceduralLayersData) + { + FGuid ProceduralLayerGuid = ItLayerDataPair.Key; + FProceduralLayerData& ProceduralLayerData = ItLayerDataPair.Value; + + struct FTextureData + { + UTexture2D* Texture; + ULandscapeWeightmapUsage* Usage; + ULandscapeWeightmapUsage* OriginalUsage; + }; + + TMap ProcessedTextures; + + for (ULandscapeComponent* Component : LandscapeProxy->LandscapeComponents) + { + FWeightmapLayerData* WeightmapLayer = ProceduralLayerData.WeightmapData.Find(Component); + + if (WeightmapLayer == nullptr) + { + FWeightmapLayerData& NewWeightmapData = ProceduralLayerData.WeightmapData.Add(Component, FWeightmapLayerData()); + + // If no data exist for this weightmap and that data exist in the base weightmap, simply copy it to the first layer, and clear the data in the base (as it will become the final weightmap) + if (FirstLayer) + { + ComponentsToCleanup.Add(Component); + + const TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(); + TArray& ComponentLayerAllocations = Component->GetWeightmapLayerAllocations(); + TArray& ComponentWeightmapTexturesUsage = Component->GetWeightmapTexturesUsage(); + + NewWeightmapData.Weightmaps.AddDefaulted(ComponentWeightmapTextures.Num()); + NewWeightmapData.WeightmapTextureUsages.AddDefaulted(ComponentWeightmapTexturesUsage.Num()); + + for (int32 TextureIndex = 0; TextureIndex < ComponentWeightmapTextures.Num(); ++TextureIndex) + { + UTexture2D* OriginalWeightmapTexture = ComponentWeightmapTextures[TextureIndex]; + FTextureData* TextureData = ProcessedTextures.Find(OriginalWeightmapTexture); + + if (TextureData != nullptr) + { + ComponentWeightmapTexturesUsage[TextureIndex] = TextureData->OriginalUsage; + + NewWeightmapData.Weightmaps[TextureIndex] = TextureData->Texture; + NewWeightmapData.WeightmapTextureUsages[TextureIndex] = TextureData->Usage; + check(TextureData->Usage->ProceduralLayerGuid == ProceduralLayerGuid); + + for (int32 ChannelIndex = 0; ChannelIndex < 4; ++ChannelIndex) + { + ULandscapeComponent* ChannelLandscapeComponent = NewWeightmapData.WeightmapTextureUsages.Last()->ChannelUsage[ChannelIndex]; + + if (ChannelLandscapeComponent != nullptr && ChannelLandscapeComponent == Component) + { + for (FWeightmapLayerAllocationInfo& Allocation : ComponentLayerAllocations) + { + if (Allocation.WeightmapTextureIndex == TextureIndex) + { + NewWeightmapData.WeightmapLayerAllocations.Add(Allocation); + } + } + + break; + } + } + } + else + { + UTexture2D* NewWeightmapTexture = LandscapeProxy->CreateLandscapeTexture(OriginalWeightmapTexture->Source.GetSizeX(), OriginalWeightmapTexture->Source.GetSizeY(), TEXTUREGROUP_Terrain_Weightmap, OriginalWeightmapTexture->Source.GetFormat()); + + int32 MipSubsectionSizeQuads = SubsectionSizeQuads; + int32 MipSizeU = OriginalWeightmapTexture->Source.GetSizeX(); + int32 MipSizeV = OriginalWeightmapTexture->Source.GetSizeY(); + + uint8 MipIndex = 0; + TArray MipData; + MipData.Reserve(MipSizeU * MipSizeV * sizeof(FColor)); + + while (MipSizeU > 1 && MipSizeV > 1 && MipSubsectionSizeQuads >= 1) + { + MipData.Reset(); + OriginalWeightmapTexture->Source.GetMipData(MipData, MipIndex); + + FColor* WeightmapTextureData = (FColor*)NewWeightmapTexture->Source.LockMip(MipIndex); + FMemory::Memcpy(WeightmapTextureData, MipData.GetData(), MipData.Num()); + NewWeightmapTexture->Source.UnlockMip(MipIndex); + + MipSizeU >>= 1; + MipSizeV >>= 1; + + MipSubsectionSizeQuads = ((MipSubsectionSizeQuads + 1) >> 1) - 1; + ++MipIndex; + } + + NewWeightmapData.Weightmaps[TextureIndex] = NewWeightmapTexture; + NewWeightmapData.WeightmapTextureUsages[TextureIndex] = ComponentWeightmapTexturesUsage[TextureIndex]; + NewWeightmapData.WeightmapTextureUsages[TextureIndex]->ProceduralLayerGuid = ProceduralLayerGuid; + + // Create new Usage for the base layer as the other one will now be used by the Layer 1 + ComponentWeightmapTexturesUsage[TextureIndex] = LandscapeProxy->WeightmapUsageMap.Add(NewWeightmapTexture, NewObject(LandscapeProxy)); + + for (FWeightmapLayerAllocationInfo& Allocation : ComponentLayerAllocations) + { + if (Allocation.WeightmapTextureIndex == TextureIndex) + { + NewWeightmapData.WeightmapLayerAllocations.Add(Allocation); + } + } + + FTextureData NewTextureData; + NewTextureData.Texture = NewWeightmapTexture; + NewTextureData.Usage = NewWeightmapData.WeightmapTextureUsages[TextureIndex]; + NewTextureData.OriginalUsage = ComponentWeightmapTexturesUsage[TextureIndex]; + + ProcessedTextures.Add(OriginalWeightmapTexture, NewTextureData); + + NewWeightmapTexture->BeginCachePlatformData(); + NewWeightmapTexture->ClearAllCachedCookedPlatformData(); + } + } + } + } + else + { + WeightmapLayer->WeightmapTextureUsages.AddDefaulted(WeightmapLayer->Weightmaps.Num()); + + // regenerate the weightmap usage + for (int32 LayerIdx = 0; LayerIdx < WeightmapLayer->WeightmapLayerAllocations.Num(); LayerIdx++) + { + FWeightmapLayerAllocationInfo& Allocation = WeightmapLayer->WeightmapLayerAllocations[LayerIdx]; + UTexture2D* WeightmapTexture = WeightmapLayer->Weightmaps[Allocation.WeightmapTextureIndex]; + ULandscapeWeightmapUsage** TempUsage = LandscapeProxy->WeightmapUsageMap.Find(WeightmapTexture); + + if (TempUsage == nullptr) + { + TempUsage = &LandscapeProxy->WeightmapUsageMap.Add(WeightmapTexture, NewObject(LandscapeProxy)); + (*TempUsage)->ProceduralLayerGuid = ProceduralLayerGuid; + } + + ULandscapeWeightmapUsage* Usage = *TempUsage; + WeightmapLayer->WeightmapTextureUsages[Allocation.WeightmapTextureIndex] = Usage; // Keep a ref to it for faster access + + check(Usage->ChannelUsage[Allocation.WeightmapTextureChannel] == nullptr || Usage->ChannelUsage[Allocation.WeightmapTextureChannel] == Component); + + Usage->ChannelUsage[Allocation.WeightmapTextureChannel] = Component; + } + } + } + + FirstLayer = false; + } + } + + for (ULandscapeComponent* Component : ComponentsToCleanup) + { + TArray& ComponentLayerAllocations = Component->GetWeightmapLayerAllocations(); + ComponentLayerAllocations.Reset(); + } // Fix Owning actor for Brushes. It can happen after save as operation, for example for (FProceduralLayer& Layer : Landscape->ProceduralLayers) { - for (int32 i = 0; i < Layer.Brushes.Num(); ++i) + for (int32 i = Layer.Brushes.Num() - 1; i >= 0; --i) { FLandscapeProceduralLayerBrush& Brush = Layer.Brushes[i]; - if (Brush.BPCustomBrush->GetOwningLandscape() == nullptr) + if (Brush.BPCustomBrush != nullptr) { - Brush.BPCustomBrush->SetOwningLandscape(Landscape); + if (Brush.BPCustomBrush->GetOwningLandscape() == nullptr) + { + Brush.BPCustomBrush->SetOwningLandscape(Landscape); + } } } @@ -971,7 +1754,7 @@ void ALandscapeProxy::SetupProceduralLayers(int32 InNumComponentsX, int32 InNumC { FLandscapeProceduralLayerBrush& Brush = Layer.Brushes[i]; - if (Brush.BPCustomBrush->IsAffectingHeightmap()) + if (Brush.BPCustomBrush != nullptr && Brush.BPCustomBrush->IsAffectingHeightmap()) { Layer.HeightmapBrushOrderIndices.Add(i); } @@ -984,7 +1767,7 @@ void ALandscapeProxy::SetupProceduralLayers(int32 InNumComponentsX, int32 InNumC { FLandscapeProceduralLayerBrush& Brush = Layer.Brushes[i]; - if (Brush.BPCustomBrush->IsAffectingWeightmap()) + if (Brush.BPCustomBrush != nullptr && Brush.BPCustomBrush->IsAffectingWeightmap()) { Layer.WeightmapBrushOrderIndices.Add(i); } @@ -992,18 +1775,183 @@ void ALandscapeProxy::SetupProceduralLayers(int32 InNumComponentsX, int32 InNumC } // TEMP stuff } - - Landscape->RequestProceduralContentUpdate(UpdateFlags); } -void ALandscape::CopyProceduralTargetToResolveTarget(UTexture* InHeightmapRTRead, UTexture* InCopyResolveTarget, FTextureResource* InCopyResolveTargetCPUResource, const FIntPoint& InFirstComponentSectionBase, int32 InCurrentMip) const +void ALandscape::CopyProceduralTexture(UTexture* InSourceTexture, UTexture* InDestTexture, FTextureResource* InDestCPUResource, const FIntPoint& InFirstComponentSectionBase, uint8 InSourceCurrentMip, uint8 InDestCurrentMip, uint32 InSourceArrayIndex, uint32 InDestArrayIndex) const { - FLandscapeProceduralCopyResource_RenderThread CopyResource(InHeightmapRTRead, InCopyResolveTarget, InCopyResolveTargetCPUResource, InFirstComponentSectionBase, SubsectionSizeQuads, NumSubsections, InCurrentMip); + if (InSourceTexture != nullptr && InDestTexture != nullptr) + { + CopyProceduralTexture(InSourceTexture->GetName(), InSourceTexture->Resource, InDestTexture->GetName(), InDestTexture->Resource, InDestCPUResource, InFirstComponentSectionBase, InSourceCurrentMip, InDestCurrentMip, InSourceArrayIndex, InDestArrayIndex); + } +} - ENQUEUE_RENDER_COMMAND(FLandscapeProceduralCopyResultCommand)( - [CopyResource](FRHICommandListImmediate& RHICmdList) mutable +void ALandscape::CopyProceduralTexture(const FString& InSourceDebugName, FTextureResource* InSourceResource, const FString& InDestDebugName, FTextureResource* InDestResource, FTextureResource* InDestCPUResource, const FIntPoint& InFirstComponentSectionBase, + uint8 InSourceCurrentMip, uint8 InDestCurrentMip, uint32 InSourceArrayIndex, uint32 InDestArrayIndex) const +{ + check(InSourceResource != nullptr); + check(InDestResource != nullptr); + + FLandscapeProceduralCopyTexture_RenderThread CopyTexture(InSourceDebugName, InSourceResource, InDestDebugName, InDestResource, InDestCPUResource, InFirstComponentSectionBase, SubsectionSizeQuads, NumSubsections, InSourceCurrentMip, InDestCurrentMip, InSourceArrayIndex, InDestArrayIndex); + + ENQUEUE_RENDER_COMMAND(FLandscapeProceduralCopyCommand)( + [CopyTexture](FRHICommandListImmediate& RHICmdList) mutable + { + CopyTexture.Copy(RHICmdList); + }); +} + +void ALandscape::DrawWeightmapComponentsToRenderTarget(const FString& InDebugName, const FIntPoint& InSectionBase, const FVector2D& InScaleBias, UTexture* InWeightmapRTRead, UTextureRenderTarget2D* InOptionalWeightmapRTRead2, UTextureRenderTarget2D* InWeightmapRTWrite, + bool InClearRTWrite, FLandscapeWeightmapProceduralShaderParameters& InShaderParams, uint8 InMipRender) const +{ + check(InWeightmapRTRead != nullptr); + check(InWeightmapRTWrite != nullptr); + + FIntPoint WeightmapWriteTextureSize(InWeightmapRTWrite->SizeX, InWeightmapRTWrite->SizeY); + FIntPoint WeightmapReadTextureSize(InWeightmapRTRead->Source.GetSizeX(), InWeightmapRTRead->Source.GetSizeY()); + UTextureRenderTarget2D* WeightmapRTRead = Cast(InWeightmapRTRead); + + if (WeightmapRTRead != nullptr) + { + WeightmapReadTextureSize.X = WeightmapRTRead->SizeX; + WeightmapReadTextureSize.Y = WeightmapRTRead->SizeY; + } + + // Quad Setup + TArray TriangleList; + TriangleList.Reserve(1 * 2 * NumSubsections); + + if (InMipRender == 0) + { + GenerateProceduralRenderQuadsAtlas(InSectionBase, InScaleBias, SubsectionSizeQuads, WeightmapReadTextureSize, WeightmapWriteTextureSize, TriangleList); + } + else + { + GenerateProceduralRenderQuadsMip(InSectionBase, InScaleBias, SubsectionSizeQuads, WeightmapReadTextureSize, WeightmapWriteTextureSize, InMipRender, TriangleList); + } + + InShaderParams.ReadWeightmap1 = InWeightmapRTRead; + InShaderParams.ReadWeightmap2 = InOptionalWeightmapRTRead2; + InShaderParams.CurrentMipComponentVertexCount = (((SubsectionSizeQuads + 1) * NumSubsections) >> InMipRender); + + if (InMipRender > 0) + { + InShaderParams.CurrentMipSize = WeightmapWriteTextureSize; + InShaderParams.ParentMipSize = WeightmapReadTextureSize; + } + + FMatrix ProjectionMatrix = AdjustProjectionMatrixForRHI(FTranslationMatrix(FVector(0, 0, 0)) * + FMatrix(FPlane(1.0f / (FMath::Max(WeightmapWriteTextureSize.X, 1.f) / 2.0f), 0.0, 0.0f, 0.0f), FPlane(0.0f, -1.0f / (FMath::Max(WeightmapWriteTextureSize.Y, 1.f) / 2.0f), 0.0f, 0.0f), FPlane(0.0f, 0.0f, 1.0f, 0.0f), FPlane(-1.0f, 1.0f, 0.0f, 1.0f))); + + LandscapeProceduralWeightmapRender_RenderThread ProceduralRender(InDebugName, InWeightmapRTWrite, WeightmapWriteTextureSize, WeightmapReadTextureSize, ProjectionMatrix, InShaderParams, InMipRender, TriangleList); + + ENQUEUE_RENDER_COMMAND(FDrawLandscapeProceduralWeightmapCommand)( + [ProceduralRender, ClearRT = InClearRTWrite](FRHICommandListImmediate& RHICmdList) mutable + { + ProceduralRender.Render(RHICmdList, ClearRT); + }); + + PrintProceduralDebugRT(InDebugName, InWeightmapRTWrite, InMipRender, false); +} + +void ALandscape::DrawWeightmapComponentsToRenderTarget(const FString& InDebugName, const TArray& InComponentsToDraw, UTexture* InWeightmapRTRead, UTextureRenderTarget2D* InOptionalWeightmapRTRead2, UTextureRenderTarget2D* InWeightmapRTWrite, + bool InClearRTWrite, FLandscapeWeightmapProceduralShaderParameters& InShaderParams, uint8 InMipRender) const +{ + check(InWeightmapRTRead != nullptr); + check(InWeightmapRTWrite != nullptr); + + FIntPoint WeightmapWriteTextureSize(InWeightmapRTWrite->SizeX, InWeightmapRTWrite->SizeY); + FIntPoint WeightmapReadTextureSize(InWeightmapRTRead->Source.GetSizeX(), InWeightmapRTRead->Source.GetSizeY()); + UTextureRenderTarget2D* WeightmapRTRead = Cast(InWeightmapRTRead); + + if (WeightmapRTRead != nullptr) + { + WeightmapReadTextureSize.X = WeightmapRTRead->SizeX; + WeightmapReadTextureSize.Y = WeightmapRTRead->SizeY; + } + + // Quad Setup + TArray TriangleList; + TriangleList.Reserve(InComponentsToDraw.Num() * 2 * NumSubsections); + + if (InMipRender == 0) + { + for (ULandscapeComponent* Component : InComponentsToDraw) { - CopyResource.CopyToResolveTarget(RHICmdList); + // TODO: check what to do with WeightmapSubsectionOffset + FVector2D WeightmapScaleBias(Component->WeightmapScaleBias.Z, Component->WeightmapScaleBias.W); + GenerateProceduralRenderQuadsAtlas(Component->GetSectionBase(), WeightmapScaleBias, SubsectionSizeQuads, WeightmapReadTextureSize, WeightmapWriteTextureSize, TriangleList); + } + } + else + { + for (ULandscapeComponent* Component : InComponentsToDraw) + { + // TODO: check what to do with WeightmapSubsectionOffset + FVector2D WeightmapScaleBias(Component->WeightmapScaleBias.Z, Component->WeightmapScaleBias.W); + GenerateProceduralRenderQuadsMip(Component->GetSectionBase(), WeightmapScaleBias, SubsectionSizeQuads, WeightmapReadTextureSize, WeightmapWriteTextureSize, InMipRender, TriangleList); + } + } + + InShaderParams.ReadWeightmap1 = InWeightmapRTRead; + InShaderParams.ReadWeightmap2 = InOptionalWeightmapRTRead2; + InShaderParams.CurrentMipComponentVertexCount = (((SubsectionSizeQuads + 1) * NumSubsections) >> InMipRender); + + if (InMipRender > 0) + { + InShaderParams.CurrentMipSize = WeightmapWriteTextureSize; + InShaderParams.ParentMipSize = WeightmapReadTextureSize; + } + + FMatrix ProjectionMatrix = AdjustProjectionMatrixForRHI(FTranslationMatrix(FVector(0, 0, 0)) * + FMatrix(FPlane(1.0f / (FMath::Max(WeightmapWriteTextureSize.X, 1.f) / 2.0f), 0.0, 0.0f, 0.0f), FPlane(0.0f, -1.0f / (FMath::Max(WeightmapWriteTextureSize.Y, 1.f) / 2.0f), 0.0f, 0.0f), FPlane(0.0f, 0.0f, 1.0f, 0.0f), FPlane(-1.0f, 1.0f, 0.0f, 1.0f))); + + LandscapeProceduralWeightmapRender_RenderThread ProceduralRender(InDebugName, InWeightmapRTWrite, WeightmapWriteTextureSize, WeightmapReadTextureSize, ProjectionMatrix, InShaderParams, InMipRender, TriangleList); + + ENQUEUE_RENDER_COMMAND(FDrawLandscapeProceduralWeightmapCommand)( + [ProceduralRender, ClearRT = InClearRTWrite](FRHICommandListImmediate& RHICmdList) mutable + { + ProceduralRender.Render(RHICmdList, ClearRT); + }); + + PrintProceduralDebugRT(InDebugName, InWeightmapRTWrite, InMipRender, false); +} + +void ALandscape::DrawWeightmapComponentToRenderTargetMips(const FIntPoint& TopLeftTexturePosition, UTexture* InReadWeightmap, bool InClearRTWrite, struct FLandscapeWeightmapProceduralShaderParameters& InShaderParams) const +{ + bool OutputDebugName = CVarOutputProceduralDebugDrawCallName.GetValueOnAnyThread() == 1 || CVarOutputProceduralRTContent.GetValueOnAnyThread() == 1 ? true : false; + int32 CurrentMip = 1; + UTexture* ReadMipRT = InReadWeightmap; + + // Convert from Texture position to SectionBase + int32 LocalComponentSizeQuad = SubsectionSizeQuads * NumSubsections; + int32 LocalComponentSizeVerts = SubsectionSizeQuads + 1 * NumSubsections; + + FVector2D PositionOffset(FMath::RoundToInt(TopLeftTexturePosition.X / LocalComponentSizeVerts), FMath::RoundToInt(TopLeftTexturePosition.Y / LocalComponentSizeVerts)); + FIntPoint ComponentSectionBase(PositionOffset.X * LocalComponentSizeQuad, PositionOffset.Y * LocalComponentSizeQuad); + FVector2D WeightmapScaleBias(0.0f, 0.0f); + + for (int32 MipRTIndex = EWeightmapRTType::WeightmapRT_Mip1; MipRTIndex < EWeightmapRTType::WeightmapRT_Count; ++MipRTIndex) + { + UTextureRenderTarget2D* WriteMipRT = WeightmapRTList[MipRTIndex]; + + if (WriteMipRT != nullptr) + { + DrawWeightmapComponentsToRenderTarget(OutputDebugName ? FString::Printf(TEXT("LS Weight: %s = -> %s Mips %d"), *ReadMipRT->GetName(), *WriteMipRT->GetName(), CurrentMip) : TEXT(""), + ComponentSectionBase, WeightmapScaleBias, ReadMipRT, nullptr, WriteMipRT, InClearRTWrite, InShaderParams, CurrentMip++); + } + + ReadMipRT = WeightmapRTList[MipRTIndex]; + } +} + +void ALandscape::ClearWeightmapTextureResource(const FString& InDebugName, FTextureRenderTargetResource* InTextureResourceToClear) +{ + LandscapeProceduralWeightmapClear_RenderThread ProceduralClear(InDebugName, InTextureResourceToClear); + + ENQUEUE_RENDER_COMMAND(FLandscapeProceduralClearWeightmapCommand)( + [ProceduralClear](FRHICommandListImmediate& RHICmdList) mutable + { + ProceduralClear.Clear(RHICmdList); }); } @@ -1013,7 +1961,7 @@ void ALandscape::DrawHeightmapComponentsToRenderTargetMips(TArray& InComponentsToDraw, UTexture* InHeightmapRTRead, UTextureRenderTarget2D* InOptionalHeightmapRTRead2, UTextureRenderTarget2D* InHeightmapRTWrite, - ERTDrawingType InDrawType, bool InClearRTWrite, FLandscapeHeightmapProceduralShaderParameters& InShaderParams, int32 InMipRender) const +void ALandscape::DrawHeightmapComponentsToRenderTarget(const FString& InDebugName, const TArray& InComponentsToDraw, UTexture* InHeightmapRTRead, UTextureRenderTarget2D* InOptionalHeightmapRTRead2, UTextureRenderTarget2D* InHeightmapRTWrite, + ERTDrawingType InDrawType, bool InClearRTWrite, FLandscapeHeightmapProceduralShaderParameters& InShaderParams, uint8 InMipRender) const { check(InHeightmapRTRead != nullptr); check(InHeightmapRTWrite != nullptr); @@ -1054,7 +2002,7 @@ void ALandscape::DrawHeightmapComponentsToRenderTarget(const FString& InDebugNam for (ULandscapeComponent* Component : InComponentsToDraw) { FVector2D HeightmapScaleBias(Component->HeightmapScaleBias.Z, Component->HeightmapScaleBias.W); - GenerateHeightmapQuadsAtlas(Component->GetSectionBase(), HeightmapScaleBias, SubsectionSizeQuads, HeightmapReadTextureSize, HeightmapWriteTextureSize, TriangleList); + GenerateProceduralRenderQuadsAtlas(Component->GetSectionBase(), HeightmapScaleBias, SubsectionSizeQuads, HeightmapReadTextureSize, HeightmapWriteTextureSize, TriangleList); } } break; @@ -1063,7 +2011,7 @@ void ALandscape::DrawHeightmapComponentsToRenderTarget(const FString& InDebugNam for (ULandscapeComponent* Component : InComponentsToDraw) { FVector2D HeightmapScaleBias(Component->HeightmapScaleBias.Z, Component->HeightmapScaleBias.W); - GenerateHeightmapQuadsAtlasToNonAtlas(Component->GetSectionBase(), HeightmapScaleBias, SubsectionSizeQuads, HeightmapReadTextureSize, HeightmapWriteTextureSize, TriangleList); + GenerateProceduralRenderQuadsAtlasToNonAtlas(Component->GetSectionBase(), HeightmapScaleBias, SubsectionSizeQuads, HeightmapReadTextureSize, HeightmapWriteTextureSize, TriangleList); } } break; @@ -1072,7 +2020,7 @@ void ALandscape::DrawHeightmapComponentsToRenderTarget(const FString& InDebugNam for (ULandscapeComponent* Component : InComponentsToDraw) { FVector2D HeightmapScaleBias(Component->HeightmapScaleBias.Z, Component->HeightmapScaleBias.W); - GenerateHeightmapQuadsNonAtlas(Component->GetSectionBase(), HeightmapScaleBias, SubsectionSizeQuads, HeightmapReadTextureSize, HeightmapWriteTextureSize, TriangleList); + GenerateProceduralRenderQuadsNonAtlas(Component->GetSectionBase(), HeightmapScaleBias, SubsectionSizeQuads, HeightmapReadTextureSize, HeightmapWriteTextureSize, TriangleList); } } break; @@ -1081,7 +2029,7 @@ void ALandscape::DrawHeightmapComponentsToRenderTarget(const FString& InDebugNam for (ULandscapeComponent* Component : InComponentsToDraw) { FVector2D HeightmapScaleBias(Component->HeightmapScaleBias.Z, Component->HeightmapScaleBias.W); - GenerateHeightmapQuadsNonAtlasToAtlas(Component->GetSectionBase(), HeightmapScaleBias, SubsectionSizeQuads, HeightmapReadTextureSize, HeightmapWriteTextureSize, TriangleList); + GenerateProceduralRenderQuadsNonAtlasToAtlas(Component->GetSectionBase(), HeightmapScaleBias, SubsectionSizeQuads, HeightmapReadTextureSize, HeightmapWriteTextureSize, TriangleList); } } break; @@ -1090,7 +2038,7 @@ void ALandscape::DrawHeightmapComponentsToRenderTarget(const FString& InDebugNam for (ULandscapeComponent* Component : InComponentsToDraw) { FVector2D HeightmapScaleBias(Component->HeightmapScaleBias.Z, Component->HeightmapScaleBias.W); - GenerateHeightmapQuadsMip(Component->GetSectionBase(), HeightmapScaleBias, SubsectionSizeQuads, HeightmapReadTextureSize, HeightmapWriteTextureSize, InMipRender, TriangleList); + GenerateProceduralRenderQuadsMip(Component->GetSectionBase(), HeightmapScaleBias, SubsectionSizeQuads, HeightmapReadTextureSize, HeightmapWriteTextureSize, InMipRender, TriangleList); } } break; @@ -1108,25 +2056,25 @@ void ALandscape::DrawHeightmapComponentsToRenderTarget(const FString& InDebugNam if (InMipRender > 0) { - InShaderParams.CurrentMipHeightmapSize = HeightmapWriteTextureSize; - InShaderParams.ParentMipHeightmapSize = HeightmapReadTextureSize; + InShaderParams.CurrentMipSize = HeightmapWriteTextureSize; + InShaderParams.ParentMipSize = HeightmapReadTextureSize; } FMatrix ProjectionMatrix = AdjustProjectionMatrixForRHI(FTranslationMatrix(FVector(0, 0, 0)) * FMatrix(FPlane(1.0f / (FMath::Max(HeightmapWriteTextureSize.X, 1.f) / 2.0f), 0.0, 0.0f, 0.0f), FPlane(0.0f, -1.0f / (FMath::Max(HeightmapWriteTextureSize.Y, 1.f) / 2.0f), 0.0f, 0.0f), FPlane(0.0f, 0.0f, 1.0f, 0.0f), FPlane(-1.0f, 1.0f, 0.0f, 1.0f))); - FLandscapeHeightmapProceduralRender_RenderThread ProceduralRender(InDebugName, InHeightmapRTWrite, HeightmapWriteTextureSize, HeightmapReadTextureSize, ProjectionMatrix, InShaderParams, InMipRender, TriangleList); + LandscapeProceduralHeightmapRender_RenderThread ProceduralRender(InDebugName, InHeightmapRTWrite, HeightmapWriteTextureSize, HeightmapReadTextureSize, ProjectionMatrix, InShaderParams, InMipRender, TriangleList); - ENQUEUE_RENDER_COMMAND(FDrawSceneCommand)( + ENQUEUE_RENDER_COMMAND(FDrawLandscapeProceduralHeightmapCommand)( [ProceduralRender, ClearRT = InClearRTWrite](FRHICommandListImmediate& RHICmdList) mutable { ProceduralRender.Render(RHICmdList, ClearRT); }); - PrintDebugRTHeightmap(InDebugName, InHeightmapRTWrite, InMipRender, InShaderParams.GenerateNormals); + PrintProceduralDebugRT(InDebugName, InHeightmapRTWrite, InMipRender, true, InShaderParams.GenerateNormals); } -void ALandscape::GenerateHeightmapQuad(const FIntPoint& InVertexPosition, const float InVertexSize, const FVector2D& InUVStart, const FVector2D& InUVSize, TArray& OutTriangles) const +void ALandscape::GenerateProceduralRenderQuad(const FIntPoint& InVertexPosition, float InVertexSize, const FVector2D& InUVStart, const FVector2D& InUVSize, TArray& OutTriangles) const { FLandscapeProceduralTriangle Tri1; @@ -1151,15 +2099,16 @@ void ALandscape::GenerateHeightmapQuad(const FIntPoint& InVertexPosition, const OutTriangles.Add(Tri2); } -void ALandscape::GenerateHeightmapQuadsAtlas(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InReadSize, const FIntPoint& InWriteSize, TArray& OutTriangles) const +void ALandscape::GenerateProceduralRenderQuadsAtlas(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InReadSize, const FIntPoint& InWriteSize, TArray& OutTriangles) const { FIntPoint ComponentSectionBase = InSectionBase; FIntPoint UVComponentSectionBase = InSectionBase; - int32 LocalComponentSizeQuad = InSubSectionSizeQuad * NumSubsections; int32 SubsectionSizeVerts = InSubSectionSizeQuad + 1; + int32 LocalComponentSizeQuad = InSubSectionSizeQuad * NumSubsections; + int32 LocalComponentSizeVerts = SubsectionSizeVerts * NumSubsections; - FVector2D HeightmapPositionOffset(FMath::RoundToInt(ComponentSectionBase.X / LocalComponentSizeQuad), FMath::RoundToInt(ComponentSectionBase.Y / LocalComponentSizeQuad)); + FVector2D PositionOffset(FMath::RoundToInt(ComponentSectionBase.X / LocalComponentSizeQuad), FMath::RoundToInt(ComponentSectionBase.Y / LocalComponentSizeQuad)); FVector2D ComponentsPerTexture(FMath::RoundToInt(InWriteSize.X / LocalComponentSizeQuad), FMath::RoundToInt(InWriteSize.Y / LocalComponentSizeQuad)); if (InReadSize.X >= InWriteSize.X) @@ -1168,16 +2117,16 @@ void ALandscape::GenerateHeightmapQuadsAtlas(const FIntPoint& InSectionBase, con { if (ComponentsPerTexture.X > 1.0f) { - UVComponentSectionBase.X = HeightmapPositionOffset.X * (SubsectionSizeVerts * NumSubsections); + UVComponentSectionBase.X = PositionOffset.X * LocalComponentSizeVerts; } else { - UVComponentSectionBase.X -= (UVComponentSectionBase.X + LocalComponentSizeQuad > InWriteSize.X) ? FMath::FloorToInt((HeightmapPositionOffset.X / ComponentsPerTexture.X)) * ComponentsPerTexture.X * LocalComponentSizeQuad : 0; + UVComponentSectionBase.X -= (UVComponentSectionBase.X + LocalComponentSizeQuad > InWriteSize.X) ? FMath::FloorToInt((PositionOffset.X / ComponentsPerTexture.X)) * ComponentsPerTexture.X * LocalComponentSizeQuad : 0; } } - ComponentSectionBase.X -= (ComponentSectionBase.X + LocalComponentSizeQuad > InWriteSize.X) ? FMath::FloorToInt((HeightmapPositionOffset.X / ComponentsPerTexture.X)) * ComponentsPerTexture.X * LocalComponentSizeQuad : 0; - HeightmapPositionOffset.X = ComponentSectionBase.X / LocalComponentSizeQuad; + ComponentSectionBase.X -= (ComponentSectionBase.X + LocalComponentSizeQuad > InWriteSize.X) ? FMath::FloorToInt((PositionOffset.X / ComponentsPerTexture.X)) * ComponentsPerTexture.X * LocalComponentSizeQuad : 0; + PositionOffset.X = ComponentSectionBase.X / LocalComponentSizeQuad; } if (InReadSize.Y >= InWriteSize.Y) @@ -1186,22 +2135,22 @@ void ALandscape::GenerateHeightmapQuadsAtlas(const FIntPoint& InSectionBase, con { if (ComponentsPerTexture.Y > 1.0f) { - UVComponentSectionBase.Y = HeightmapPositionOffset.Y * (SubsectionSizeVerts * NumSubsections); + UVComponentSectionBase.Y = PositionOffset.Y * LocalComponentSizeVerts; } else { - UVComponentSectionBase.Y -= (UVComponentSectionBase.Y + LocalComponentSizeQuad > InWriteSize.Y) ? FMath::FloorToInt((HeightmapPositionOffset.Y / ComponentsPerTexture.Y)) * ComponentsPerTexture.Y * LocalComponentSizeQuad : 0; + UVComponentSectionBase.Y -= (UVComponentSectionBase.Y + LocalComponentSizeQuad > InWriteSize.Y) ? FMath::FloorToInt((PositionOffset.Y / ComponentsPerTexture.Y)) * ComponentsPerTexture.Y * LocalComponentSizeQuad : 0; } } - ComponentSectionBase.Y -= (ComponentSectionBase.Y + LocalComponentSizeQuad > InWriteSize.Y) ? FMath::FloorToInt((HeightmapPositionOffset.Y / ComponentsPerTexture.Y)) * ComponentsPerTexture.Y * LocalComponentSizeQuad : 0; - HeightmapPositionOffset.Y = ComponentSectionBase.Y / LocalComponentSizeQuad; + ComponentSectionBase.Y -= (ComponentSectionBase.Y + LocalComponentSizeQuad > InWriteSize.Y) ? FMath::FloorToInt((PositionOffset.Y / ComponentsPerTexture.Y)) * ComponentsPerTexture.Y * LocalComponentSizeQuad : 0; + PositionOffset.Y = ComponentSectionBase.Y / LocalComponentSizeQuad; } - ComponentSectionBase.X = HeightmapPositionOffset.X * (SubsectionSizeVerts * NumSubsections); - ComponentSectionBase.Y = HeightmapPositionOffset.Y * (SubsectionSizeVerts * NumSubsections); + ComponentSectionBase.X = PositionOffset.X * LocalComponentSizeVerts; + ComponentSectionBase.Y = PositionOffset.Y * LocalComponentSizeVerts; - FVector2D HeightmapUVSize((float)SubsectionSizeVerts / (float)InReadSize.X, (float)SubsectionSizeVerts / (float)InReadSize.Y); + FVector2D UVSize((float)SubsectionSizeVerts / (float)InReadSize.X, (float)SubsectionSizeVerts / (float)InReadSize.Y); FIntPoint SubSectionSectionBase; for (int8 SubY = 0; SubY < NumSubsections; ++SubY) @@ -1211,44 +2160,46 @@ void ALandscape::GenerateHeightmapQuadsAtlas(const FIntPoint& InSectionBase, con SubSectionSectionBase.X = ComponentSectionBase.X + SubsectionSizeVerts * SubX; SubSectionSectionBase.Y = ComponentSectionBase.Y + SubsectionSizeVerts * SubY; - // Offset for this component's data in heightmap texture - FVector2D HeightmapUVStart; + // Offset for this component's data in texture + FVector2D UVStart; if (InReadSize.X >= InWriteSize.X) { - HeightmapUVStart.X = ((float)UVComponentSectionBase.X / (float)InReadSize.X) + HeightmapUVSize.X * (float)SubX; + UVStart.X = ((float)UVComponentSectionBase.X / (float)InReadSize.X) + UVSize.X * (float)SubX; } else { - HeightmapUVStart.X = InScaleBias.X + HeightmapUVSize.X * (float)SubX; + UVStart.X = InScaleBias.X + UVSize.X * (float)SubX; } if (InReadSize.Y >= InWriteSize.Y) { - HeightmapUVStart.Y = ((float)UVComponentSectionBase.Y / (float)InReadSize.Y) + HeightmapUVSize.Y * (float)SubY; + UVStart.Y = ((float)UVComponentSectionBase.Y / (float)InReadSize.Y) + UVSize.Y * (float)SubY; } else { - HeightmapUVStart.Y = InScaleBias.Y + HeightmapUVSize.Y * (float)SubY; + UVStart.Y = InScaleBias.Y + UVSize.Y * (float)SubY; } - GenerateHeightmapQuad(SubSectionSectionBase, SubsectionSizeVerts, HeightmapUVStart, HeightmapUVSize, OutTriangles); + GenerateProceduralRenderQuad(SubSectionSectionBase, SubsectionSizeVerts, UVStart, UVSize, OutTriangles); } } } -void ALandscape::GenerateHeightmapQuadsMip(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InReadSize, const FIntPoint& InWriteSize, int32 CurrentMip, TArray& OutTriangles) const +void ALandscape::GenerateProceduralRenderQuadsMip(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InReadSize, const FIntPoint& InWriteSize, uint8 InCurrentMip, TArray& OutTriangles) const { - int32 LocalComponentSizeQuad = InSubSectionSizeQuad * NumSubsections; int32 SubsectionSizeVerts = InSubSectionSizeQuad + 1; - int32 MipSubsectionSizeVerts = SubsectionSizeVerts >> CurrentMip; - - FVector2D HeightmapPositionOffset(FMath::RoundToInt(InSectionBase.X / LocalComponentSizeQuad), FMath::RoundToInt(InSectionBase.Y / LocalComponentSizeQuad)); + int32 LocalComponentSizeQuad = InSubSectionSizeQuad * NumSubsections; + int32 LocalComponentSizeVerts = SubsectionSizeVerts * NumSubsections; + int32 MipSubsectionSizeVerts = SubsectionSizeVerts >> InCurrentMip; + int32 MipLocalComponentSizeVerts = MipSubsectionSizeVerts * NumSubsections; + + FVector2D PositionOffset(FMath::RoundToInt(InSectionBase.X / LocalComponentSizeQuad), FMath::RoundToInt(InSectionBase.Y / LocalComponentSizeQuad)); FVector2D ComponentsPerTexture(FMath::RoundToInt(InWriteSize.X / LocalComponentSizeQuad), FMath::RoundToInt(InWriteSize.Y / LocalComponentSizeQuad)); - FIntPoint ComponentSectionBase(HeightmapPositionOffset.X * (MipSubsectionSizeVerts * NumSubsections), HeightmapPositionOffset.Y * (MipSubsectionSizeVerts * NumSubsections)); - FIntPoint UVComponentSectionBase(HeightmapPositionOffset.X * (SubsectionSizeVerts * NumSubsections), HeightmapPositionOffset.Y * (SubsectionSizeVerts * NumSubsections)); - FVector2D HeightmapUVSize((float)(SubsectionSizeVerts >> (CurrentMip - 1)) / (float)InReadSize.X, (float)(SubsectionSizeVerts >> (CurrentMip - 1)) / (float)InReadSize.Y); + FIntPoint ComponentSectionBase(PositionOffset.X * MipLocalComponentSizeVerts, PositionOffset.Y * MipLocalComponentSizeVerts); + FIntPoint UVComponentSectionBase(PositionOffset.X * LocalComponentSizeVerts, PositionOffset.Y * LocalComponentSizeVerts); + FVector2D UVSize((float)(SubsectionSizeVerts >> (InCurrentMip - 1)) / (float)InReadSize.X, (float)(SubsectionSizeVerts >> (InCurrentMip - 1)) / (float)InReadSize.Y); FIntPoint SubSectionSectionBase; for (int8 SubY = 0; SubY < NumSubsections; ++SubY) @@ -1258,32 +2209,25 @@ void ALandscape::GenerateHeightmapQuadsMip(const FIntPoint& InSectionBase, const SubSectionSectionBase.X = ComponentSectionBase.X + MipSubsectionSizeVerts * SubX; SubSectionSectionBase.Y = ComponentSectionBase.Y + MipSubsectionSizeVerts * SubY; - // Offset for this component's data in heightmap texture - FVector2D HeightmapUVStart; - HeightmapUVStart.X = ((float)(UVComponentSectionBase.X >> (CurrentMip - 1)) / (float)InReadSize.X) + HeightmapUVSize.X * (float)SubX; - HeightmapUVStart.Y = ((float)(UVComponentSectionBase.Y >> (CurrentMip - 1)) / (float)InReadSize.Y) + HeightmapUVSize.Y * (float)SubY; + // Offset for this component's data in texture + FVector2D UVStart(((float)(UVComponentSectionBase.X >> (InCurrentMip - 1)) / (float)InReadSize.X) + UVSize.X * (float)SubX, ((float)(UVComponentSectionBase.Y >> (InCurrentMip - 1)) / (float)InReadSize.Y) + UVSize.Y * (float)SubY); - GenerateHeightmapQuad(SubSectionSectionBase, MipSubsectionSizeVerts, HeightmapUVStart, HeightmapUVSize, OutTriangles); + GenerateProceduralRenderQuad(SubSectionSectionBase, MipSubsectionSizeVerts, UVStart, UVSize, OutTriangles); } } } -void ALandscape::GenerateHeightmapQuadsAtlasToNonAtlas(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InHeightmapReadTextureSize, const FIntPoint& InHeightmapWriteTextureSize, TArray& OutTriangles) const +void ALandscape::GenerateProceduralRenderQuadsAtlasToNonAtlas(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InReadSize, const FIntPoint& InWriteSize, TArray& OutTriangles) const { - FIntPoint ComponentSectionBase = InSectionBase; - int32 LocalComponentSizeQuad = InSubSectionSizeQuad * NumSubsections; - int32 HeightmapPositionOffsetX = ComponentSectionBase.X / LocalComponentSizeQuad; - int32 HeightmapPositionOffsetY = ComponentSectionBase.Y / LocalComponentSizeQuad; int32 SubsectionSizeVerts = InSubSectionSizeQuad + 1; + int32 LocalComponentSizeQuad = InSubSectionSizeQuad * NumSubsections; + int32 LocalComponentSizeVerts = SubsectionSizeVerts * NumSubsections; - FIntPoint UVComponentSectionBase = InSectionBase; - UVComponentSectionBase.X = HeightmapPositionOffsetX * (SubsectionSizeVerts * NumSubsections); - UVComponentSectionBase.Y = HeightmapPositionOffsetY * (SubsectionSizeVerts * NumSubsections); + FVector2D PositionOffset(FMath::RoundToInt(InSectionBase.X / LocalComponentSizeQuad), FMath::RoundToInt(InSectionBase.Y / LocalComponentSizeQuad)); + FIntPoint ComponentSectionBase(PositionOffset.X * LocalComponentSizeQuad, PositionOffset.Y * LocalComponentSizeQuad); + FIntPoint UVComponentSectionBase(PositionOffset.X * SubsectionSizeVerts, PositionOffset.Y * SubsectionSizeVerts); + FVector2D UVSize((float)SubsectionSizeVerts / (float)InReadSize.X, (float)SubsectionSizeVerts / (float)InReadSize.Y); - ComponentSectionBase.X = HeightmapPositionOffsetX * (InSubSectionSizeQuad * NumSubsections); - ComponentSectionBase.Y = HeightmapPositionOffsetY * (InSubSectionSizeQuad * NumSubsections); - - FVector2D HeightmapUVSize((float)SubsectionSizeVerts / (float)InHeightmapReadTextureSize.X, (float)SubsectionSizeVerts / (float)InHeightmapReadTextureSize.Y); FIntPoint SubSectionSectionBase; for (int8 SubY = 0; SubY < NumSubsections; ++SubY) @@ -1293,48 +2237,44 @@ void ALandscape::GenerateHeightmapQuadsAtlasToNonAtlas(const FIntPoint& InSectio SubSectionSectionBase.X = ComponentSectionBase.X + InSubSectionSizeQuad * SubX; SubSectionSectionBase.Y = ComponentSectionBase.Y + InSubSectionSizeQuad * SubY; - // Offset for this component's data in heightmap texture - FVector2D HeightmapUVStart; + // Offset for this component's data in texture + FVector2D UVStart; - if (InHeightmapReadTextureSize.X >= InHeightmapWriteTextureSize.X) + if (InReadSize.X >= InWriteSize.X) { - HeightmapUVStart.X = ((float)UVComponentSectionBase.X / (float)InHeightmapReadTextureSize.X) + HeightmapUVSize.X * (float)SubX; + UVStart.X = ((float)UVComponentSectionBase.X / (float)InReadSize.X) + UVSize.X * (float)SubX; } else { - HeightmapUVStart.X = InScaleBias.X + HeightmapUVSize.X * (float)SubX; + UVStart.X = InScaleBias.X + UVSize.X * (float)SubX; } - if (InHeightmapReadTextureSize.Y >= InHeightmapWriteTextureSize.Y) + if (InReadSize.Y >= InWriteSize.Y) { - HeightmapUVStart.Y = ((float)UVComponentSectionBase.Y / (float)InHeightmapReadTextureSize.Y) + HeightmapUVSize.Y * (float)SubY; + UVStart.Y = ((float)UVComponentSectionBase.Y / (float)InReadSize.Y) + UVSize.Y * (float)SubY; } else { - HeightmapUVStart.Y = InScaleBias.Y + HeightmapUVSize.Y * (float)SubY; + UVStart.Y = InScaleBias.Y + UVSize.Y * (float)SubY; } - GenerateHeightmapQuad(SubSectionSectionBase, SubsectionSizeVerts, HeightmapUVStart, HeightmapUVSize, OutTriangles); + GenerateProceduralRenderQuad(SubSectionSectionBase, SubsectionSizeVerts, UVStart, UVSize, OutTriangles); } } } -void ALandscape::GenerateHeightmapQuadsNonAtlas(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InHeightmapReadTextureSize, const FIntPoint& InHeightmapWriteTextureSize, TArray& OutTriangles) const +void ALandscape::GenerateProceduralRenderQuadsNonAtlas(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InReadSize, const FIntPoint& InWriteSize, TArray& OutTriangles) const { // We currently only support drawing in non atlas mode with the same texture size - check(InHeightmapReadTextureSize.X == InHeightmapWriteTextureSize.X && InHeightmapReadTextureSize.Y == InHeightmapWriteTextureSize.Y); + check(InReadSize.X == InWriteSize.X && InReadSize.Y == InWriteSize.Y); - FIntPoint ComponentSectionBase = InSectionBase; - int32 LocalComponentSizeQuad = InSubSectionSizeQuad * NumSubsections; - int32 HeightmapPositionOffsetX = ComponentSectionBase.X / LocalComponentSizeQuad; - int32 HeightmapPositionOffsetY = ComponentSectionBase.Y / LocalComponentSizeQuad; int32 SubsectionSizeVerts = InSubSectionSizeQuad + 1; + int32 LocalComponentSizeQuad = InSubSectionSizeQuad * NumSubsections; - FIntPoint UVComponentSectionBase = InSectionBase; - UVComponentSectionBase.X = HeightmapPositionOffsetX * (InSubSectionSizeQuad * NumSubsections); - UVComponentSectionBase.Y = HeightmapPositionOffsetY * (InSubSectionSizeQuad * NumSubsections); - - FVector2D HeightmapUVSize((float)SubsectionSizeVerts / (float)InHeightmapReadTextureSize.X, (float)SubsectionSizeVerts / (float)InHeightmapReadTextureSize.Y); + FVector2D PositionOffset(FMath::RoundToInt(InSectionBase.X / LocalComponentSizeQuad), FMath::RoundToInt(InSectionBase.Y / LocalComponentSizeQuad)); + FIntPoint ComponentSectionBase = InSectionBase; + FIntPoint UVComponentSectionBase(PositionOffset.X * LocalComponentSizeQuad, PositionOffset.Y * LocalComponentSizeQuad); + FVector2D UVSize((float)SubsectionSizeVerts / (float)InReadSize.X, (float)SubsectionSizeVerts / (float)InReadSize.Y); FIntPoint SubSectionSectionBase; for (int8 SubY = 0; SubY < NumSubsections; ++SubY) @@ -1344,25 +2284,23 @@ void ALandscape::GenerateHeightmapQuadsNonAtlas(const FIntPoint& InSectionBase, SubSectionSectionBase.X = ComponentSectionBase.X + InSubSectionSizeQuad * SubX; SubSectionSectionBase.Y = ComponentSectionBase.Y + InSubSectionSizeQuad * SubY; - // Offset for this component's data in heightmap texture - FVector2D HeightmapUVStart(((float)UVComponentSectionBase.X / (float)InHeightmapReadTextureSize.X) + HeightmapUVSize.X * (float)SubX, ((float)UVComponentSectionBase.Y / (float)InHeightmapReadTextureSize.Y) + HeightmapUVSize.Y * (float)SubY); - GenerateHeightmapQuad(SubSectionSectionBase, SubsectionSizeVerts, HeightmapUVStart, HeightmapUVSize, OutTriangles); + // Offset for this component's data in texture + FVector2D UVStart(((float)UVComponentSectionBase.X / (float)InReadSize.X) + UVSize.X * (float)SubX, ((float)UVComponentSectionBase.Y / (float)InReadSize.Y) + UVSize.Y * (float)SubY); + GenerateProceduralRenderQuad(SubSectionSectionBase, SubsectionSizeVerts, UVStart, UVSize, OutTriangles); } } } -void ALandscape::GenerateHeightmapQuadsNonAtlasToAtlas(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InHeightmapReadTextureSize, const FIntPoint& InHeightmapWriteTextureSize, TArray& OutTriangles) const +void ALandscape::GenerateProceduralRenderQuadsNonAtlasToAtlas(const FIntPoint& InSectionBase, const FVector2D& InScaleBias, float InSubSectionSizeQuad, const FIntPoint& InReadSize, const FIntPoint& InWriteSize, TArray& OutTriangles) const { - FIntPoint ComponentSectionBase = InSectionBase; - int32 LocalComponentSizeQuad = InSubSectionSizeQuad * NumSubsections; - int32 HeightmapPositionOffsetX = ComponentSectionBase.X / LocalComponentSizeQuad; - int32 HeightmapPositionOffsetY = ComponentSectionBase.Y / LocalComponentSizeQuad; int32 SubsectionSizeVerts = InSubSectionSizeQuad + 1; + int32 LocalComponentSizeQuad = InSubSectionSizeQuad * NumSubsections; + int32 LocalComponentSizeVerts = SubsectionSizeVerts * NumSubsections; - ComponentSectionBase.X = HeightmapPositionOffsetX * (SubsectionSizeVerts * NumSubsections); - ComponentSectionBase.Y = HeightmapPositionOffsetY * (SubsectionSizeVerts * NumSubsections); + FVector2D PositionOffset(FMath::RoundToInt(InSectionBase.X / LocalComponentSizeQuad), FMath::RoundToInt(InSectionBase.Y / LocalComponentSizeQuad)); + FIntPoint ComponentSectionBase(PositionOffset.X * LocalComponentSizeVerts, PositionOffset.Y * LocalComponentSizeVerts); + FVector2D UVSize((float)SubsectionSizeVerts / (float)InReadSize.X, (float)SubsectionSizeVerts / (float)InReadSize.Y); - FVector2D HeightmapUVSize((float)SubsectionSizeVerts / (float)InHeightmapReadTextureSize.X, (float)SubsectionSizeVerts / (float)InHeightmapReadTextureSize.Y); FIntPoint SubSectionSectionBase; for (int8 SubY = 0; SubY < NumSubsections; ++SubY) @@ -1372,17 +2310,17 @@ void ALandscape::GenerateHeightmapQuadsNonAtlasToAtlas(const FIntPoint& InSectio SubSectionSectionBase.X = ComponentSectionBase.X + SubsectionSizeVerts * SubX; SubSectionSectionBase.Y = ComponentSectionBase.Y + SubsectionSizeVerts * SubY; - // Offset for this component's data in heightmap texture - float HeightmapScaleBiasZ = (float)InSectionBase.X / (float)InHeightmapReadTextureSize.X; - float HeightmapScaleBiasW = (float)InSectionBase.Y / (float)InHeightmapReadTextureSize.Y; - FVector2D HeightmapUVStart(HeightmapScaleBiasZ + ((float)InSubSectionSizeQuad / (float)InHeightmapReadTextureSize.X) * (float)SubX, HeightmapScaleBiasW + ((float)InSubSectionSizeQuad / (float)InHeightmapReadTextureSize.Y) * (float)SubY); + // Offset for this component's data in texture + float ScaleBiasZ = (float)InSectionBase.X / (float)InReadSize.X; + float ScaleBiasW = (float)InSectionBase.Y / (float)InReadSize.Y; + FVector2D UVStart(ScaleBiasZ + ((float)InSubSectionSizeQuad / (float)InReadSize.X) * (float)SubX, ScaleBiasW + ((float)InSubSectionSizeQuad / (float)InReadSize.Y) * (float)SubY); - GenerateHeightmapQuad(SubSectionSectionBase, SubsectionSizeVerts, HeightmapUVStart, HeightmapUVSize, OutTriangles); + GenerateProceduralRenderQuad(SubSectionSectionBase, SubsectionSizeVerts, UVStart, UVSize, OutTriangles); } } } -void ALandscape::PrintDebugHeightData(const FString& InContext, const TArray& InHeightmapData, const FIntPoint& InDataSize, int32 InMipRender, bool InOutputNormals) const +void ALandscape::PrintProceduralDebugHeightData(const FString& InContext, const TArray& InHeightmapData, const FIntPoint& InDataSize, uint8 InMipRender, bool InOutputNormals) const { bool DisplayDebugPrint = CVarOutputProceduralRTContent.GetValueOnAnyThread() == 1 ? true : false; bool DisplayHeightAsDelta = false; @@ -1484,9 +2422,47 @@ void ALandscape::PrintDebugHeightData(const FString& InContext, const TArray& InWeightmapData, const FIntPoint& InDataSize, uint8 InMipRender) const { - bool DisplayDebugPrint = CVarOutputProceduralRTContent.GetValueOnAnyThread() == 1 ? true : false; + bool DisplayDebugPrint = (CVarOutputProceduralRTContent.GetValueOnAnyThread() == 1 || CVarOutputProceduralWeightmapsRTContent.GetValueOnAnyThread() == 1) ? true : false; + + if (!DisplayDebugPrint) + { + return; + } + + UE_LOG(LogLandscapeBP, Display, TEXT("Context: %s"), *InContext); + + int32 MipSize = ((SubsectionSizeQuads + 1) >> InMipRender); + + for (int32 Y = 0; Y < InDataSize.Y; ++Y) + { + FString WeightmapOutput; + + for (int32 X = 0; X < InDataSize.X; ++X) + { + const FColor& Weight = InWeightmapData[X + Y * InDataSize.X]; + + if (X > 0 && MipSize > 0 && X % MipSize == 0) + { + WeightmapOutput += FString::Printf(TEXT(" ")); + } + + WeightmapOutput += FString::Printf(TEXT("%s "), *Weight.ToString()); + } + + if (Y > 0 && MipSize > 0 && Y % MipSize == 0) + { + UE_LOG(LogLandscapeBP, Display, TEXT("")); + } + + UE_LOG(LogLandscapeBP, Display, TEXT("%s"), *WeightmapOutput); + } +} + +void ALandscape::PrintProceduralDebugRT(const FString& InContext, UTextureRenderTarget2D* InDebugRT, uint8 InMipRender, bool InOutputHeight, bool InOutputNormals) const +{ + bool DisplayDebugPrint = (CVarOutputProceduralRTContent.GetValueOnAnyThread() == 1 || CVarOutputProceduralWeightmapsRTContent.GetValueOnAnyThread() == 1) ? true : false; if (!DisplayDebugPrint) { @@ -1494,8 +2470,8 @@ void ALandscape::PrintDebugRTHeightmap(FString Context, UTextureRenderTarget2D* } FTextureRenderTargetResource* RenderTargetResource = InDebugRT->GameThread_GetRenderTargetResource(); - ENQUEUE_RENDER_COMMAND(HeightmapRTCanvasRenderTargetResolveCommand)( - [RenderTargetResource](FRHICommandListImmediate& RHICmdList) + ENQUEUE_RENDER_COMMAND(FProceduralDebugRenderTargetResolveCommand)( + [RenderTargetResource](FRHICommandListImmediate& RHICmdList) mutable { // Copy (resolve) the rendered image from the frame buffer to its render target texture RHICmdList.CopyToResolveTarget(RenderTargetResource->GetRenderTargetTexture(), RenderTargetResource->TextureRHI, FResolveParams()); @@ -1509,12 +2485,96 @@ void ALandscape::PrintDebugRTHeightmap(FString Context, UTextureRenderTarget2D* FReadSurfaceDataFlags Flags(RCM_UNorm, CubeFace_MAX); - TArray OutputRTHeightmap; - OutputRTHeightmap.Reserve(SampleRect.Width() * SampleRect.Height()); + TArray OutputRT; + OutputRT.Reserve(SampleRect.Width() * SampleRect.Height()); - InDebugRT->GameThread_GetRenderTargetResource()->ReadPixels(OutputRTHeightmap, Flags, SampleRect); + InDebugRT->GameThread_GetRenderTargetResource()->ReadPixels(OutputRT, Flags, SampleRect); - PrintDebugHeightData(Context, OutputRTHeightmap, FIntPoint(SampleRect.Width(), SampleRect.Height()), InMipRender, InOutputNormals); + if (InOutputHeight) + { + PrintProceduralDebugHeightData(InContext, OutputRT, FIntPoint(SampleRect.Width(), SampleRect.Height()), InMipRender, InOutputNormals); + } + else + { + PrintProceduralDebugWeightData(InContext, OutputRT, FIntPoint(SampleRect.Width(), SampleRect.Height()), InMipRender); + } +} + +void ALandscape::PrintProceduralDebugTextureResource(const FString& InContext, FTextureResource* InTextureResource, uint8 InMipRender, bool InOutputHeight, bool InOutputNormals) const +{ + bool DisplayDebugPrint = (CVarOutputProceduralRTContent.GetValueOnAnyThread() == 1 || CVarOutputProceduralWeightmapsRTContent.GetValueOnAnyThread() == 1) ? true : false; + + if (!DisplayDebugPrint) + { + return; + } + + int32 MinX, MinY, MaxX, MaxY; + const ULandscapeInfo* LandscapeInfo = GetLandscapeInfo(); + LandscapeInfo->GetLandscapeExtent(MinX, MinY, MaxX, MaxY); + FIntRect SampleRect = FIntRect(0, 0, InTextureResource->GetSizeX(), InTextureResource->GetSizeY()); + + TArray OutputTexels; + OutputTexels.Reserve(SampleRect.Width() * SampleRect.Height()); + + FReadSurfaceDataFlags Flags(RCM_UNorm, CubeFace_MAX); + Flags.SetMip(InMipRender); + + ENQUEUE_RENDER_COMMAND(FProceduralDebugReadSurfaceCommand)( + [SourceTextureRHI = InTextureResource->TextureRHI, SampleRect = SampleRect, OutData = &OutputTexels, ReadFlags = Flags](FRHICommandListImmediate& RHICmdList) mutable + { + RHICmdList.ReadSurfaceData(SourceTextureRHI, SampleRect, *OutData, ReadFlags); + }); + + FlushRenderingCommands(); + + if (InOutputHeight) + { + PrintProceduralDebugHeightData(InContext, OutputTexels, FIntPoint(SampleRect.Width(), SampleRect.Height()), InMipRender, InOutputNormals); + } + else + { + PrintProceduralDebugWeightData(InContext, OutputTexels, FIntPoint(SampleRect.Width(), SampleRect.Height()), InMipRender); + } +} + +bool ALandscape::AreHeightmapTextureResourcesReady(const TArray& InAllLandscapes) const +{ + for (ALandscapeProxy* Landscape : InAllLandscapes) + { + for (auto& ItLayerDataPair : Landscape->ProceduralLayersData) + { + for (auto& ItHeightmapPair : ItLayerDataPair.Value.Heightmaps) + { + UTexture2D* OriginalHeightmap = ItHeightmapPair.Key; + UTexture2D* LayerHeightmap = ItHeightmapPair.Value; + + if (!LayerHeightmap->IsAsyncCacheComplete() || !OriginalHeightmap->IsFullyStreamedIn()) + { + return false; + } + + if (LayerHeightmap->Resource == nullptr) + { + LayerHeightmap->FinishCachePlatformData(); + + LayerHeightmap->Resource = LayerHeightmap->CreateResource(); + + if (LayerHeightmap->Resource != nullptr) + { + BeginInitResource(LayerHeightmap->Resource); + } + } + + if (LayerHeightmap->Resource == nullptr || !LayerHeightmap->Resource->IsInitialized() || !LayerHeightmap->IsFullyStreamedIn()) + { + return false; + } + } + } + } + + return true; } void ALandscape::RegenerateProceduralHeightmaps() @@ -1536,37 +2596,9 @@ void ALandscape::RegenerateProceduralHeightmaps() AllLandscapes.Add(It); } - for (ALandscapeProxy* Landscape : AllLandscapes) + if (!AreHeightmapTextureResourcesReady(AllLandscapes)) { - for (auto& ItLayerDataPair : Landscape->ProceduralLayersData) - { - for (auto& ItHeightmapPair : ItLayerDataPair.Value.Heightmaps) - { - UTexture2D* OriginalHeightmap = ItHeightmapPair.Key; - UTexture2D* LayerHeightmap = ItHeightmapPair.Value; - - if (!LayerHeightmap->IsAsyncCacheComplete() || !OriginalHeightmap->IsFullyStreamedIn()) - { - return; - } - - if (LayerHeightmap->Resource == nullptr) - { - LayerHeightmap->FinishCachePlatformData(); - - LayerHeightmap->Resource = LayerHeightmap->CreateResource(); - if (LayerHeightmap->Resource) - { - BeginInitResource(LayerHeightmap->Resource); - } - } - - if (!LayerHeightmap->Resource->IsInitialized() || !LayerHeightmap->IsFullyStreamedIn()) - { - return; - } - } - } + return; } TArray AllLandscapeComponents; @@ -1581,24 +2613,24 @@ void ALandscape::RegenerateProceduralHeightmaps() FLandscapeHeightmapProceduralShaderParameters ShaderParams; bool FirstLayer = true; - UTextureRenderTarget2D* CombinedHeightmapAtlasRT = HeightmapRTList[EHeightmapRTType::LandscapeSizeCombinedAtlas]; - UTextureRenderTarget2D* CombinedHeightmapNonAtlasRT = HeightmapRTList[EHeightmapRTType::LandscapeSizeCombinedNonAtlas]; - UTextureRenderTarget2D* LandscapeScratchRT1 = HeightmapRTList[EHeightmapRTType::LandscapeSizeScratch1]; - UTextureRenderTarget2D* LandscapeScratchRT2 = HeightmapRTList[EHeightmapRTType::LandscapeSizeScratch2]; - UTextureRenderTarget2D* LandscapeScratchRT3 = HeightmapRTList[EHeightmapRTType::LandscapeSizeScratch3]; + UTextureRenderTarget2D* CombinedHeightmapAtlasRT = HeightmapRTList[EHeightmapRTType::HeightmapRT_CombinedAtlas]; + UTextureRenderTarget2D* CombinedHeightmapNonAtlasRT = HeightmapRTList[EHeightmapRTType::HeightmapRT_CombinedNonAtlas]; + UTextureRenderTarget2D* LandscapeScratchRT1 = HeightmapRTList[EHeightmapRTType::HeightmapRT_Scratch1]; + UTextureRenderTarget2D* LandscapeScratchRT2 = HeightmapRTList[EHeightmapRTType::HeightmapRT_Scratch2]; + UTextureRenderTarget2D* LandscapeScratchRT3 = HeightmapRTList[EHeightmapRTType::HeightmapRT_Scratch3]; - bool OutputDebugName = CVarOutputProceduralDebugDrawCallName.GetValueOnAnyThread() == 1 || CVarOutputProceduralRTContent.GetValueOnAnyThread() == 1 ? true : false; + bool OutputDebugName = (CVarOutputProceduralDebugDrawCallName.GetValueOnAnyThread() == 1 || CVarOutputProceduralRTContent.GetValueOnAnyThread() == 1) ? true : false; for (FProceduralLayer& Layer : ProceduralLayers) { //Draw Layer heightmap to Combined RT Atlas ShaderParams.ApplyLayerModifiers = true; - ShaderParams.LayerVisible = Layer.Visible; - ShaderParams.LayerWeight = Layer.Weight; + ShaderParams.LayerVisible = Layer.bVisible; + ShaderParams.LayerAlpha = Layer.HeightmapAlpha; for (ALandscapeProxy* Landscape : AllLandscapes) { - FProceduralLayerData* LayerData = Landscape->ProceduralLayersData.Find(Layer.Name); + FProceduralLayerData* LayerData = Landscape->ProceduralLayersData.Find(Layer.Guid); if (LayerData != nullptr) { @@ -1607,9 +2639,9 @@ void ALandscape::RegenerateProceduralHeightmaps() FRenderDataPerHeightmap& HeightmapRenderData = *Landscape->RenderDataPerHeightmap.Find(ItPair.Key); UTexture2D* Heightmap = ItPair.Value; - CopyProceduralTargetToResolveTarget(Heightmap, LandscapeScratchRT1, nullptr, HeightmapRenderData.TopLeftSectionBase, 0); + CopyProceduralTexture(Heightmap, LandscapeScratchRT1, nullptr, HeightmapRenderData.TopLeftSectionBase); - PrintDebugRTHeightmap(OutputDebugName ? FString::Printf(TEXT("LS Height: %s Component %s += -> CombinedAtlas %s"), *Layer.Name.ToString(), *Heightmap->GetName(), *LandscapeScratchRT1->GetName()) : TEXT(""), LandscapeScratchRT1); + PrintProceduralDebugRT(OutputDebugName ? FString::Printf(TEXT("LS Height: %s Component %s += -> CombinedAtlas %s"), *Layer.Name.ToString(), *Heightmap->GetName(), *LandscapeScratchRT1->GetName()) : TEXT(""), LandscapeScratchRT1); } } } @@ -1624,13 +2656,13 @@ void ALandscape::RegenerateProceduralHeightmaps() ShaderParams.ApplyLayerModifiers = false; - if (Layer.Visible) + if (Layer.bVisible) { // Draw each Combined RT into a Non Atlas RT format to be use as base for all brush rendering if (Layer.Brushes.Num() > 0) { - CopyProceduralTargetToResolveTarget(CombinedHeightmapNonAtlasRT, LandscapeScratchRT1, nullptr, FIntPoint(0,0), 0); - PrintDebugRTHeightmap(OutputDebugName ? FString::Printf(TEXT("LS Height: %s Component %s += -> CombinedNonAtlas %s"), *Layer.Name.ToString(), *CombinedHeightmapNonAtlasRT->GetName(), *LandscapeScratchRT1->GetName()) : TEXT(""), LandscapeScratchRT1); + CopyProceduralTexture(CombinedHeightmapNonAtlasRT, LandscapeScratchRT1); + PrintProceduralDebugRT(OutputDebugName ? FString::Printf(TEXT("LS Height: %s Component %s += -> CombinedNonAtlas %s"), *Layer.Name.ToString(), *CombinedHeightmapNonAtlasRT->GetName(), *LandscapeScratchRT1->GetName()) : TEXT(""), LandscapeScratchRT1); } // Draw each brushes @@ -1641,13 +2673,11 @@ void ALandscape::RegenerateProceduralHeightmaps() FLandscapeProceduralLayerBrush& Brush = Layer.Brushes[Layer.HeightmapBrushOrderIndices[i]]; - if (Brush.BPCustomBrush == nullptr) + if (Brush.BPCustomBrush == nullptr || !Brush.BPCustomBrush->IsAffectingHeightmap()) { continue; } - check(Brush.BPCustomBrush->IsAffectingHeightmap()); - if (!Brush.IsInitialized()) { Brush.Initialize(GetBoundingRect(), FIntPoint(CombinedHeightmapNonAtlasRT->SizeX, CombinedHeightmapNonAtlasRT->SizeY)); @@ -1660,18 +2690,18 @@ void ALandscape::RegenerateProceduralHeightmaps() continue; } - INC_DWORD_STAT(STAT_LandscapeRegenerateProceduralHeightmapsDrawCalls); // Brush Render + INC_DWORD_STAT(STAT_LandscapeRegenerateProceduralDrawCalls); // Brush Render - PrintDebugRTHeightmap(OutputDebugName ? FString::Printf(TEXT("LS Height: %s %s -> BrushNonAtlas %s"), *Layer.Name.ToString(), *Brush.BPCustomBrush->GetName(), *BrushOutputNonAtlasRT->GetName()) : TEXT(""), BrushOutputNonAtlasRT); + PrintProceduralDebugRT(OutputDebugName ? FString::Printf(TEXT("LS Height: %s %s -> BrushNonAtlas %s"), *Layer.Name.ToString(), *Brush.BPCustomBrush->GetName(), *BrushOutputNonAtlasRT->GetName()) : TEXT(""), BrushOutputNonAtlasRT); // Resolve back to Combined heightmap - CopyProceduralTargetToResolveTarget(BrushOutputNonAtlasRT, CombinedHeightmapNonAtlasRT, nullptr, FIntPoint(0, 0), 0); - PrintDebugRTHeightmap(OutputDebugName ? FString::Printf(TEXT("LS Height: %s Component %s += -> CombinedNonAtlas %s"), *Layer.Name.ToString(), *BrushOutputNonAtlasRT->GetName(), *CombinedHeightmapNonAtlasRT->GetName()) : TEXT(""), CombinedHeightmapNonAtlasRT); + CopyProceduralTexture(BrushOutputNonAtlasRT, CombinedHeightmapNonAtlasRT); + PrintProceduralDebugRT(OutputDebugName ? FString::Printf(TEXT("LS Height: %s Component %s += -> CombinedNonAtlas %s"), *Layer.Name.ToString(), *BrushOutputNonAtlasRT->GetName(), *CombinedHeightmapNonAtlasRT->GetName()) : TEXT(""), CombinedHeightmapNonAtlasRT); } } - CopyProceduralTargetToResolveTarget(CombinedHeightmapNonAtlasRT, LandscapeScratchRT3, nullptr, FIntPoint(0, 0), 0); - PrintDebugRTHeightmap(OutputDebugName ? FString::Printf(TEXT("LS Height: %s Component %s += -> CombinedNonAtlas %s"), *Layer.Name.ToString(), *CombinedHeightmapNonAtlasRT->GetName(), *LandscapeScratchRT3->GetName()) : TEXT(""), LandscapeScratchRT3); + CopyProceduralTexture(CombinedHeightmapNonAtlasRT, LandscapeScratchRT3); + PrintProceduralDebugRT(OutputDebugName ? FString::Printf(TEXT("LS Height: %s Component %s += -> CombinedNonAtlas %s"), *Layer.Name.ToString(), *CombinedHeightmapNonAtlasRT->GetName(), *LandscapeScratchRT3->GetName()) : TEXT(""), LandscapeScratchRT3); FirstLayer = false; } @@ -1697,22 +2727,24 @@ void ALandscape::RegenerateProceduralHeightmaps() int32 CurrentMip = 0; FRenderDataPerHeightmap& HeightmapRenderData = ItPair.Value; - CopyProceduralTargetToResolveTarget(CombinedHeightmapAtlasRT, HeightmapRenderData.OriginalHeightmap, HeightmapRenderData.HeightmapsCPUReadBack, HeightmapRenderData.TopLeftSectionBase, CurrentMip++); + CopyProceduralTexture(CombinedHeightmapAtlasRT, HeightmapRenderData.OriginalHeightmap, HeightmapRenderData.HeightmapsCPUReadBack, HeightmapRenderData.TopLeftSectionBase, CurrentMip, CurrentMip); + ++CurrentMip; - for (int32 MipRTIndex = EHeightmapRTType::LandscapeSizeMip1; MipRTIndex < EHeightmapRTType::Count; ++MipRTIndex) + for (int32 MipRTIndex = EHeightmapRTType::HeightmapRT_Mip1; MipRTIndex < EHeightmapRTType::HeightmapRT_Count; ++MipRTIndex) { if (HeightmapRTList[MipRTIndex] != nullptr) { - CopyProceduralTargetToResolveTarget(HeightmapRTList[MipRTIndex], HeightmapRenderData.OriginalHeightmap, HeightmapRenderData.HeightmapsCPUReadBack, HeightmapRenderData.TopLeftSectionBase, CurrentMip++); + CopyProceduralTexture(HeightmapRTList[MipRTIndex], HeightmapRenderData.OriginalHeightmap, HeightmapRenderData.HeightmapsCPUReadBack, HeightmapRenderData.TopLeftSectionBase, CurrentMip, CurrentMip); + ++CurrentMip; } } } } } - if ((ProceduralContentUpdateFlags & EProceduralContentUpdateFlag::Heightmap_ResolveToTexture) != 0 || (ProceduralContentUpdateFlags & EProceduralContentUpdateFlag::Heightmap_ResolveToTextureDDC) != 0) + if ((ProceduralContentUpdateFlags & EProceduralContentUpdateFlag::Heightmap_ResolveToTexture) != 0) { - ResolveProceduralHeightmapTexture((ProceduralContentUpdateFlags & EProceduralContentUpdateFlag::Heightmap_ResolveToTextureDDC) != 0); + ResolveProceduralHeightmapTexture(AllLandscapes); } if ((ProceduralContentUpdateFlags & EProceduralContentUpdateFlag::Heightmap_BoundsAndCollision) != 0) @@ -1726,32 +2758,20 @@ void ALandscape::RegenerateProceduralHeightmaps() } } - ProceduralContentUpdateFlags = 0; + ProceduralContentUpdateFlags &= ~EProceduralContentUpdateFlag::Heightmap_All; // If doing rendering debug, keep doing the render only if (CVarOutputProceduralDebugDrawCallName.GetValueOnAnyThread() == 1) { - ProceduralContentUpdateFlags = EProceduralContentUpdateFlag::Heightmap_Render; + ProceduralContentUpdateFlags |= EProceduralContentUpdateFlag::Heightmap_Render; } } -void ALandscape::ResolveProceduralHeightmapTexture(bool InUpdateDDC) +void ALandscape::ResolveProceduralHeightmapTexture(const TArray& InAllLandscapes) { SCOPE_CYCLE_COUNTER(STAT_LandscapeResolveProceduralHeightmap); - ULandscapeInfo* Info = GetLandscapeInfo(); - - TArray AllLandscapes; - AllLandscapes.Add(this); - - for (auto& It : Info->Proxies) - { - AllLandscapes.Add(It); - } - - TArray PendingDDCUpdateTextureList; - - for (ALandscapeProxy* Landscape : AllLandscapes) + for (ALandscapeProxy* Landscape : InAllLandscapes) { TArray> MipData; @@ -1764,88 +2784,1319 @@ void ALandscape::ResolveProceduralHeightmapTexture(bool InUpdateDDC) continue; } - if (MipData.Num() == 0) - { - MipData.AddDefaulted(HeightmapRenderData.HeightmapsCPUReadBack->TextureRHI->GetNumMips()); - } - - int32 MipSizeU = HeightmapRenderData.HeightmapsCPUReadBack->GetSizeX(); - int32 MipSizeV = HeightmapRenderData.HeightmapsCPUReadBack->GetSizeY(); - int32 MipIndex = 0; - - while (MipSizeU >= 1 && MipSizeV >= 1) - { - MipData[MipIndex].Reset(); - - FReadSurfaceDataFlags Flags(RCM_UNorm, CubeFace_MAX); - Flags.SetMip(MipIndex); - FIntRect Rect(0, 0, MipSizeU, MipSizeV); - - { - TArray* OutData = &MipData[MipIndex]; - FTextureRHIRef SourceTextureRHI = HeightmapRenderData.HeightmapsCPUReadBack->TextureRHI; - ENQUEUE_RENDER_COMMAND(ReadSurfaceCommand)( - [SourceTextureRHI, Rect, OutData, Flags](FRHICommandListImmediate& RHICmdList) - { - RHICmdList.ReadSurfaceData(SourceTextureRHI, Rect, *OutData, Flags); - }); - } - - MipSizeU >>= 1; - MipSizeV >>= 1; - ++MipIndex; - } - - FlushRenderingCommands(); - - for (MipIndex = 0; MipIndex < MipData.Num(); ++MipIndex) - { - if (MipData[MipIndex].Num() > 0) - { - PrintDebugHeightData(CVarOutputProceduralRTContent.GetValueOnAnyThread() == 1 ? FString::Printf(TEXT("CPUReadBack -> Source Heightmap %s, Mip: %d"), *HeightmapRenderData.OriginalHeightmap->GetName(), MipIndex) : TEXT(""), - MipData[MipIndex], FIntPoint(HeightmapRenderData.HeightmapsCPUReadBack->GetSizeX() >> MipIndex, HeightmapRenderData.HeightmapsCPUReadBack->GetSizeY() >> MipIndex), MipIndex, true); - - FColor* HeightmapTextureData = (FColor*)HeightmapRenderData.OriginalHeightmap->Source.LockMip(MipIndex); - FMemory::Memzero(HeightmapTextureData, MipData[MipIndex].Num() * sizeof(FColor)); - FMemory::Memcpy(HeightmapTextureData, MipData[MipIndex].GetData(), MipData[MipIndex].Num() * sizeof(FColor)); - HeightmapRenderData.OriginalHeightmap->Source.UnlockMip(MipIndex); - } - } - - if (InUpdateDDC) - { - HeightmapRenderData.OriginalHeightmap->BeginCachePlatformData(); - HeightmapRenderData.OriginalHeightmap->ClearAllCachedCookedPlatformData(); - PendingDDCUpdateTextureList.Add(HeightmapRenderData.OriginalHeightmap); - HeightmapRenderData.OriginalHeightmap->MarkPackageDirty(); - } + ResolveProceduralTexture(HeightmapRenderData.HeightmapsCPUReadBack, HeightmapRenderData.OriginalHeightmap); } - } + } +} - if (InUpdateDDC) +void ALandscape::ResolveProceduralTexture(FLandscapeProceduralTexture2DCPUReadBackResource* InCPUReadBackTexture, UTexture2D* InOriginalTexture) +{ + TArray> MipData; + MipData.AddDefaulted(InCPUReadBackTexture->TextureRHI->GetNumMips()); + + int32 MipSizeU = InCPUReadBackTexture->GetSizeX(); + int32 MipSizeV = InCPUReadBackTexture->GetSizeY(); + int32 MipIndex = 0; + + while (MipSizeU >= 1 && MipSizeV >= 1) { - // Wait for all texture to be finished, do them async, since we can have many to update but we still need to wait for all of them to be finish before continuing - for (UTexture* PendingDDCUpdateTexture : PendingDDCUpdateTextureList) - { - PendingDDCUpdateTexture->FinishCachePlatformData(); + MipData[MipIndex].Reset(); - PendingDDCUpdateTexture->Resource = PendingDDCUpdateTexture->CreateResource(); - if (PendingDDCUpdateTexture->Resource) + FReadSurfaceDataFlags Flags(RCM_UNorm, CubeFace_MAX); + Flags.SetMip(MipIndex); + FIntRect Rect(0, 0, MipSizeU, MipSizeV); + + ENQUEUE_RENDER_COMMAND(FProceduralReadSurfaceCommand)( + [SourceTextureRHI = InCPUReadBackTexture->TextureRHI, Rect = Rect, OutData = &MipData[MipIndex], ReadFlags = Flags](FRHICommandListImmediate& RHICmdList) mutable + { + RHICmdList.ReadSurfaceData(SourceTextureRHI, Rect, *OutData, ReadFlags); + }); + + + MipSizeU >>= 1; + MipSizeV >>= 1; + ++MipIndex; + } + + // TODO: find a way to NOT have to flush the rendering command as this create hic up of ~10-15ms + FlushRenderingCommands(); + + for (MipIndex = 0; MipIndex < MipData.Num(); ++MipIndex) + { + if (MipData[MipIndex].Num() > 0) + { + FColor* TextureData = (FColor*)InOriginalTexture->Source.LockMip(MipIndex); + FMemory::Memcpy(TextureData, MipData[MipIndex].GetData(), MipData[MipIndex].Num() * sizeof(FColor)); + InOriginalTexture->Source.UnlockMip(MipIndex); + } + } +} + +void ALandscape::PrepareProceduralComponentDataForExtractLayersCS(const FProceduralLayer& InProceduralLayer, int32 InCurrentWeightmapToProcessIndex, bool InOutputDebugName, const TArray& InAllLandscape, FLandscapeTexture2DResource* InOutTextureData, + TArray& OutComponentData, TMap& OutLayerInfoObjects) +{ + ULandscapeInfo* Info = GetLandscapeInfo(); + + for (const ALandscapeProxy* Landscape : InAllLandscape) + { + const FProceduralLayerData* ProceduralLayerData = Landscape->ProceduralLayersData.Find(InProceduralLayer.Guid); + + if (ProceduralLayerData != nullptr) + { + for (const auto& ItPair : ProceduralLayerData->WeightmapData) { - BeginInitResource(PendingDDCUpdateTexture->Resource); + ULandscapeComponent* Component = ItPair.Key; + const FWeightmapLayerData& WeightLayerData = ItPair.Value; + + if (WeightLayerData.Weightmaps.IsValidIndex(InCurrentWeightmapToProcessIndex)) + { + UTexture2D* Weightmap = WeightLayerData.Weightmaps[InCurrentWeightmapToProcessIndex]; + check(Weightmap != nullptr); + + const ULandscapeWeightmapUsage* WeightmapUsage = WeightLayerData.WeightmapTextureUsages[InCurrentWeightmapToProcessIndex]; + check(WeightmapUsage != nullptr); + + CopyProceduralTexture(*Weightmap->GetName(), Weightmap->Resource, InOutputDebugName ? FString::Printf(TEXT("%s WeightmapScratchTexture"), *InProceduralLayer.Name.ToString()) : TEXT(""), InOutTextureData, nullptr, Component->GetSectionBase(), 0); + PrintProceduralDebugTextureResource(InOutputDebugName ? FString::Printf(TEXT("LS Weight: %s WeightmapScratchTexture %s"), *InProceduralLayer.Name.ToString(), TEXT("WeightmapScratchTextureResource")) : TEXT(""), InOutTextureData, 0, false); + + for (const FWeightmapLayerAllocationInfo& WeightmapLayerAllocation : WeightLayerData.WeightmapLayerAllocations) + { + if (WeightmapLayerAllocation.LayerInfo != nullptr && WeightmapLayerAllocation.WeightmapTextureIndex != 255 && WeightLayerData.Weightmaps[WeightmapLayerAllocation.WeightmapTextureIndex] == Weightmap) + { + FLandscapeProceduralWeightmapExtractLayersComponentData Data; + + const ULandscapeComponent* DestComponent = WeightmapUsage->ChannelUsage[WeightmapLayerAllocation.WeightmapTextureChannel]; + check(DestComponent); + + // Compute component top left vertex position from section base info + int32 LocalComponentSizeQuad = Component->SubsectionSizeQuads * NumSubsections; + int32 LocalComponentSizeVerts = (Component->SubsectionSizeQuads + 1) * NumSubsections; + FVector2D SourcePositionOffset(FMath::RoundToInt(Component->GetSectionBase().X / LocalComponentSizeQuad), FMath::RoundToInt(Component->GetSectionBase().Y / LocalComponentSizeQuad)); + FVector2D DestPositionOffset(FMath::RoundToInt(DestComponent->GetSectionBase().X / LocalComponentSizeQuad), FMath::RoundToInt(DestComponent->GetSectionBase().Y / LocalComponentSizeQuad)); + + Data.ComponentVertexPosition = FIntPoint(SourcePositionOffset.X * LocalComponentSizeVerts, SourcePositionOffset.Y * LocalComponentSizeVerts); + Data.AtlasTexturePositionOutput = FIntPoint(DestPositionOffset.X * LocalComponentSizeVerts, DestPositionOffset.Y * LocalComponentSizeVerts); + Data.WeightmapChannelToProcess = WeightmapLayerAllocation.WeightmapTextureChannel; + + if (WeightmapLayerAllocation.LayerInfo == ALandscapeProxy::VisibilityLayer) + { + Data.DestinationPaintLayerIndex = 0; + int32& NewLayerinfoObjectIndex = OutLayerInfoObjects.FindOrAdd(ALandscapeProxy::VisibilityLayer); + NewLayerinfoObjectIndex = 0; + } + else + { + for (int32 LayerInfoSettingsIndex = 0; LayerInfoSettingsIndex < Info->Layers.Num(); ++LayerInfoSettingsIndex) + { + const FLandscapeInfoLayerSettings& InfoLayerSettings = Info->Layers[LayerInfoSettingsIndex]; + + if (InfoLayerSettings.LayerInfoObj == WeightmapLayerAllocation.LayerInfo) + { + Data.DestinationPaintLayerIndex = LayerInfoSettingsIndex + 1; // due to visibility layer that is at 0 + int32& NewLayerinfoObjectIndex = OutLayerInfoObjects.FindOrAdd(WeightmapLayerAllocation.LayerInfo); + NewLayerinfoObjectIndex = LayerInfoSettingsIndex + 1; + + break; + } + } + } + + OutComponentData.Add(Data); + } + } + } } } } } -void ALandscape::RegenerateProceduralWeightmaps() +void ALandscape::PrepareProceduralComponentDataForPackLayersCS(int32 InCurrentWeightmapToProcessIndex, bool InOutputDebugName, const TArray& InAllLandscapeComponents, TArray& InOutProcessedWeightmaps, + TArray& InOutProcessedWeightmapCPUCopy, TArray& OutComponentData) { + ULandscapeInfo* Info = GetLandscapeInfo(); + for (const ULandscapeComponent* Component : InAllLandscapeComponents) + { + const TArray& WeightmapTextures = Component->GetWeightmapTextures(); + + if (WeightmapTextures.IsValidIndex(InCurrentWeightmapToProcessIndex)) + { + UTexture2D* WeightmapTexture = WeightmapTextures[InCurrentWeightmapToProcessIndex]; + + if (!InOutProcessedWeightmaps.Contains(WeightmapTexture)) + { + InOutProcessedWeightmaps.Add(WeightmapTexture); + + FLandscapeProceduralTexture2DCPUReadBackResource** WeightmapCPUCopy = Component->GetLandscapeProxy()->WeightmapCPUReadBackTextures.Find(WeightmapTexture); + check(WeightmapCPUCopy != nullptr); + + InOutProcessedWeightmapCPUCopy.Add(*WeightmapCPUCopy); + + const TArray& WeightmapTexturesUsage = Component->GetWeightmapTexturesUsage(); + + const ULandscapeWeightmapUsage* WeightmapUsage = WeightmapTexturesUsage[InCurrentWeightmapToProcessIndex]; + check(WeightmapUsage != nullptr); + + TArray AlreadyProcessedAllocation; + FLandscapeProceduralWeightmapPackLayersComponentData Data; + + for (int32 WeightmapChannelIndex = 0; WeightmapChannelIndex < 4; ++WeightmapChannelIndex) + { + // Clear out data to known values + Data.ComponentVertexPositionX[WeightmapChannelIndex] = INDEX_NONE; + Data.ComponentVertexPositionY[WeightmapChannelIndex] = INDEX_NONE; + Data.SourcePaintLayerIndex[WeightmapChannelIndex] = INDEX_NONE; + Data.WeightmapChannelToProcess[WeightmapChannelIndex] = INDEX_NONE; + + if (WeightmapUsage->ChannelUsage[WeightmapChannelIndex] != nullptr) + { + const ULandscapeComponent* ChannelComponent = WeightmapUsage->ChannelUsage[WeightmapChannelIndex]; + FIntPoint ComponentSectionBase = ChannelComponent->GetSectionBase(); + + // Compute component top left vertex position from section base info + int32 LocalComponentSizeQuad = ChannelComponent->SubsectionSizeQuads * NumSubsections; + int32 LocalComponentSizeVerts = (ChannelComponent->SubsectionSizeQuads + 1) * NumSubsections; + FVector2D PositionOffset(FMath::RoundToInt(ChannelComponent->GetSectionBase().X / LocalComponentSizeQuad), FMath::RoundToInt(ChannelComponent->GetSectionBase().Y / LocalComponentSizeQuad)); + + Data.ComponentVertexPositionX[WeightmapChannelIndex] = PositionOffset.X * LocalComponentSizeVerts; + Data.ComponentVertexPositionY[WeightmapChannelIndex] = PositionOffset.Y * LocalComponentSizeVerts; + + const TArray& ChannelLayerAllocations = ChannelComponent->GetWeightmapLayerAllocations(); + const TArray& ChannelComponentWeightmapTextures = ChannelComponent->GetWeightmapTextures(); + + for (const FWeightmapLayerAllocationInfo& ChannelLayerAllocation : ChannelLayerAllocations) + { + if (ChannelLayerAllocation.LayerInfo != nullptr && !AlreadyProcessedAllocation.Contains(&ChannelLayerAllocation) && ChannelComponentWeightmapTextures[ChannelLayerAllocation.WeightmapTextureIndex] == WeightmapTexture) + { + Data.WeightmapChannelToProcess[WeightmapChannelIndex] = ChannelLayerAllocation.WeightmapTextureChannel; + AlreadyProcessedAllocation.Add(&ChannelLayerAllocation); + + if (ChannelLayerAllocation.LayerInfo == ALandscapeProxy::VisibilityLayer) + { + Data.SourcePaintLayerIndex[WeightmapChannelIndex] = 0; // Always store after the last weightmap index + } + else + { + for (int32 LayerInfoSettingsIndex = 0; LayerInfoSettingsIndex < Info->Layers.Num(); ++LayerInfoSettingsIndex) + { + const FLandscapeInfoLayerSettings& LayerInfo = Info->Layers[LayerInfoSettingsIndex]; + + if (ChannelLayerAllocation.LayerInfo == LayerInfo.LayerInfoObj) + { + Data.SourcePaintLayerIndex[WeightmapChannelIndex] = LayerInfoSettingsIndex + 1; // due to visibility layer that is at 0 + break; + } + } + } + + break; + } + } + } + } + + OutComponentData.Add(Data); + } + } + } } -void ALandscape::RequestProceduralContentUpdate(uint32 InDataFlags) +void ALandscape::ReallocateProceduralWeightmaps(const TArray& InAllLandscape, const TArray& InBrushRequiredAllocations, TArray& OutComponentThatNeedMaterialRebuild) +{ + SCOPE_CYCLE_COUNTER(STAT_LandscapeReallocateProceduralWeightmaps); + + TArray AllLandscapeComponents; + + for (ALandscapeProxy* Landscape : InAllLandscape) + { + AllLandscapeComponents.Append(Landscape->LandscapeComponents); + } + + // Copy Previous Usage, to know which texture need updating + TMap CurrentWeightmapsUsage; + + for (ULandscapeComponent* Component : AllLandscapeComponents) + { + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(); + TArray& ComponentWeightmapTextureUsage = Component->GetWeightmapTexturesUsage(); + + for (int32 i = 0; i < ComponentWeightmapTextures.Num(); ++i) + { + UTexture2D* ComponentWeightmapTexture = ComponentWeightmapTextures[i]; + ULandscapeWeightmapUsage** CurrentWeightmapTextureUsage = CurrentWeightmapsUsage.Find(ComponentWeightmapTexture); + + if (CurrentWeightmapTextureUsage == nullptr) + { + ULandscapeWeightmapUsage* ComponentWeightmapUsage = ComponentWeightmapTextureUsage[i]; + ULandscapeWeightmapUsage* Usage = NewObject(Component->GetLandscapeProxy()); + + for (int32 j = 0; j < 4; ++j) + { + Usage->ChannelUsage[j] = ComponentWeightmapUsage->ChannelUsage[j]; + } + + CurrentWeightmapsUsage.Add(ComponentWeightmapTexture, Usage); + } + } + } + + // Clear allocation data + for (ULandscapeComponent* Component : AllLandscapeComponents) + { + TArray& BaseLayerAllocations = Component->GetWeightmapLayerAllocations(); + + for (FWeightmapLayerAllocationInfo& BaseWeightmapAllocation : BaseLayerAllocations) + { + BaseWeightmapAllocation.WeightmapTextureChannel = 255; + BaseWeightmapAllocation.WeightmapTextureIndex = 255; + } + + TArray& WeightmapTexturesUsage = Component->GetWeightmapTexturesUsage(); + + for (int32 i = 0; i < WeightmapTexturesUsage.Num(); ++i) + { + ULandscapeWeightmapUsage* Usage = WeightmapTexturesUsage[i]; + check(Usage != nullptr); + + Usage->ClearUsage(); + } + } + + bool NeedMaterialInstanceRebuild = false; + + // Build a map of all the allocation per components + TMap> LayerAllocsPerComponent; + + for (const ALandscapeProxy* Landscape : InAllLandscape) + { + for (auto& ItLayerPair : Landscape->ProceduralLayersData) + { + const FProceduralLayerData& ProceduralLayerData = ItLayerPair.Value; + + for (const auto& ItWeightmapPair : ProceduralLayerData.WeightmapData) + { + ULandscapeComponent* Component = ItWeightmapPair.Key; + const FWeightmapLayerData& WeightLayerData = ItWeightmapPair.Value; + + TArray* ComponentLayerAlloc = LayerAllocsPerComponent.Find(Component); + + if (ComponentLayerAlloc == nullptr) + { + TArray NewLayerAllocs; + ComponentLayerAlloc = &LayerAllocsPerComponent.Add(Component, NewLayerAllocs); + } + + for (const FWeightmapLayerAllocationInfo& LayerWeightmapAllocation : WeightLayerData.WeightmapLayerAllocations) + { + if (LayerWeightmapAllocation.LayerInfo != nullptr) + { + ComponentLayerAlloc->AddUnique(LayerWeightmapAllocation.LayerInfo); + } + } + + // Add the brush alloc also + for (ULandscapeLayerInfoObject* BrushLayerInfo : InBrushRequiredAllocations) + { + if (BrushLayerInfo != nullptr) + { + ComponentLayerAlloc->AddUnique(BrushLayerInfo); + } + } + } + } + } + + // Determine if the Final layer need to add/remove some alloc + for (auto& ItPair : LayerAllocsPerComponent) + { + ULandscapeComponent* Component = ItPair.Key; + TArray& ComponentLayerAlloc = ItPair.Value; + TArray& ComponentBaseLayerAlloc = Component->GetWeightmapLayerAllocations(); + + // Deal with the one that need removal + for (int32 i = ComponentBaseLayerAlloc.Num() - 1; i >= 0; --i) + { + FWeightmapLayerAllocationInfo& Alloc = ComponentBaseLayerAlloc[i]; + + if (!ComponentLayerAlloc.Contains(Alloc.LayerInfo)) + { + ComponentBaseLayerAlloc.RemoveAt(i); + } + } + + // Then add the new one + for (ULandscapeLayerInfoObject* LayerAlloc : ComponentLayerAlloc) + { + const bool AllocExist = ComponentBaseLayerAlloc.ContainsByPredicate([&LayerAlloc](FWeightmapLayerAllocationInfo& BaseLayerAlloc) { return (LayerAlloc == BaseLayerAlloc.LayerInfo); }); + + if (!AllocExist) + { + ComponentBaseLayerAlloc.Add(FWeightmapLayerAllocationInfo(LayerAlloc)); + } + } + } + + // Realloc the weightmap so it will create proper texture (if needed) and will set the allocations information + TArray NewCreatedTextures; + + for (ULandscapeComponent* Component : AllLandscapeComponents) + { + Component->ReallocateWeightmaps(nullptr, false, false, true, &NewCreatedTextures); + } + + // TODO: correctly only recreate what is required instead of everything.. + //GDisableAutomaticTextureMaterialUpdateDependencies = true; + + for (UTexture2D* Texture : NewCreatedTextures) + { + Texture->FinishCachePlatformData(); + Texture->PostEditChange(); + + Texture->WaitForStreaming(); + } + + //GDisableAutomaticTextureMaterialUpdateDependencies = false; + + // Determine which Component need updating + for (ULandscapeComponent* Component : AllLandscapeComponents) + { + if (OutComponentThatNeedMaterialRebuild.Contains(Component)) + { + continue; + } + + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(); + TArray& ComponentWeightmapTextureUsage = Component->GetWeightmapTexturesUsage(); + + for (int32 i = 0; i < ComponentWeightmapTextures.Num(); ++i) + { + UTexture2D* ComponentWeightmapTexture = ComponentWeightmapTextures[i]; + ULandscapeWeightmapUsage* ComponentWeightmapUsage = ComponentWeightmapTextureUsage[i]; + ULandscapeWeightmapUsage** CurrentWeightmapTextureUsage = CurrentWeightmapsUsage.Find(ComponentWeightmapTexture); + + if (CurrentWeightmapTextureUsage != nullptr) + { + for (int32 j = 0; j < 4; ++j) + { + if (ComponentWeightmapUsage->ChannelUsage[j] != (*CurrentWeightmapTextureUsage)->ChannelUsage[j] && ComponentWeightmapUsage->ChannelUsage[j] != nullptr) + { + OutComponentThatNeedMaterialRebuild.AddUnique(ComponentWeightmapUsage->ChannelUsage[j]); + } + } + } + else if (NewCreatedTextures.Contains(ComponentWeightmapTexture)) + { + for (int32 j = 0; j < 4; ++j) + { + if (ComponentWeightmapUsage->ChannelUsage[j] != nullptr) + { + OutComponentThatNeedMaterialRebuild.AddUnique(ComponentWeightmapUsage->ChannelUsage[j]); + } + } + } + } + } +} + +void ALandscape::InitProceduralWeightmapResources(uint8 InLayerCount) +{ + ULandscapeInfo* Info = GetLandscapeInfo(); + + // Use the 1st one to compute the resource as they are all the same anyway + UTextureRenderTarget2D* FirstWeightmapRT = WeightmapRTList[WeightmapRT_Scratch1]; + + if (CombinedProcLayerWeightmapAllLayersResource != nullptr && InLayerCount != CombinedProcLayerWeightmapAllLayersResource->GetSizeZ()) + { + ReleaseResourceAndFlush(CombinedProcLayerWeightmapAllLayersResource); + CombinedProcLayerWeightmapAllLayersResource = nullptr; + } + + if (CombinedProcLayerWeightmapAllLayersResource == nullptr) + { + CombinedProcLayerWeightmapAllLayersResource = new FLandscapeTexture2DArrayResource(FirstWeightmapRT->SizeX, FirstWeightmapRT->SizeY, InLayerCount, PF_G8, 1, true); + BeginInitResource(CombinedProcLayerWeightmapAllLayersResource); + } + + if (CurrentProcLayerWeightmapAllLayersResource != nullptr && InLayerCount != CurrentProcLayerWeightmapAllLayersResource->GetSizeZ()) + { + ReleaseResourceAndFlush(CurrentProcLayerWeightmapAllLayersResource); + CurrentProcLayerWeightmapAllLayersResource = nullptr; + } + + if (CurrentProcLayerWeightmapAllLayersResource == nullptr) + { + CurrentProcLayerWeightmapAllLayersResource = new FLandscapeTexture2DArrayResource(FirstWeightmapRT->SizeX, FirstWeightmapRT->SizeY, InLayerCount, PF_G8, 1, true); + BeginInitResource(CurrentProcLayerWeightmapAllLayersResource); + } + + if (WeightmapScratchExtractLayerTextureResource == nullptr) + { + WeightmapScratchExtractLayerTextureResource = new FLandscapeTexture2DResource(FirstWeightmapRT->SizeX, FirstWeightmapRT->SizeY, PF_B8G8R8A8, 1, false); + BeginInitResource(WeightmapScratchExtractLayerTextureResource); + } + + if (WeightmapScratchPackLayerTextureResource == nullptr) + { + int32 MipCount = 0; + + for (int32 MipRTIndex = EWeightmapRTType::WeightmapRT_Mip0; MipRTIndex < EWeightmapRTType::WeightmapRT_Count; ++MipRTIndex) + { + if (WeightmapRTList[MipRTIndex] != nullptr) + { + ++MipCount; + } + } + + WeightmapScratchPackLayerTextureResource = new FLandscapeTexture2DResource(FirstWeightmapRT->SizeX, FirstWeightmapRT->SizeY, PF_B8G8R8A8, MipCount, true); + BeginInitResource(WeightmapScratchPackLayerTextureResource); + } +} + +/* This code was removed as it's a fix to remove weightmap with 0 weightmap for a component, unfortunatly it will cause side effect, like perf issue, etc, so for now it's disabled until we can address this later +bool ALandscape::GenerateZeroAllocationPerComponents(const TArray& InAllLandscape, const TMap& InWeightmapLayersBlendSubstractive, TMap>& OutZeroAllocationsPerComponents) +{ + // Make sure all CPU Readback texture are ready to be used + for (ALandscapeProxy* Landscape : InAllLandscape) + { + for (auto& ItPair : Landscape->WeightmapCPUReadBackTextures) + { + FLandscapeProceduralTexture2DCPUReadBackResource* CPuReadBackTexture = ItPair.Value; + + if (CPuReadBackTexture != nullptr && !CPuReadBackTexture->IsInitialized()) + { + return false; + } + } + } + + TArray AllLandscapeComponents; + + for (ALandscapeProxy* Landscape : InAllLandscape) + { + AllLandscapeComponents.Append(Landscape->LandscapeComponents); + } + + // Compute the data to facilitate the process + struct FTextureChannelData + { + TArray> ComponentPerChannel; + TArray> LayerInfoPerChannel; + TArray TexelData; + FLandscapeProceduralTexture2DCPUReadBackResource* CPUReadbackTexture; + }; + + TMap TextureDataPerTextures; + + for (ULandscapeComponent* LandscapeComponent : AllLandscapeComponents) + { + const TArray& ComponentWeightmapUsage = LandscapeComponent->GetWeightmapTexturesUsage(); + const TArray& ComponentWeightmapTextures = LandscapeComponent->GetWeightmapTextures(); + + for (int32 TextureIndex = 0; TextureIndex < ComponentWeightmapTextures.Num(); ++TextureIndex) + { + UTexture2D* WeightmapTexture = ComponentWeightmapTextures[TextureIndex]; + + if (TextureDataPerTextures.Contains(WeightmapTexture)) + { + continue; + } + + FLandscapeWeightmapUsage* Usage = ComponentWeightmapUsage[TextureIndex]; + + for (int32 i = 0; i < 4; ++i) + { + ULandscapeComponent* Component = Usage->ChannelUsage[i]; + + if (Component != nullptr) + { + const TArray& ComponentUsageWeightmapAllocations = Component->GetWeightmapLayerAllocations(); + + for (const FWeightmapLayerAllocationInfo& Allocation : ComponentUsageWeightmapAllocations) + { + if (Allocation.WeightmapTextureIndex == TextureIndex) + { + const bool* IsSubtractive = InWeightmapLayersBlendSubstractive.Find(Allocation.LayerInfo); + + if (IsSubtractive != nullptr && *IsSubtractive) + { + FTextureChannelData* TextureData = TextureDataPerTextures.Find(WeightmapTexture); + + if (TextureData == nullptr) + { + FTextureChannelData NewData; + NewData.ComponentPerChannel.SetNumZeroed(4); + NewData.LayerInfoPerChannel.SetNumZeroed(4); + + FLandscapeProceduralTexture2DCPUReadBackResource** CPUReadbackTexture = Component->GetLandscapeProxy()->WeightmapCPUReadBackTextures.Find(WeightmapTexture); + check(CPUReadbackTexture); + + NewData.CPUReadbackTexture = *CPUReadbackTexture; + + TextureData = &TextureDataPerTextures.Add(WeightmapTexture, NewData); + } + + TextureData->LayerInfoPerChannel[Allocation.WeightmapTextureChannel] = Allocation.LayerInfo; + TextureData->ComponentPerChannel[Allocation.WeightmapTextureChannel] = Component; + } + } + } + } + } + } + } + + // Read the data form the CPU texture + if (TextureDataPerTextures.Num() > 0) + { + for (auto& ItPair : TextureDataPerTextures) + { + FTextureChannelData& TextureChannelData = ItPair.Value; + + int32 MipSizeU = TextureChannelData.CPUReadbackTexture->GetSizeX(); + int32 MipSizeV = TextureChannelData.CPUReadbackTexture->GetSizeY(); + + FReadSurfaceDataFlags Flags(RCM_UNorm, CubeFace_MAX); + Flags.SetMip(0); + FIntRect Rect(0, 0, MipSizeU, MipSizeV); + + ENQUEUE_UNIQUE_RENDER_COMMAND_FOURPARAMETER( + ReadSurfaceCommand, + FTextureRHIRef, SourceTextureRHI, TextureChannelData.CPUReadbackTexture->TextureRHI, + FIntRect, Rect, Rect, + TArray*, OutData, &TextureChannelData.TexelData, + FReadSurfaceDataFlags, ReadFlags, Flags, + { + RHICmdList.ReadSurfaceData(SourceTextureRHI, Rect, *OutData, ReadFlags); + }); + } + + FlushRenderingCommands(); + + // Determine which texture channel is zero allocation to "remove" them + for (auto& ItPair : TextureDataPerTextures) + { + FTextureChannelData& TextureChannelData = ItPair.Value; + + TArray AreLayerEmpty; + AreLayerEmpty.Init(true, 4); + + for (FColor& Color : TextureChannelData.TexelData) + { + if (Color.R != 0) + { + AreLayerEmpty[0] = false; + } + + if (Color.G != 0) + { + AreLayerEmpty[1] = false; + } + + if (Color.B != 0) + { + AreLayerEmpty[2] = false; + } + + if (Color.A != 0) + { + AreLayerEmpty[3] = false; + } + } + + for (int32 i = 0; i < 4; ++i) + { + if (AreLayerEmpty[i] && TextureChannelData.ComponentPerChannel[0] != nullptr) + { + TArray& ZeroAllocations = OutZeroAllocationsPerComponents.FindOrAdd(TextureChannelData.ComponentPerChannel[0]); + ZeroAllocations.Add(TextureChannelData.LayerInfoPerChannel[0]); + } + } + } + } + + return true; +} +*/ + +bool ALandscape::AreWeightmapTextureResourcesReady(const TArray& InAllLandscapes) const +{ + // Make sure all our original weightmap textures are streamed in and ready to be used + for (const ALandscapeProxy* Landscape : InAllLandscapes) + { + for (const auto& ItLayerDataPair : Landscape->ProceduralLayersData) + { + for (const auto& ItWeightmapPair : ItLayerDataPair.Value.WeightmapData) + { + ULandscapeComponent* Component = ItWeightmapPair.Key; + const TArray& OriginalWeightmaps = Component->GetWeightmapTextures(); + + for (UTexture2D* Weightmap : OriginalWeightmaps) + { + if (!Weightmap->IsFullyStreamedIn()) + { + return false; + } + } + } + } + } + + // Init all needed resources + for (const ALandscapeProxy* Landscape : InAllLandscapes) + { + for (const auto& ItLayerDataPair : Landscape->ProceduralLayersData) + { + for (const auto& ItWeightmapPair : ItLayerDataPair.Value.WeightmapData) + { + const FWeightmapLayerData& WeightmapLayerData = ItWeightmapPair.Value; + + for (UTexture2D* Weightmap : WeightmapLayerData.Weightmaps) + { + if (Weightmap->Resource == nullptr) + { + Weightmap->FinishCachePlatformData(); + + Weightmap->Resource = Weightmap->CreateResource(); + + if (Weightmap->Resource != nullptr) + { + BeginInitResource(Weightmap->Resource); + } + } + } + } + } + } + + // Wait for the new resource to be fully initialized/streamed in + for (const ALandscapeProxy* Landscape : InAllLandscapes) + { + for (const auto& ItLayerDataPair : Landscape->ProceduralLayersData) + { + for (const auto& ItWeightmapPair : ItLayerDataPair.Value.WeightmapData) + { + const FWeightmapLayerData& WeightmapLayerData = ItWeightmapPair.Value; + + for (UTexture2D* Weightmap : WeightmapLayerData.Weightmaps) + { + if (Weightmap->Resource == nullptr || !Weightmap->Resource->IsInitialized() || !Weightmap->IsFullyStreamedIn()) + { + return false; + } + } + } + } + } + + return true; +} + +void ALandscape::RegenerateProceduralWeightmaps() +{ + SCOPE_CYCLE_COUNTER(STAT_LandscapeRegenerateProceduralWeightmaps); + + ULandscapeInfo* Info = GetLandscapeInfo(); + + if (ProceduralContentUpdateFlags == 0 || Info == nullptr || Info->Layers.Num() == 0) + { + return; + } + + TArray AllLandscapes; + AllLandscapes.Add(this); + + for (const auto& It : Info->Proxies) + { + AllLandscapes.Add(It); + } + + if (!AreWeightmapTextureResourcesReady(AllLandscapes)) + { + return; + } + + TArray AllLandscapeComponents; + + for (ALandscapeProxy* Landscape : AllLandscapes) + { + AllLandscapeComponents.Append(Landscape->LandscapeComponents); + } + + TArray ComponentThatNeedMaterialRebuild; + TArray BrushRequiredAllocations; + int32 LayerCount = Info->Layers.Num() + 1; // due to visibility being stored at 0 + bool ClearFlagsAfterUpdate = true; + + if ((ProceduralContentUpdateFlags & EProceduralContentUpdateFlag::Weightmap_Render) != 0 && WeightmapRTList.Num() > 0) + { + UTextureRenderTarget2D* LandscapeScratchRT1 = WeightmapRTList[EWeightmapRTType::WeightmapRT_Scratch1]; + UTextureRenderTarget2D* LandscapeScratchRT2 = WeightmapRTList[EWeightmapRTType::WeightmapRT_Scratch2]; + UTextureRenderTarget2D* LandscapeScratchRT3 = WeightmapRTList[EWeightmapRTType::WeightmapRT_Scratch3]; + UTextureRenderTarget2D* EmptyRT = WeightmapRTList[EWeightmapRTType::WeightmapRT_Scratch_RGBA]; + FLandscapeWeightmapProceduralShaderParameters PSShaderParams; + bool OutputDebugName = (CVarOutputProceduralDebugDrawCallName.GetValueOnAnyThread() == 1 || CVarOutputProceduralRTContent.GetValueOnAnyThread() == 1 || CVarOutputProceduralWeightmapsRTContent.GetValueOnAnyThread() == 1) ? true : false; + + InitProceduralWeightmapResources(LayerCount); + + ClearWeightmapTextureResource(TEXT("ClearRT RGBA"), EmptyRT->GameThread_GetRenderTargetResource()); + ClearWeightmapTextureResource(TEXT("ClearRT R"), LandscapeScratchRT1->GameThread_GetRenderTargetResource()); + + for (int32 LayerIndex = 0; LayerIndex < LayerCount; ++LayerIndex) + { + CopyProceduralTexture(*LandscapeScratchRT1->GetName(), LandscapeScratchRT1->GameThread_GetRenderTargetResource(), OutputDebugName ? FString::Printf(TEXT("Weight: Clear CombinedProcLayerWeightmapAllLayersResource %d, "), LayerIndex) : TEXT(""), CombinedProcLayerWeightmapAllLayersResource, nullptr, FIntPoint(0, 0), 0, 0, 0, LayerIndex); + } + + bool ComputeShaderGeneratedData = false; + bool FirstLayer = true; + TMap WeightmapLayersBlendSubstractive; + + for (FProceduralLayer& ProceduralLayer : ProceduralLayers) + { + int8 CurrentWeightmapToProcessIndex = 0; + bool HasFoundWeightmapToProcess = true; // try processing at least once + + TMap LayerInfoObjects; // + + // Determine if some brush want to write to layer that we have currently no data on + if (ProceduralLayer.bVisible) + { + for (int32 LayerInfoSettingsIndex = 0; LayerInfoSettingsIndex < Info->Layers.Num(); ++LayerInfoSettingsIndex) + { + const FLandscapeInfoLayerSettings& InfoLayerSettings = Info->Layers[LayerInfoSettingsIndex]; + + for (int32 i = 0; i < ProceduralLayer.WeightmapBrushOrderIndices.Num(); ++i) + { + FLandscapeProceduralLayerBrush& Brush = ProceduralLayer.Brushes[ProceduralLayer.WeightmapBrushOrderIndices[i]]; + + if (Brush.BPCustomBrush == nullptr) + { + continue; + } + + if (Brush.BPCustomBrush->IsAffectingWeightmapLayer(InfoLayerSettings.GetLayerName()) && !LayerInfoObjects.Contains(InfoLayerSettings.LayerInfoObj)) + { + LayerInfoObjects.Add(InfoLayerSettings.LayerInfoObj, LayerInfoSettingsIndex + 1); // due to visibility layer that is at 0 + } + } + } + } + + // Loop until there is no more weightmap texture to process + while (HasFoundWeightmapToProcess) + { + CopyProceduralTexture(*EmptyRT->GetName(), EmptyRT->GameThread_GetRenderTargetResource(), OutputDebugName ? FString::Printf(TEXT("Weight: %s Clear WeightmapScratchExtractLayerTextureResource"), *ProceduralLayer.Name.ToString()) : TEXT(""), WeightmapScratchExtractLayerTextureResource); + + // Prepare compute shader data + TArray ComponentsData; + PrepareProceduralComponentDataForExtractLayersCS(ProceduralLayer, CurrentWeightmapToProcessIndex, OutputDebugName, AllLandscapes, WeightmapScratchExtractLayerTextureResource, ComponentsData, LayerInfoObjects); + + HasFoundWeightmapToProcess = ComponentsData.Num() > 0; + + // Perform the compute shader + if (ComponentsData.Num() > 0) + { + PrintProceduralDebugTextureResource(OutputDebugName ? FString::Printf(TEXT("LS Weight: %s WeightmapScratchTexture %s"), *ProceduralLayer.Name.ToString(), TEXT("WeightmapScratchTextureResource")) : TEXT(""), WeightmapScratchExtractLayerTextureResource, 0, false); + + // Clear the current atlas if required + if (CurrentWeightmapToProcessIndex == 0) + { + ClearWeightmapTextureResource(TEXT("ClearRT"), LandscapeScratchRT1->GameThread_GetRenderTargetResource()); + + // Important: for performance reason we only clear the layer we will write to, the other one might contain data but they will not be read during the blend phase + for (auto& ItPair : LayerInfoObjects) + { + int32 LayerIndex = ItPair.Value; + CopyProceduralTexture(*LandscapeScratchRT1->GetName(), LandscapeScratchRT1->GameThread_GetRenderTargetResource(), OutputDebugName ? FString::Printf(TEXT("Weight: %s Clear CurrentProcLayerWeightmapAllLayersResource %d, "), *ProceduralLayer.Name.ToString(), LayerIndex) : TEXT(""), CurrentProcLayerWeightmapAllLayersResource, nullptr, FIntPoint(0, 0), 0, 0, 0, LayerIndex); + } + } + + FLandscapeWeightmapProceduralWeightmapExtractLayersComputeShaderParameters CSExtractLayersShaderParams; + CSExtractLayersShaderParams.AtlasWeightmapsPerLayer = CurrentProcLayerWeightmapAllLayersResource; + CSExtractLayersShaderParams.ComponentWeightmapResource = WeightmapScratchExtractLayerTextureResource; + CSExtractLayersShaderParams.ComputeShaderResource = new FLandscapeProceduralWeightmapExtractLayersComputeShaderResource(ComponentsData); + CSExtractLayersShaderParams.ComponentSize = (SubsectionSizeQuads + 1) * NumSubsections; + + BeginInitResource(CSExtractLayersShaderParams.ComputeShaderResource); + + FLandscapeProceduralWeightmapExtractLayersCSDispatch_RenderThread CSDispatch(CSExtractLayersShaderParams); + + ENQUEUE_RENDER_COMMAND(FLandscapeProceduralExtractLayersCSCommand)( + [CSDispatch](FRHICommandListImmediate& RHICmdList) mutable + { + CSDispatch.ExtractLayers(RHICmdList); + }); + + ++CurrentWeightmapToProcessIndex; + ComputeShaderGeneratedData = true; // at least 1 CS was executed, so we can continue the processing + } + } + + // If we did process at least one compute shader + if (LayerInfoObjects.Num() > 0) + { + for (auto& LayerInfoObject : LayerInfoObjects) + { + int32 LayerIndex = LayerInfoObject.Value; + ULandscapeLayerInfoObject* LayerInfoObj = LayerInfoObject.Key; + + // Copy the layer we are working on + CopyProceduralTexture(OutputDebugName ? FString::Printf(TEXT("Weight: %s PaintLayer: %s, CurrentProcLayerWeightmapAllLayersResource"), *ProceduralLayer.Name.ToString(), *LayerInfoObj->LayerName.ToString()) : TEXT(""), CurrentProcLayerWeightmapAllLayersResource, *LandscapeScratchRT1->GetName(), LandscapeScratchRT1->GameThread_GetRenderTargetResource(), nullptr, FIntPoint(0, 0), 0, 0, LayerIndex, 0); + PrintProceduralDebugRT(OutputDebugName ? FString::Printf(TEXT("LS Weight: %s CurrentProcLayerWeightmapAllLayersResource -> Paint Layer RT %s"), *ProceduralLayer.Name.ToString(), *LandscapeScratchRT1->GetName()) : TEXT(""), LandscapeScratchRT1, 0, false); + + PSShaderParams.ApplyLayerModifiers = true; + PSShaderParams.LayerVisible = ProceduralLayer.bVisible; + PSShaderParams.LayerAlpha = LayerInfoObj == ALandscapeProxy::VisibilityLayer ? 1.0f : ProceduralLayer.WeightmapAlpha; // visibility can't be affected by weight + + DrawWeightmapComponentsToRenderTarget(OutputDebugName ? FString::Printf(TEXT("LS Weight: %s Paint: %s += -> %s"), *ProceduralLayer.Name.ToString(), *LayerInfoObj->LayerName.ToString(), *LandscapeScratchRT1->GetName(), *LandscapeScratchRT2->GetName()) : TEXT(""), + AllLandscapeComponents, LandscapeScratchRT1, nullptr, LandscapeScratchRT2, true, PSShaderParams, 0); + + PSShaderParams.ApplyLayerModifiers = false; + + // Combined Layer data with current stack + CopyProceduralTexture(OutputDebugName ? FString::Printf(TEXT("Weight: %s PaintLayer: %s CombinedProcLayerWeightmap"), *ProceduralLayer.Name.ToString(), *LayerInfoObj->LayerName.ToString()) : TEXT(""), CombinedProcLayerWeightmapAllLayersResource, *LandscapeScratchRT1->GetName(), LandscapeScratchRT1->GameThread_GetRenderTargetResource(), nullptr, FIntPoint(0, 0), 0, 0, LayerIndex, 0); + PrintProceduralDebugRT(OutputDebugName ? FString::Printf(TEXT("LS Weight: %s CombinedProcLayerWeightmap -> Paint Layer RT %s"), *ProceduralLayer.Name.ToString(), *LandscapeScratchRT1->GetName()) : TEXT(""), LandscapeScratchRT1, 0, false); + + // Combine with current status and copy back to the combined 2d resource array + PSShaderParams.OutputAsSubstractive = false; + + if (!FirstLayer) + { + const bool* BlendSubstractive = ProceduralLayer.WeightmapLayerAllocationBlend.Find(LayerInfoObj); + PSShaderParams.OutputAsSubstractive = BlendSubstractive != nullptr ? *BlendSubstractive : false; + + if (PSShaderParams.OutputAsSubstractive) + { + bool& IsSubstractiveBlend = WeightmapLayersBlendSubstractive.FindOrAdd(LayerInfoObj); + IsSubstractiveBlend = true; + } + } + + DrawWeightmapComponentsToRenderTarget(OutputDebugName ? FString::Printf(TEXT("LS Weight: %s PaintLayer: %s, %s += -> Combined %s"), *ProceduralLayer.Name.ToString(), *LayerInfoObj->LayerName.ToString(), *LandscapeScratchRT2->GetName(), *LandscapeScratchRT3->GetName()) : TEXT(""), + AllLandscapeComponents, LandscapeScratchRT2, FirstLayer ? nullptr : LandscapeScratchRT1, LandscapeScratchRT3, true, PSShaderParams, 0); + + PSShaderParams.OutputAsSubstractive = false; + + CopyProceduralTexture(OutputDebugName ? FString::Printf(TEXT("Weight: %s PaintLayer: %s %s"), *ProceduralLayer.Name.ToString(), *LayerInfoObj->LayerName.ToString(), *LandscapeScratchRT3->GetName()) : TEXT(""), LandscapeScratchRT3->GameThread_GetRenderTargetResource(), TEXT("CombinedProcLayerWeightmap"), CombinedProcLayerWeightmapAllLayersResource, nullptr, FIntPoint(0, 0), 0, 0, 0, LayerIndex); + + // Handle brush blending + if (ProceduralLayer.bVisible) + { + // Draw each brushes + for (int32 i = 0; i < ProceduralLayer.WeightmapBrushOrderIndices.Num(); ++i) + { + // TODO: handle conversion/handling of RT not same size as internal size + + FLandscapeProceduralLayerBrush& Brush = ProceduralLayer.Brushes[ProceduralLayer.WeightmapBrushOrderIndices[i]]; + + if (Brush.BPCustomBrush == nullptr || !Brush.BPCustomBrush->IsAffectingWeightmap() || !Brush.BPCustomBrush->IsAffectingWeightmapLayer(LayerInfoObj->LayerName)) + { + continue; + } + + BrushRequiredAllocations.AddUnique(LayerInfoObj); + + if (!Brush.IsInitialized()) + { + Brush.Initialize(GetBoundingRect(), FIntPoint(LandscapeScratchRT3->SizeX, LandscapeScratchRT3->SizeY)); + } + + UTextureRenderTarget2D* BrushOutputRT = Brush.Render(false, LandscapeScratchRT3); + + if (BrushOutputRT == nullptr || BrushOutputRT->SizeX != LandscapeScratchRT3->SizeX || BrushOutputRT->SizeY != LandscapeScratchRT3->SizeY) + { + continue; + } + + INC_DWORD_STAT(STAT_LandscapeRegenerateProceduralDrawCalls); // Brush Render + + PrintProceduralDebugRT(OutputDebugName ? FString::Printf(TEXT("LS Weight: %s %s -> Brush %s"), *ProceduralLayer.Name.ToString(), *Brush.BPCustomBrush->GetName(), *BrushOutputRT->GetName()) : TEXT(""), BrushOutputRT); + + CopyProceduralTexture(OutputDebugName ? FString::Printf(TEXT("Weight: %s PaintLayer: %s Brush: %s"), *ProceduralLayer.Name.ToString(), *LayerInfoObj->LayerName.ToString(), *BrushOutputRT->GetName()) : TEXT(""), BrushOutputRT->GameThread_GetRenderTargetResource(), *LandscapeScratchRT3->GetName(), LandscapeScratchRT3->GameThread_GetRenderTargetResource()); + PrintProceduralDebugRT(OutputDebugName ? FString::Printf(TEXT("LS Weight: %s Component %s += -> Combined %s"), *ProceduralLayer.Name.ToString(), *BrushOutputRT->GetName(), *LandscapeScratchRT3->GetName()) : TEXT(""), LandscapeScratchRT3); + } + + PrintProceduralDebugRT(OutputDebugName ? FString::Printf(TEXT("LS Weight: %s CombinedPostBrushProcLayerWeightmap -> Paint Layer RT %s"), *ProceduralLayer.Name.ToString(), *LandscapeScratchRT3->GetName()) : TEXT(""), LandscapeScratchRT3, 0, false); + CopyProceduralTexture(OutputDebugName ? FString::Printf(TEXT("Weight: %s PaintLayer: %s %s"), *ProceduralLayer.Name.ToString(), *LayerInfoObj->LayerName.ToString(), *LandscapeScratchRT3->GetName()) : TEXT(""), LandscapeScratchRT3->GameThread_GetRenderTargetResource(), TEXT("CombinedProcLayerWeightmap"), CombinedProcLayerWeightmapAllLayersResource, nullptr, FIntPoint(0, 0), 0, 0, 0, LayerIndex); + } + } + + PSShaderParams.ApplyLayerModifiers = false; + } + + FirstLayer = false; + } + + // TODO: if editing a Brush affecting layers, since we don't have any bounds to brush, right now ReallocateProceduralWeightmaps wont ask a rebuild of the component affected by Brushes, which mean ComponentThatNeedMaterialRebuild wont contains Brush affected component! + ReallocateProceduralWeightmaps(AllLandscapes, BrushRequiredAllocations, ComponentThatNeedMaterialRebuild); + + // Allocation that will need to be excluded when we update materials + TMap> ZeroAllocationsPerComponents; + + if (ComputeShaderGeneratedData) + { + // Will generate CPU read back resource, if required + for (ALandscapeProxy* LandscapeProxy : AllLandscapes) + { + for (const ULandscapeComponent* Component : LandscapeProxy->LandscapeComponents) + { + const TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(); + + for (UTexture2D* WeightmapTexture : ComponentWeightmapTextures) + { + FLandscapeProceduralTexture2DCPUReadBackResource** WeightmapCPUReadBack = LandscapeProxy->WeightmapCPUReadBackTextures.Find(WeightmapTexture); + + if (WeightmapCPUReadBack == nullptr) + { + FLandscapeProceduralTexture2DCPUReadBackResource* NewWeightmapCPUReadBack = new FLandscapeProceduralTexture2DCPUReadBackResource(WeightmapTexture->Source.GetSizeX(), WeightmapTexture->Source.GetSizeY(), WeightmapTexture->GetPixelFormat(), WeightmapTexture->Source.GetNumMips()); + BeginInitResource(NewWeightmapCPUReadBack); + + LandscapeProxy->WeightmapCPUReadBackTextures.Add(WeightmapTexture, NewWeightmapCPUReadBack); + } + } + } + } + + int8 CurrentWeightmapToProcessIndex = 0; + bool HasFoundWeightmapToProcess = true; // try processing at least once + + TArray WeightmapLayerWeightBlend; + TArray ProcessedWeightmaps; + TArray ProcessedWeightmapsCPUCopy; + int32 NextTextureIndexToProcess = 0; + + // Generate the component data from the weightmap allocation that were done earlier and weight blend them if required (i.e renormalize) + while (HasFoundWeightmapToProcess) + { + TArray PackLayersComponentsData; + PrepareProceduralComponentDataForPackLayersCS(CurrentWeightmapToProcessIndex, OutputDebugName, AllLandscapeComponents, ProcessedWeightmaps, ProcessedWeightmapsCPUCopy, PackLayersComponentsData); + HasFoundWeightmapToProcess = PackLayersComponentsData.Num() > 0; + + // Perform the compute shader + if (PackLayersComponentsData.Num() > 0) + { + // Compute the weightblend mode of each layer for the compute shader + if (WeightmapLayerWeightBlend.Num() != LayerCount) + { + WeightmapLayerWeightBlend.SetNum(LayerCount); + + for (int32 LayerInfoSettingsIndex = 0; LayerInfoSettingsIndex < Info->Layers.Num(); ++LayerInfoSettingsIndex) + { + const FLandscapeInfoLayerSettings& LayerInfo = Info->Layers[LayerInfoSettingsIndex]; + WeightmapLayerWeightBlend[LayerInfoSettingsIndex + 1] = LayerInfo.LayerInfoObj != nullptr ? (LayerInfo.LayerInfoObj->bNoWeightBlend ? 0.0f : 1.0f) : 1.0f; + } + + WeightmapLayerWeightBlend[0] = 0.0f; // Blend of Visibility + } + + TArray WeightmapTextureOutputOffset; + + // Compute each weightmap location so compute shader will be able to output at expected location + int32 ComponentSize = (SubsectionSizeQuads + 1) * NumSubsections; + float ComponentY = 0; + float ComponentX = 0; + + for (int32 i = 0; i < PackLayersComponentsData.Num(); ++i) + { + check(ComponentY < WeightmapScratchPackLayerTextureResource->GetSizeY()); // This should never happen as it would be a bug in the algo + + if (ComponentX >= WeightmapScratchPackLayerTextureResource->GetSizeX()) + { + ComponentY += ComponentSize; + ComponentX = 0; + } + + WeightmapTextureOutputOffset.Add(FVector2D(ComponentX, ComponentY)); + ComponentX += ComponentSize; + } + + // Clear Pack texture + CopyProceduralTexture(*EmptyRT->GetName(), EmptyRT->GameThread_GetRenderTargetResource(), TEXT("Weight: Clear WeightmapScratchPackLayerTextureResource"), WeightmapScratchPackLayerTextureResource); + + FLandscapeProceduralWeightmapPackLayersComputeShaderParameters CSPackLayersShaderParams; + CSPackLayersShaderParams.AtlasWeightmapsPerLayer = CombinedProcLayerWeightmapAllLayersResource; + CSPackLayersShaderParams.ComponentWeightmapResource = WeightmapScratchPackLayerTextureResource; + CSPackLayersShaderParams.ComputeShaderResource = new FLandscapeProceduralWeightmapPackLayersComputeShaderResource(PackLayersComponentsData, WeightmapLayerWeightBlend, WeightmapTextureOutputOffset); + CSPackLayersShaderParams.ComponentSize = ComponentSize; + BeginInitResource(CSPackLayersShaderParams.ComputeShaderResource); + + FLandscapeProceduralWeightmapPackLayerCSDispatch_RenderThread CSDispatch(CSPackLayersShaderParams); + + ENQUEUE_RENDER_COMMAND(FLandscapeProceduralPackLayersCSCommand)( + [CSDispatch](FRHICommandListImmediate& RHICmdList) mutable + { + CSDispatch.PackLayers(RHICmdList); + }); + + int32 StartTextureIndex = NextTextureIndexToProcess; + + for (; NextTextureIndexToProcess < ProcessedWeightmaps.Num(); ++NextTextureIndexToProcess) + { + UTexture2D* WeightmapTexture = ProcessedWeightmaps[NextTextureIndexToProcess]; + FLandscapeProceduralTexture2DCPUReadBackResource* WeightmapCPUReadBack = ProcessedWeightmapsCPUCopy[NextTextureIndexToProcess]; + FIntPoint TextureTopLeftPositionInAtlas(WeightmapTextureOutputOffset[NextTextureIndexToProcess - StartTextureIndex].X, WeightmapTextureOutputOffset[NextTextureIndexToProcess - StartTextureIndex].Y); + + UTextureRenderTarget2D* CurrentRT = WeightmapRTList[WeightmapRT_Mip0]; + CopyProceduralTexture(TEXT("WeightmapScratchTexture"), WeightmapScratchPackLayerTextureResource, *CurrentRT->GetName(), CurrentRT->GameThread_GetRenderTargetResource()); + + DrawWeightmapComponentToRenderTargetMips(TextureTopLeftPositionInAtlas, CurrentRT, true, PSShaderParams); + + int32 CurrentMip = 0; + + for (int32 MipRTIndex = EWeightmapRTType::WeightmapRT_Mip0; MipRTIndex < EWeightmapRTType::WeightmapRT_Count; ++MipRTIndex) + { + CurrentRT = WeightmapRTList[MipRTIndex]; + + if (CurrentRT != nullptr) + { + CopyProceduralTexture(*CurrentRT->GetName(), CurrentRT->GameThread_GetRenderTargetResource(), OutputDebugName ? FString::Printf(TEXT("Weightmap Mip: %d"), CurrentMip) : TEXT(""), WeightmapTexture->Resource, WeightmapCPUReadBack, TextureTopLeftPositionInAtlas, CurrentMip, CurrentMip); + ++CurrentMip; + } + } + } + } + + ++CurrentWeightmapToProcessIndex; + } + + // Generate all the allocation with zero painted data for each components, it will be used to update the material instance to not sample those textures + /*if (ComputeShaderGeneratedData) + { + ClearFlagsAfterUpdate = GenerateZeroAllocationPerComponents(AllLandscapes, WeightmapLayersBlendSubstractive, ZeroAllocationsPerComponents); + } + */ + } + + UpdateProceduralMaterialInstances(ProceduralUpdateAllMaterials ? AllLandscapeComponents : ComponentThatNeedMaterialRebuild, ZeroAllocationsPerComponents); + ProceduralUpdateAllMaterials = false; + } + + if ((ProceduralContentUpdateFlags & EProceduralContentUpdateFlag::Weightmap_ResolveToTexture)) + { + ResolveProceduralWeightmapTexture(AllLandscapes); + } + + if ((ProceduralContentUpdateFlags & EProceduralContentUpdateFlag::Weightmap_Collision) != 0) + { + for (ULandscapeComponent* Component : AllLandscapeComponents) + { + Component->UpdateCollisionLayerData(); + } + } + + if (ClearFlagsAfterUpdate) + { + ProceduralContentUpdateFlags &= ~EProceduralContentUpdateFlag::Weightmap_All; + } + + // If doing rendering debug, keep doing the render only + if (CVarOutputProceduralDebugDrawCallName.GetValueOnAnyThread() == 1) + { + ProceduralContentUpdateFlags |= EProceduralContentUpdateFlag::Weightmap_Render; + } +} + +void ALandscape::UpdateProceduralMaterialInstances(const TArray& InComponentsToUpdate, const TMap>& InZeroAllocationsPerComponents) +{ + if (InComponentsToUpdate.Num() == 0 && InZeroAllocationsPerComponents.Num() == 0) + { + return; + } + + TArray ComponentsToUpdate; + //InZeroAllocationsPerComponents.GenerateKeyArray(ComponentsToUpdate); + ComponentsToUpdate.Append(InComponentsToUpdate); + + SCOPE_CYCLE_COUNTER(STAT_LandscapeProceduralUpdateMaterialInstance); + + // we're not having the material update context recreate render states because we will manually do it for only our components + TArray RecreateRenderStateContexts; + RecreateRenderStateContexts.Reserve(ComponentsToUpdate.Num()); + + for (ULandscapeComponent* Component : ComponentsToUpdate) + { + RecreateRenderStateContexts.Emplace(Component); + } + TOptional MaterialUpdateContext; + MaterialUpdateContext.Emplace(FMaterialUpdateContext::EOptions::Default & ~FMaterialUpdateContext::EOptions::RecreateRenderStates); + + for (ULandscapeComponent* Component : ComponentsToUpdate) + { + int32 MaxLOD = FMath::CeilLogTwo(SubsectionSizeQuads + 1) - 1; + TMap NewMaterialPerLOD; + Component->LODIndexToMaterialIndex.SetNumUninitialized(MaxLOD + 1); + int8 LastLODIndex = INDEX_NONE; + + UMaterialInterface* BaseMaterial = GetLandscapeMaterial(); + UMaterialInterface* LOD0Material = GetLandscapeMaterial(0); + + for (int32 LODIndex = 0; LODIndex <= MaxLOD; ++LODIndex) + { + UMaterialInterface* CurrentMaterial = GetLandscapeMaterial(LODIndex); + + // if we have a LOD0 override, do not let the base material override it, it should override everything! + if (CurrentMaterial == BaseMaterial && BaseMaterial != LOD0Material) + { + CurrentMaterial = LOD0Material; + } + + const int8* MaterialLOD = NewMaterialPerLOD.Find(CurrentMaterial); + + if (MaterialLOD != nullptr) + { + Component->LODIndexToMaterialIndex[LODIndex] = *MaterialLOD > LastLODIndex ? *MaterialLOD : LastLODIndex; + } + else + { + int32 AddedIndex = NewMaterialPerLOD.Num(); + NewMaterialPerLOD.Add(CurrentMaterial, LODIndex); + Component->LODIndexToMaterialIndex[LODIndex] = AddedIndex; + LastLODIndex = AddedIndex; + } + } + + Component->MaterialPerLOD = NewMaterialPerLOD; + + Component->MaterialInstances.SetNumZeroed(Component->MaterialPerLOD.Num()/* * 2*/); // over allocate in case we are using tessellation + Component->MaterialIndexToDisabledTessellationMaterial.Init(INDEX_NONE, MaxLOD + 1); + int8 TessellatedMaterialCount = 0; + int8 MaterialIndex = 0; + + TArray WeightmapBaseLayerAllocation = Component->GetWeightmapLayerAllocations(); // We copy the array here + /*const TArray* ZeroAllocations = InZeroAllocationsPerComponents.Find(Component); + + // We have some allocation to remove + if (ZeroAllocations != nullptr) + { + for (int32 i = WeightmapBaseLayerAllocation.Num() - 1; i >= 0; --i) + { + const FWeightmapLayerAllocationInfo& Allocation = WeightmapBaseLayerAllocation[i]; + + if (ZeroAllocations->Contains(Allocation.LayerInfo)) + { + WeightmapBaseLayerAllocation.RemoveAt(i); + } + } + } + */ + + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(); + UTexture2D* Heightmap = Component->GetHeightmap(); + + for (auto& ItPair : Component->MaterialPerLOD) + { + const int8 MaterialLOD = ItPair.Value; + + // Find or set a matching MIC in the Landscape's map. + UMaterialInstanceConstant* CombinationMaterialInstance = Component->GetCombinationMaterial(nullptr, WeightmapBaseLayerAllocation, MaterialLOD, false); + + if (CombinationMaterialInstance != nullptr) + { + UMaterialInstanceConstant* MaterialInstance = Component->MaterialInstances[MaterialIndex]; + bool NeedToCreateMIC = MaterialInstance == nullptr; + + if (NeedToCreateMIC) + { + // Create the instance for this component, that will use the layer combination instance. + MaterialInstance = NewObject(GetOutermost()); + Component->MaterialInstances[MaterialIndex] = MaterialInstance; + } + + MaterialInstance->SetParentEditorOnly(CombinationMaterialInstance); + + MaterialUpdateContext.GetValue().AddMaterialInstance(MaterialInstance); // must be done after SetParent + + FLinearColor Masks[4] = { FLinearColor(1.0f, 0.0f, 0.0f, 0.0f), FLinearColor(0.0f, 1.0f, 0.0f, 0.0f), FLinearColor(0.0f, 0.0f, 1.0f, 0.0f), FLinearColor(0.0f, 0.0f, 0.0f, 1.0f) }; + + // Set the layer mask + for (int32 AllocIdx = 0; AllocIdx < WeightmapBaseLayerAllocation.Num(); AllocIdx++) + { + FWeightmapLayerAllocationInfo& Allocation = WeightmapBaseLayerAllocation[AllocIdx]; + + FName LayerName = Allocation.LayerInfo == ALandscapeProxy::VisibilityLayer ? UMaterialExpressionLandscapeVisibilityMask::ParameterName : Allocation.LayerInfo ? Allocation.LayerInfo->LayerName : NAME_None; + MaterialInstance->SetVectorParameterValueEditorOnly(FName(*FString::Printf(TEXT("LayerMask_%s"), *LayerName.ToString())), Masks[Allocation.WeightmapTextureChannel]); + } + + // Set the weightmaps + for (int32 i = 0; i < ComponentWeightmapTextures.Num(); i++) + { + MaterialInstance->SetTextureParameterValueEditorOnly(FName(*FString::Printf(TEXT("Weightmap%d"), i)), ComponentWeightmapTextures[i]); + } + + if (NeedToCreateMIC) + { + MaterialInstance->PostEditChange(); + } + + /*// Setup material instance with disabled tessellation + if (CombinationMaterialInstance->GetMaterial()->D3D11TessellationMode != EMaterialTessellationMode::MTM_NoTessellation) + { + int32 TessellatedMaterialIndex = MaterialPerLOD.Num() + TessellatedMaterialCount++; + ULandscapeMaterialInstanceConstant* TessellationMaterialInstance = Cast(MaterialInstances[TessellatedMaterialIndex]); + + if (NeedToCreateMIC || TessellationMaterialInstance == nullptr) + { + TessellationMaterialInstance = NewObject(GetOutermost()); + TessellationMaterialInstance->SetParentEditorOnly(MaterialInstance); + + MaterialInstances[TessellatedMaterialIndex] = TessellationMaterialInstance; + MaterialIndexToDisabledTessellationMaterial[MaterialIndex] = TessellatedMaterialIndex; + + TessellationMaterialInstance->bDisableTessellation = true; + TessellationMaterialInstance->PostEditChange(); + } + + Context.AddMaterialInstance(TessellationMaterialInstance); // must be done after SetParent + } + */ + } + + ++MaterialIndex; + } + + if (Component->MaterialPerLOD.Num() == 0) + { + Component->MaterialInstances.Empty(1); + Component->MaterialInstances.Add(nullptr); + Component->LODIndexToMaterialIndex.Empty(1); + Component->LODIndexToMaterialIndex.Add(0); + } + } + + // End material update + MaterialUpdateContext.Reset(); + + // Recreate the render state for our components, needed to update the static drawlist which has cached the MaterialRenderProxies + // Must be after the FMaterialUpdateContext is destroyed + RecreateRenderStateContexts.Empty(); +} + +void ALandscape::ResolveProceduralWeightmapTexture(const TArray& InAllLandscapes) +{ + SCOPE_CYCLE_COUNTER(STAT_LandscapeResolveProceduralWeightmap); + + for (ALandscapeProxy* Landscape : InAllLandscapes) + { + TArray> MipData; + + for (auto& ItPair : Landscape->WeightmapCPUReadBackTextures) + { + UTexture2D* OriginalWeightmap = ItPair.Key; + FLandscapeProceduralTexture2DCPUReadBackResource* WeightmapsCPUReadBack = ItPair.Value; + + if (WeightmapsCPUReadBack == nullptr) + { + continue; + } + + ResolveProceduralTexture(WeightmapsCPUReadBack, OriginalWeightmap); + } + } +} + +void ALandscape::RequestProceduralContentUpdate(uint32 InDataFlags, bool InUpdateAllMaterials) { ProceduralContentUpdateFlags = InDataFlags; + ProceduralUpdateAllMaterials = InUpdateAllMaterials; } void ALandscape::RegenerateProceduralContent() @@ -1853,35 +4104,556 @@ void ALandscape::RegenerateProceduralContent() if ((ProceduralContentUpdateFlags & Heightmap_Setup) != 0 || (ProceduralContentUpdateFlags & Weightmap_Setup) != 0) { SetupProceduralLayers(); + ProceduralContentUpdateFlags &= ~(EProceduralContentUpdateFlag::All_Setup); } RegenerateProceduralHeightmaps(); RegenerateProceduralWeightmaps(); } -void ALandscape::OnPreSaveWorld(uint32 SaveFlags, UWorld* World) +void ALandscape::TickProcedural(float DeltaTime, ELevelTick TickType, FActorTickFunction& ThisTickFunction) { - if (GetMutableDefault()->bProceduralLandscape) + check(GIsEditor); + + UWorld* World = GetWorld(); + if (World && !World->IsPlayInEditor()) { - // Need to perform setup here, as it's possible to get here with the data not setup, when doing a Save As on a level - if (PreviousExperimentalLandscapeProcedural != GetMutableDefault()->bProceduralLandscape) + if (GetMutableDefault()->bProceduralLandscape) { - PreviousExperimentalLandscapeProcedural = GetMutableDefault()->bProceduralLandscape; - RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All_Setup | EProceduralContentUpdateFlag::All_WithDDCUpdate); + if (PreviousExperimentalLandscapeProcedural != GetMutableDefault()->bProceduralLandscape) + { + PreviousExperimentalLandscapeProcedural = GetMutableDefault()->bProceduralLandscape; + + RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All_Setup); + } + + // If doing editing while shader are compiling or at load of a map, it's possible we will need another update pass after shader are completed to see the correct result + const int32 RemainingShadersThisFrame = GShaderCompilingManager->GetNumRemainingJobs(); + + if (!WasCompilingShaders && RemainingShadersThisFrame > 0) + { + WasCompilingShaders = true; + } + else if (WasCompilingShaders) + { + WasCompilingShaders = false; + RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All); + } + + RegenerateProceduralContent(); } else { - RequestProceduralContentUpdate(EProceduralContentUpdateFlag::Heightmap_ResolveToTextureDDC | EProceduralContentUpdateFlag::Weightmap_ResolveToTextureDDC); - } + if (PreviousExperimentalLandscapeProcedural != GetMutableDefault()->bProceduralLandscape) + { + PreviousExperimentalLandscapeProcedural = GetMutableDefault()->bProceduralLandscape; - RegenerateProceduralContent(); - ProceduralContentUpdateFlags = 0; // Force reset so we don't end up performing save info at the next Tick + #if WITH_EDITORONLY_DATA + for (auto& ItPair : RenderDataPerHeightmap) + { + FRenderDataPerHeightmap& HeightmapRenderData = ItPair.Value; + + if (HeightmapRenderData.HeightmapsCPUReadBack != nullptr) + { + BeginReleaseResource(HeightmapRenderData.HeightmapsCPUReadBack); + } + } + + for (auto& ItPair : WeightmapCPUReadBackTextures) + { + FLandscapeProceduralTexture2DCPUReadBackResource* WeightmapCPUReadBack = ItPair.Value; + + if (WeightmapCPUReadBack != nullptr) + { + BeginReleaseResource(WeightmapCPUReadBack); + } + } + + if (CombinedProcLayerWeightmapAllLayersResource != nullptr) + { + BeginReleaseResource(CombinedProcLayerWeightmapAllLayersResource); + } + + if (CurrentProcLayerWeightmapAllLayersResource != nullptr) + { + BeginReleaseResource(CurrentProcLayerWeightmapAllLayersResource); + } + + if (WeightmapScratchExtractLayerTextureResource != nullptr) + { + BeginReleaseResource(WeightmapScratchExtractLayerTextureResource); + } + + if (WeightmapScratchPackLayerTextureResource != nullptr) + { + BeginReleaseResource(WeightmapScratchPackLayerTextureResource); + } + + FlushRenderingCommands(); + + for (auto& ItPair : RenderDataPerHeightmap) + { + FRenderDataPerHeightmap& HeightmapRenderData = ItPair.Value; + + delete HeightmapRenderData.HeightmapsCPUReadBack; + HeightmapRenderData.HeightmapsCPUReadBack = nullptr; + } + + for (auto& ItPair : WeightmapCPUReadBackTextures) + { + FLandscapeProceduralTexture2DCPUReadBackResource* WeightmapCPUReadBack = ItPair.Value; + + delete WeightmapCPUReadBack; + WeightmapCPUReadBack = nullptr; + } + + delete CombinedProcLayerWeightmapAllLayersResource; + delete CurrentProcLayerWeightmapAllLayersResource; + delete WeightmapScratchExtractLayerTextureResource; + delete WeightmapScratchPackLayerTextureResource; + + CombinedProcLayerWeightmapAllLayersResource = nullptr; + CurrentProcLayerWeightmapAllLayersResource = nullptr; + WeightmapScratchExtractLayerTextureResource = nullptr; + WeightmapScratchPackLayerTextureResource = nullptr; + #endif + } + } } } -void ALandscape::OnPostSaveWorld(uint32 SaveFlags, UWorld* World, bool bSuccess) -{ -} -#endif //WITH_EDITOR +#endif -#undef LOCTEXT_NAMESPACE \ No newline at end of file +void ALandscapeProxy::BeginDestroy() +{ +#if WITH_EDITORONLY_DATA + if (GetMutableDefault()->bProceduralLandscape) + { + for (auto& ItPair : RenderDataPerHeightmap) + { + FRenderDataPerHeightmap& HeightmapRenderData = ItPair.Value; + + if (HeightmapRenderData.HeightmapsCPUReadBack != nullptr) + { + BeginReleaseResource(HeightmapRenderData.HeightmapsCPUReadBack); + } + } + + for (auto& ItPair : WeightmapCPUReadBackTextures) + { + FLandscapeProceduralTexture2DCPUReadBackResource* WeightmapCPUReadBack = ItPair.Value; + + if (WeightmapCPUReadBack != nullptr) + { + BeginReleaseResource(WeightmapCPUReadBack); + } + } + + ReleaseResourceFence.BeginFence(); + } +#endif + + Super::BeginDestroy(); +} + +bool ALandscapeProxy::IsReadyForFinishDestroy() +{ + bool bReadyForFinishDestroy = Super::IsReadyForFinishDestroy(); + +#if WITH_EDITORONLY_DATA + if (GetMutableDefault()->bProceduralLandscape) + { + if (bReadyForFinishDestroy) + { + bReadyForFinishDestroy = ReleaseResourceFence.IsFenceComplete(); + } + } +#endif + + return bReadyForFinishDestroy; +} + +void ALandscapeProxy::FinishDestroy() +{ +#if WITH_EDITORONLY_DATA + if (GetMutableDefault()->bProceduralLandscape) + { + check(ReleaseResourceFence.IsFenceComplete()); + + for (auto& ItPair : RenderDataPerHeightmap) + { + FRenderDataPerHeightmap& HeightmapRenderData = ItPair.Value; + + delete HeightmapRenderData.HeightmapsCPUReadBack; + HeightmapRenderData.HeightmapsCPUReadBack = nullptr; + } + + for (auto& ItPair : WeightmapCPUReadBackTextures) + { + FLandscapeProceduralTexture2DCPUReadBackResource* WeightmapCPUReadBack = ItPair.Value; + + delete WeightmapCPUReadBack; + WeightmapCPUReadBack = nullptr; + } + } +#endif + + Super::FinishDestroy(); +} + +void ALandscape::BeginDestroy() +{ +#if WITH_EDITOR + if (GetMutableDefault()->bProceduralLandscape) + { + if (CombinedProcLayerWeightmapAllLayersResource != nullptr) + { + BeginReleaseResource(CombinedProcLayerWeightmapAllLayersResource); + } + + if (CurrentProcLayerWeightmapAllLayersResource != nullptr) + { + BeginReleaseResource(CurrentProcLayerWeightmapAllLayersResource); + } + + if (WeightmapScratchExtractLayerTextureResource != nullptr) + { + BeginReleaseResource(WeightmapScratchExtractLayerTextureResource); + } + + if (WeightmapScratchPackLayerTextureResource != nullptr) + { + BeginReleaseResource(WeightmapScratchPackLayerTextureResource); + } + + // Use ResourceFence from base class + } +#endif + + Super::BeginDestroy(); +} + +void ALandscape::FinishDestroy() +{ +#if WITH_EDITORONLY_DATA + if (GetMutableDefault()->bProceduralLandscape) + { + check(ReleaseResourceFence.IsFenceComplete()); + + delete CombinedProcLayerWeightmapAllLayersResource; + delete CurrentProcLayerWeightmapAllLayersResource; + delete WeightmapScratchExtractLayerTextureResource; + delete WeightmapScratchPackLayerTextureResource; + + CombinedProcLayerWeightmapAllLayersResource = nullptr; + CurrentProcLayerWeightmapAllLayersResource = nullptr; + WeightmapScratchExtractLayerTextureResource = nullptr; + WeightmapScratchPackLayerTextureResource = nullptr; + } +#endif + + Super::FinishDestroy(); +} + +#if WITH_EDITOR +bool ALandscape::IsProceduralLayerNameUnique(const FName& InName) const +{ + return Algo::CountIf(ProceduralLayers, [InName](const FProceduralLayer& Layer) { return (Layer.Name == InName); }) == 0; +} + +void ALandscape::SetProceduralLayerName(int32 InLayerIndex, const FName& InName) +{ + FProceduralLayer* Layer = GetProceduralLayer(InLayerIndex); + ULandscapeInfo* LandscapeInfo = GetLandscapeInfo(); + if (!LandscapeInfo || !Layer || Layer->Name == InName) + { + return; + } + + if (!IsProceduralLayerNameUnique(InName)) + { + return; + } + + ProceduralLayers[InLayerIndex].Name = InName; +} + +void ALandscape::SetProceduralLayerAlpha(int32 InLayerIndex, const float InAlpha, bool bInHeightmap) +{ + FProceduralLayer* Layer = GetProceduralLayer(InLayerIndex); + if (!Layer) + { + return; + } + float& LayerAlpha = bInHeightmap ? Layer->HeightmapAlpha : Layer->WeightmapAlpha; + if (LayerAlpha == InAlpha) + { + return; + } + + LayerAlpha = InAlpha; + RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All, true); +} + +void ALandscape::SetProceduralLayerVisibility(int32 InLayerIndex, bool bInVisible) +{ + FProceduralLayer* Layer = GetProceduralLayer(InLayerIndex); + if (!Layer || Layer->bVisible == bInVisible) + { + return; + } + + Layer->bVisible = bInVisible; + RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All, true); +} + +FProceduralLayer* ALandscape::GetProceduralLayer(int32 InLayerIndex) +{ + if (ProceduralLayers.IsValidIndex(InLayerIndex)) + { + return &ProceduralLayers[InLayerIndex]; + } + return nullptr; +} + +const FProceduralLayer* ALandscape::GetProceduralLayer(int32 InLayerIndex) const +{ + if (ProceduralLayers.IsValidIndex(InLayerIndex)) + { + return &ProceduralLayers[InLayerIndex]; + } + return nullptr; +} + +void ALandscape::DeleteProceduralLayer(int32 InLayerIndex) +{ + ensure(GetMutableDefault()->bProceduralLandscape); + + ULandscapeInfo* LandscapeInfo = GetLandscapeInfo(); + const FProceduralLayer* Layer = GetProceduralLayer(InLayerIndex); + if (!LandscapeInfo || !Layer || ProceduralLayers.Num() <= 1) + { + return; + } + + Modify(); + FGuid LayerGuid = Layer->Guid; + + // Clean up Weightmap usage in LandscapeProxies + LandscapeInfo->ForAllLandscapeProxies([LayerGuid](ALandscapeProxy* Proxy) + { + const FProceduralLayerData* LayerData = Proxy->ProceduralLayersData.Find(LayerGuid); + if (LayerData) + { + for (ULandscapeComponent* Component : Proxy->LandscapeComponents) + { + const FWeightmapLayerData* WeightmapLayer = LayerData->WeightmapData.Find(Component); + if (WeightmapLayer) + { + for (const FWeightmapLayerAllocationInfo& Allocation : WeightmapLayer->WeightmapLayerAllocations) + { + UTexture2D* WeightmapTexture = WeightmapLayer->Weightmaps[Allocation.WeightmapTextureIndex]; + ULandscapeWeightmapUsage** Usage = Proxy->WeightmapUsageMap.Find(WeightmapTexture); + if (Usage != nullptr && (*Usage) != nullptr) + { + (*Usage)->ChannelUsage[Allocation.WeightmapTextureChannel] = nullptr; + if ((*Usage)->FreeChannelCount() == 4) + { + Proxy->WeightmapUsageMap.Remove(WeightmapTexture); + } + } + } + } + } + } + }); + + // Remove associated layer data of each landscape proxy + LandscapeInfo->ForAllLandscapeProxies([LayerGuid](ALandscapeProxy* Proxy) + { + Proxy->ProceduralLayersData.Remove(LayerGuid); + }); + + // Remove layer from list + ProceduralLayers.RemoveAt(InLayerIndex); + + // Request Update + RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All_Setup | All, true); +} + +void ALandscape::ClearProceduralLayer(int32 InLayerIndex) +{ + const FProceduralLayer* Layer = GetProceduralLayer(InLayerIndex); + if (Layer) + { + ClearProceduralLayer(Layer->Guid); + } +} + +void ALandscape::ClearProceduralLayer(const FGuid& InLayerGuid) +{ + ensure(GetMutableDefault()->bProceduralLandscape); + + FProceduralLayer* Layer = ProceduralLayers.FindByPredicate([InLayerGuid](const FProceduralLayer& Other) { return Other.Guid == InLayerGuid; }); + ULandscapeInfo* LandscapeInfo = GetLandscapeInfo(); + if (!LandscapeInfo || !Layer) + { + return; + } + + Modify(); + FScopedSetLandscapeCurrentEditingProceduralLayer Scope(this, Layer ? Layer->Guid : FGuid(), [=] { RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All, true); }); + + TArray NewData; + NewData.AddZeroed(FMath::Square(ComponentSizeQuads + 1)); + uint16 ZeroValue = LandscapeDataAccess::GetTexHeight(0.f); + for (uint16& NewDataValue : NewData) + { + NewDataValue = ZeroValue; + } + + FLandscapeEditDataInterface LandscapeEdit(LandscapeInfo); + LandscapeInfo->ForAllLandscapeProxies([&](ALandscapeProxy* Proxy) + { + for (ULandscapeComponent* Component : Proxy->LandscapeComponents) + { + int32 MinX = MAX_int32; + int32 MinY = MAX_int32; + int32 MaxX = MIN_int32; + int32 MaxY = MIN_int32; + Component->GetComponentExtent(MinX, MinY, MaxX, MaxY); + check(ComponentSizeQuads == (MaxX - MinX)); + check(ComponentSizeQuads == (MaxY - MinY)); + + TArray OldData; + OldData.AddZeroed((1 + MaxY - MinY) * (1 + MaxX - MinX)); + + LandscapeEdit.GetHeightData(MinX, MinY, MaxX, MaxY, OldData.GetData(), 0); + if (FMemory::Memcmp(OldData.GetData(), NewData.GetData(), NewData.Num() * NewData.GetTypeSize()) != 0) + { + LandscapeEdit.SetHeightData(MinX, MinY, MaxX, MaxY, NewData.GetData(), 0, true); + } + + // Clear weight maps + for (FLandscapeInfoLayerSettings& LayerSettings : LandscapeInfo->Layers) + { + Component->DeleteLayer(LayerSettings.LayerInfoObj, LandscapeEdit); + } + } + }); +} + +void ALandscape::ShowOnlySelectedProceduralLayer(int32 InLayerIndex) +{ + const FProceduralLayer* VisibleLayer = GetProceduralLayer(InLayerIndex); + if (VisibleLayer) + { + for (FProceduralLayer& Layer : ProceduralLayers) + { + Layer.bVisible = (&Layer == VisibleLayer); + } + RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All, true); + } +} + +void ALandscape::ShowAllProceduralLayers() +{ + if (ProceduralLayers.Num() > 0) + { + for (FProceduralLayer& Layer : ProceduralLayers) + { + Layer.bVisible = true; + } + RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All, true); + } +} + +FScopedSetLandscapeCurrentEditingProceduralLayer::FScopedSetLandscapeCurrentEditingProceduralLayer(ALandscape* InLandscape, const FGuid& InProceduralLayer, TFunction InCompletionCallback) + : Landscape(InLandscape) + , ProceduralLayer(InProceduralLayer) + , CompletionCallback(MoveTemp(InCompletionCallback)) +{ + if (GetMutableDefault()->bProceduralLandscape && Landscape.IsValid() && ProceduralLayer.IsValid()) + { + Landscape->SetCurrentEditingProceduralLayer(ProceduralLayer); + } +} + +FScopedSetLandscapeCurrentEditingProceduralLayer::~FScopedSetLandscapeCurrentEditingProceduralLayer() +{ + if (GetMutableDefault()->bProceduralLandscape && Landscape.IsValid() && ProceduralLayer.IsValid()) + { + Landscape->SetCurrentEditingProceduralLayer(); + if (CompletionCallback) + { + CompletionCallback(); + } + } +} + +void ALandscape::SetCurrentEditingProceduralLayer(FGuid InLayerGuid) +{ + ensure(GetMutableDefault()->bProceduralLandscape); + + ULandscapeInfo* LandscapeInfo = GetLandscapeInfo(); + if (!LandscapeInfo) + { + return; + } + + LandscapeInfo->ForAllLandscapeProxies([InLayerGuid, this](ALandscapeProxy* Proxy) + { + FProceduralLayer* Layer = ProceduralLayers.FindByPredicate([InLayerGuid](const FProceduralLayer& Other) { return Other.Guid == InLayerGuid; }); + FProceduralLayerData* LayerData = Layer ? Proxy->ProceduralLayersData.Find(Layer->Guid) : nullptr; + + for (ULandscapeComponent* Component : Proxy->LandscapeComponents) + { + Component->SetCurrentEditingProceduralLayer(Layer, LayerData); + Component->MarkRenderStateDirty(); + } + }); +} + +void ALandscape::CreateProceduralLayer(FName InName, bool bInUpdateProceduralContent) +{ + ULandscapeInfo* LandscapeInfo = GetLandscapeInfo(); + if (!LandscapeInfo || !GetMutableDefault()->bProceduralLandscape) + { + return; + } + + Modify(); + FProceduralLayer NewLayer; + NewLayer.Name = GenerateUniqueProceduralLayerName(InName); + ProceduralLayers.Add(NewLayer); + + // Create associated layer data in each landscape proxy + LandscapeInfo->ForAllLandscapeProxies([NewLayer](ALandscapeProxy* Proxy) + { + Proxy->ProceduralLayersData.Add(NewLayer.Guid, FProceduralLayerData()); + }); + + if (bInUpdateProceduralContent) + { + // Request Update + RequestProceduralContentUpdate(EProceduralContentUpdateFlag::All_Setup); + RegenerateProceduralContent(); + } +} + +FName ALandscape::GenerateUniqueProceduralLayerName(FName InName) const +{ + FString BaseName = InName == NAME_None ? "Layer" : InName.ToString(); + FName NewName; + int32 LayerIndex = 0; + do + { + ++LayerIndex; + NewName = FName(*FString::Printf(TEXT("%s%d"), *BaseName, LayerIndex)); + } while (ProceduralLayers.ContainsByPredicate([NewName](const FProceduralLayer& Layer) { return Layer.Name == NewName; })); + + return NewName; +} +#endif // WITH_EDITOR + +#undef LOCTEXT_NAMESPACE diff --git a/Engine/Source/Runtime/Landscape/Private/LandscapeGrass.cpp b/Engine/Source/Runtime/Landscape/Private/LandscapeGrass.cpp index 473db4837a60..5dea0db390b2 100644 --- a/Engine/Source/Runtime/Landscape/Private/LandscapeGrass.cpp +++ b/Engine/Source/Runtime/Landscape/Private/LandscapeGrass.cpp @@ -2120,7 +2120,9 @@ void ALandscapeProxy::UpdateGrass(const TArray& Cameras, bool bForceSyn { CurrentForcedStreamedTextures.Add(Heightmap); } - for (auto WeightmapTexture : Component->WeightmapTextures) + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(); + + for (auto WeightmapTexture : ComponentWeightmapTextures) { if (WeightmapTexture->bForceMiplevelsToBeResident) { @@ -2353,7 +2355,9 @@ void ALandscapeProxy::UpdateGrass(const TArray& Cameras, bool bForceSyn { // we're ready to generate but our textures need streaming in DesiredForceStreamedTextures.Add(Component->GetHeightmap()); - for (auto WeightmapTexture : Component->WeightmapTextures) + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(); + + for (auto WeightmapTexture : ComponentWeightmapTextures) { DesiredForceStreamedTextures.Add(WeightmapTexture); } @@ -2526,7 +2530,9 @@ void ALandscapeProxy::UpdateGrass(const TArray& Cameras, bool bForceSyn // Force stream in other heightmaps but only if we're not waiting for the textures // near the camera to stream in DesiredForceStreamedTextures.Add(Component->GetHeightmap()); - for (auto WeightmapTexture : Component->WeightmapTextures) + TArray& ComponentWeightmapTextures = Component->GetWeightmapTextures(); + + for (auto WeightmapTexture : ComponentWeightmapTextures) { DesiredForceStreamedTextures.Add(WeightmapTexture); } diff --git a/Engine/Source/Runtime/Landscape/Private/LandscapePrivate.h b/Engine/Source/Runtime/Landscape/Private/LandscapePrivate.h index fffb5bb21ae4..42de3369a205 100644 --- a/Engine/Source/Runtime/Landscape/Private/LandscapePrivate.h +++ b/Engine/Source/Runtime/Landscape/Private/LandscapePrivate.h @@ -21,6 +21,18 @@ DECLARE_CYCLE_STAT_EXTERN(TEXT("PostInit View Custom Data"), STAT_LandscapePostI DECLARE_CYCLE_STAT_EXTERN(TEXT("Compute Custom Mesh Batch LOD"), STAT_LandscapeComputeCustomMeshBatchLOD, STATGROUP_Landscape, ); DECLARE_CYCLE_STAT_EXTERN(TEXT("Compute Custom Shadow Mesh Batch LOD"), STAT_LandscapeComputeCustomShadowMeshBatchLOD, STATGROUP_Landscape, ); +DECLARE_CYCLE_STAT_EXTERN(TEXT("Regenerate Procedural (RenderThread)"), STAT_LandscapeRegenerateProcedural_RenderThread, STATGROUP_Landscape, ); +DECLARE_CYCLE_STAT_EXTERN(TEXT("Regenerate Procedural Heightmap (GameThread)"), STAT_LandscapeRegenerateProceduralHeightmaps, STATGROUP_Landscape, ); +DECLARE_CYCLE_STAT_EXTERN(TEXT("Resolve Procedural Heightmap"), STAT_LandscapeResolveProceduralHeightmap, STATGROUP_Landscape, ); + +DECLARE_CYCLE_STAT_EXTERN(TEXT("Regenerate Procedural Weightmap (GameThread)"), STAT_LandscapeRegenerateProceduralWeightmaps, STATGROUP_Landscape, ); + +DECLARE_CYCLE_STAT_EXTERN(TEXT("Resolve Procedural Weightmap"), STAT_LandscapeResolveProceduralWeightmap, STATGROUP_Landscape, ); +DECLARE_CYCLE_STAT_EXTERN(TEXT("Reallocate Procedural Weightmaps"), STAT_LandscapeReallocateProceduralWeightmaps, STATGROUP_Landscape, ); +DECLARE_CYCLE_STAT_EXTERN(TEXT("Update Procedural Material Instances"), STAT_LandscapeProceduralUpdateMaterialInstance, STATGROUP_Landscape, ); + +DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Regenerate Procedural DrawCalls"), STAT_LandscapeRegenerateProceduralDrawCalls, STATGROUP_Landscape, ); + DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Components Using SubSection DrawCall"), STAT_LandscapeComponentUsingSubSectionDrawCalls, STATGROUP_Landscape, ); DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Tessellated Shadow Cascade"), STAT_LandscapeTessellatedShadowCascade, STATGROUP_Landscape, ); DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Tessellated Components"), STAT_LandscapeTessellatedComponents, STATGROUP_Landscape, ); @@ -28,12 +40,6 @@ DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Processed Triangles"), STAT_LandscapeTri DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Render Passes"), STAT_LandscapeComponentRenderPasses, STATGROUP_Landscape, ); DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("DrawCalls"), STAT_LandscapeDrawCalls, STATGROUP_Landscape, ); -DECLARE_CYCLE_STAT_EXTERN(TEXT("Regenerate Procedural Heightmap (GameThread)"), STAT_LandscapeRegenerateProceduralHeightmaps, STATGROUP_Landscape, ); -DECLARE_CYCLE_STAT_EXTERN(TEXT("Regenerate Procedural Heightmap (RenderThread)"), STAT_LandscapeRegenerateProceduralHeightmaps_RenderThread, STATGROUP_Landscape, ); -DECLARE_CYCLE_STAT_EXTERN(TEXT("Resolve Procedural Heightmap"), STAT_LandscapeResolveProceduralHeightmap, STATGROUP_Landscape, ); - -DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Regenerate Procedural Heightmap DrawCalls"), STAT_LandscapeRegenerateProceduralHeightmapsDrawCalls, STATGROUP_Landscape, ); - DECLARE_MEMORY_STAT_EXTERN(TEXT("Vertex Mem"), STAT_LandscapeVertexMem, STATGROUP_Landscape, ); DECLARE_MEMORY_STAT_EXTERN(TEXT("Occluder Mem"), STAT_LandscapeOccluderMem, STATGROUP_Landscape, ); DECLARE_DWORD_COUNTER_STAT_EXTERN(TEXT("Component Mem"), STAT_LandscapeComponentMem, STATGROUP_Landscape, ); diff --git a/Engine/Source/Runtime/Landscape/Private/LandscapeRender.cpp b/Engine/Source/Runtime/Landscape/Private/LandscapeRender.cpp index 81164647da5d..046b99c95bec 100644 --- a/Engine/Source/Runtime/Landscape/Private/LandscapeRender.cpp +++ b/Engine/Source/Runtime/Landscape/Private/LandscapeRender.cpp @@ -614,7 +614,7 @@ FLandscapeComponentSceneProxy::FLandscapeComponentSceneProxy(ULandscapeComponent , FLandscapeNeighborInfo(InComponent->GetWorld(), InComponent->GetLandscapeProxy()->GetLandscapeGuid(), InComponent->GetSectionBase() / InComponent->ComponentSizeQuads, InComponent->GetHeightmap(), InComponent->ForcedLOD, InComponent->LODBias) , MaxLOD(FMath::CeilLogTwo(InComponent->SubsectionSizeQuads + 1) - 1) , UseTessellationComponentScreenSizeFalloff(InComponent->GetLandscapeProxy()->UseTessellationComponentScreenSizeFalloff) - , NumWeightmapLayerAllocations(InComponent->WeightmapLayerAllocations.Num()) + , NumWeightmapLayerAllocations(InComponent->GetWeightmapLayerAllocations().Num()) , StaticLightingLOD(InComponent->GetLandscapeProxy()->StaticLightingLOD) , WeightmapSubsectionOffset(InComponent->WeightmapSubsectionOffset) , FirstLOD(0) @@ -631,7 +631,7 @@ FLandscapeComponentSceneProxy::FLandscapeComponentSceneProxy(ULandscapeComponent , SectionBase(InComponent->GetSectionBase()) , LandscapeComponent(InComponent) , WeightmapScaleBias(InComponent->WeightmapScaleBias) - , WeightmapTextures(InComponent->WeightmapTextures) + , WeightmapTextures(InComponent->GetWeightmapTextures()) , NormalmapTexture(InComponent->GetHeightmap()) , BaseColorForGITexture(InComponent->GIBakedBaseColorTexture) , HeightmapScaleBias(InComponent->HeightmapScaleBias) @@ -839,7 +839,9 @@ FLandscapeComponentSceneProxy::FLandscapeComponentSceneProxy(ULandscapeComponent bSupportsHeightfieldRepresentation = true; #if WITH_EDITOR - for (auto& Allocation : InComponent->WeightmapLayerAllocations) + TArray& ComponentWeightmapLayerAllocations = InComponent->GetWeightmapLayerAllocations(); + + for (auto& Allocation : ComponentWeightmapLayerAllocations) { if (Allocation.LayerInfo != nullptr) { @@ -1792,7 +1794,6 @@ void FLandscapeComponentSceneProxy::CalculateBatchElementLOD(const FSceneView& I float SubSectionMaxExtend = ComponentMaxExtend / 2.0f; float SubSectionRadius = LandscapeComponent->Bounds.SphereRadius / 2.0f; - const FSceneView& View = GetLODView(InView); float CombinedScreenRatio = 0.0f; bool AllSubSectionHaveSameScreenSize = true; @@ -1804,11 +1805,11 @@ void FLandscapeComponentSceneProxy::CalculateBatchElementLOD(const FSceneView& I int32 SubSectionIndex = SubX + SubY * NumSubsections; FViewCustomDataSubSectionLOD& SubSectionLODData = InOutLODData.SubSections[SubSectionIndex]; - SubSectionLODData.ScreenSizeSquared = GetComponentScreenSize(&View, SubSectionScreenSizeTestingPosition[SubSectionIndex], SubSectionMaxExtend, SubSectionRadius); + SubSectionLODData.ScreenSizeSquared = GetComponentScreenSize(&InView, SubSectionScreenSizeTestingPosition[SubSectionIndex], SubSectionMaxExtend, SubSectionRadius); check(SubSectionLODData.ScreenSizeSquared > 0.0f); - CalculateLODFromScreenSize(View, SubSectionLODData.ScreenSizeSquared, InViewLODScale, SubSectionIndex, InOutLODData); + CalculateLODFromScreenSize(InView, SubSectionLODData.ScreenSizeSquared, InViewLODScale, SubSectionIndex, InOutLODData); check(SubSectionLODData.fBatchElementCurrentLOD != -1.0f); InOutLODData.ShaderCurrentLOD.Component(SubSectionIndex) = SubSectionLODData.fBatchElementCurrentLOD; @@ -1945,7 +1946,6 @@ void* FLandscapeComponentSceneProxy::InitViewCustomData(const FSceneView& InView // NOTE: we can't access other proxy here as this can be run in parallel we need to wait for the PostInitViewCustomData which is run in synchronous PrimitiveCustomDataIndex = GetPrimitiveSceneInfo()->GetIndex(); - const FSceneView& View = GetLODView(InView); FViewCustomDataLOD* LODData = (FViewCustomDataLOD*)new(InCustomDataMemStack) FViewCustomDataLOD(); @@ -1955,10 +1955,10 @@ void* FLandscapeComponentSceneProxy::InitViewCustomData(const FSceneView& InView // If a valid screen size was provided, we use it instead of recomputing it if (InMeshScreenSizeSquared < 0.0f) { - LODData->ComponentScreenSize = GetComponentScreenSize(&View, LandscapeComponent->Bounds.Origin, ComponentMaxExtend, LandscapeComponent->Bounds.SphereRadius); + LODData->ComponentScreenSize = GetComponentScreenSize(&InView, LandscapeComponent->Bounds.Origin, ComponentMaxExtend, LandscapeComponent->Bounds.SphereRadius); } - CalculateBatchElementLOD(View, LODData->ComponentScreenSize, InViewLODScale, *LODData, false); + CalculateBatchElementLOD(InView, LODData->ComponentScreenSize, InViewLODScale, *LODData, false); if (InIsStaticRelevant) { @@ -1986,7 +1986,7 @@ void* FLandscapeComponentSceneProxy::InitViewCustomData(const FSceneView& InView LODData->LodBias = GetShaderLODBias(); } - ComputeTessellationFalloffShaderValues(*LODData, View.ViewMatrices.GetProjectionMatrix(), LODData->LodTessellationParams.X, LODData->LodTessellationParams.Y); + ComputeTessellationFalloffShaderValues(*LODData, InView.ViewMatrices.GetProjectionMatrix(), LODData->LodTessellationParams.X, LODData->LodTessellationParams.Y); return LODData; } @@ -2156,7 +2156,7 @@ float FLandscapeComponentSceneProxy::GetNeighborLOD(const FSceneView& InView, fl FVector ComponentTopLeftCorner = LandscapeComponentOrigin - FVector(SubSectionMaxExtend, SubSectionMaxExtend, 0.0f); FVector SubSectionOrigin = ComponentTopLeftCorner + FVector(LandscapeComponentMaxExtends * DesiredSubSectionX, LandscapeComponentMaxExtends * DesiredSubSectionY, 0.0f); - float MeshBatchScreenSizeSquared = GetComponentScreenSize(&GetLODView(InView), SubSectionOrigin, SubSectionMaxExtend, LandscapeComponent->Bounds.SphereRadius / 2.0f); + float MeshBatchScreenSizeSquared = GetComponentScreenSize(&InView, SubSectionOrigin, SubSectionMaxExtend, LandscapeComponent->Bounds.SphereRadius / 2.0f); FViewCustomDataLOD NeighborLODData; CalculateLODFromScreenSize(InView, MeshBatchScreenSizeSquared, InView.LODDistanceFactor, DesiredSubSectionIndex, NeighborLODData); @@ -2170,7 +2170,7 @@ float FLandscapeComponentSceneProxy::GetNeighborLOD(const FSceneView& InView, fl } else { - float MeshBatchScreenSizeSquared = GetComponentScreenSize(&GetLODView(InView), LandscapeComponentOrigin, LandscapeComponentMaxExtends, LandscapeComponent->Bounds.SphereRadius); + float MeshBatchScreenSizeSquared = GetComponentScreenSize(&InView, LandscapeComponentOrigin, LandscapeComponentMaxExtends, LandscapeComponent->Bounds.SphereRadius); FViewCustomDataLOD NeighborLODData; CalculateLODFromScreenSize(InView, MeshBatchScreenSizeSquared, InView.LODDistanceFactor, 0, NeighborLODData); @@ -2269,11 +2269,10 @@ bool FLandscapeComponentSceneProxy::CanUseMeshBatchForShadowCascade(int8 InLODIn return MeshBatch->TessellationDisablingShadowMapMeshSize >= WorldUnitsForOneTexel * (GShadowMapWorldUnitsToTexelFactor != -1.0f ? GShadowMapWorldUnitsToTexelFactor : 1.0f); } -FLODMask FLandscapeComponentSceneProxy::GetCustomLOD(const FSceneView& View, float InViewLODScale, int32 InForcedLODLevel, float& OutScreenSizeSquared) const +FLODMask FLandscapeComponentSceneProxy::GetCustomLOD(const FSceneView& InView, float InViewLODScale, int32 InForcedLODLevel, float& OutScreenSizeSquared) const { SCOPE_CYCLE_COUNTER(STAT_LandscapeComputeCustomMeshBatchLOD); FLODMask LODToRender; - const FSceneView& LODView = GetLODView(View); OutScreenSizeSquared = 0.0f; // Handle forced LOD level first @@ -2285,10 +2284,10 @@ FLODMask FLandscapeComponentSceneProxy::GetCustomLOD(const FSceneView& View, flo LODToRender.SetLOD(FMath::Clamp(InForcedLODLevel, MinMeshLOD, MaxMeshLOD)); } - else if (LODView.Family->EngineShowFlags.LOD) + else if (InView.Family->EngineShowFlags.LOD) { int8 PotentialLOD = INDEX_NONE; - OutScreenSizeSquared = GetComponentScreenSize(&LODView, LandscapeComponent->Bounds.Origin, ComponentMaxExtend, LandscapeComponent->Bounds.SphereRadius); + OutScreenSizeSquared = GetComponentScreenSize(&InView, LandscapeComponent->Bounds.Origin, ComponentMaxExtend, LandscapeComponent->Bounds.SphereRadius); if (NumSubsections > 1) { @@ -2296,7 +2295,7 @@ FLODMask FLandscapeComponentSceneProxy::GetCustomLOD(const FSceneView& View, flo float SubSectionRadius = LandscapeComponent->Bounds.SphereRadius / 2.0f; // Compute screen size of each sub section to determine if we should use the combined logic or the individual logic - float ScreenSizeSquared = GetComponentScreenSize(&View, SubSectionScreenSizeTestingPosition[0], SubSectionMaxExtend, SubSectionRadius); + float ScreenSizeSquared = GetComponentScreenSize(&InView, SubSectionScreenSizeTestingPosition[0], SubSectionMaxExtend, SubSectionRadius); PotentialLOD = GetLODFromScreenSize(ScreenSizeSquared, InViewLODScale); } else @@ -2304,7 +2303,7 @@ FLODMask FLandscapeComponentSceneProxy::GetCustomLOD(const FSceneView& View, flo PotentialLOD = GetLODFromScreenSize(OutScreenSizeSquared, InViewLODScale); } - const auto FeatureLevel = View.GetFeatureLevel(); + const auto FeatureLevel = InView.GetFeatureLevel(); bool HasTessellationEnabled = false; if (FeatureLevel >= ERHIFeatureLevel::SM4) @@ -2365,14 +2364,13 @@ FLODMask FLandscapeComponentSceneProxy::GetCustomWholeSceneShadowLOD(const FScen } else { - const FSceneView& LODView = GetLODView(InView); - const FViewCustomDataLOD* PrimitiveCustomData = (const FViewCustomDataLOD*)LODView.GetCustomData(GetPrimitiveSceneInfo()->GetIndex()); + const FViewCustomDataLOD* PrimitiveCustomData = (const FViewCustomDataLOD*)InView.GetCustomData(GetPrimitiveSceneInfo()->GetIndex()); int8 PotentialLOD = INDEX_NONE; float ScreenSizeSquared = 0.0f; if (PrimitiveCustomData == nullptr) { - ScreenSizeSquared = GetComponentScreenSize(&LODView, LandscapeComponent->Bounds.Origin, ComponentMaxExtend, LandscapeComponent->Bounds.SphereRadius); + ScreenSizeSquared = GetComponentScreenSize(&InView, LandscapeComponent->Bounds.Origin, ComponentMaxExtend, LandscapeComponent->Bounds.SphereRadius); if (NumSubsections > 1) { @@ -2380,7 +2378,7 @@ FLODMask FLandscapeComponentSceneProxy::GetCustomWholeSceneShadowLOD(const FScen float SubSectionRadius = LandscapeComponent->Bounds.SphereRadius / 2.0f; // Compute screen size of each sub section to determine if we should use the combined logic or the individual logic - float SubSectionScreenSizeSquared = GetComponentScreenSize(&LODView, SubSectionScreenSizeTestingPosition[0], SubSectionMaxExtend, SubSectionRadius); + float SubSectionScreenSizeSquared = GetComponentScreenSize(&InView, SubSectionScreenSizeTestingPosition[0], SubSectionMaxExtend, SubSectionRadius); PotentialLOD = GetLODFromScreenSize(SubSectionScreenSizeSquared, InViewLODScale); } else @@ -3423,11 +3421,10 @@ public: ShaderBindings.Add(LodValuesParameter, SceneProxy->GetShaderLODValues(BatchElementParams->CurrentLOD)); } - const FSceneView& View = GetLODView(*InView); int32 SubSectionIndex = BatchElementParams->SubX + BatchElementParams->SubY * SceneProxy->NumSubsections; // If we have no custom data for this primitive we will compute of the fly the proper values, this will happen if the shader is not used for normal landscape rendering(i.e grass rendering) - FLandscapeComponentSceneProxy::FViewCustomDataLOD* LODData = (FLandscapeComponentSceneProxy::FViewCustomDataLOD*)View.GetCustomData(SceneProxy->GetPrimitiveSceneInfo()->GetIndex()); + FLandscapeComponentSceneProxy::FViewCustomDataLOD* LODData = (FLandscapeComponentSceneProxy::FViewCustomDataLOD*)InView->GetCustomData(SceneProxy->GetPrimitiveSceneInfo()->GetIndex()); if (LODData != nullptr) { @@ -3488,10 +3485,10 @@ public: } else { - float ComponentScreenSize = SceneProxy->GetComponentScreenSize(&View, SceneProxy->LandscapeComponent->Bounds.Origin, SceneProxy->ComponentMaxExtend, SceneProxy->LandscapeComponent->Bounds.SphereRadius); + float ComponentScreenSize = SceneProxy->GetComponentScreenSize(InView, SceneProxy->LandscapeComponent->Bounds.Origin, SceneProxy->ComponentMaxExtend, SceneProxy->LandscapeComponent->Bounds.SphereRadius); FLandscapeComponentSceneProxy::FViewCustomDataLOD CurrentLODData; - SceneProxy->CalculateBatchElementLOD(*InView, ComponentScreenSize, View.LODDistanceFactor, CurrentLODData, true); + SceneProxy->CalculateBatchElementLOD(*InView, ComponentScreenSize, InView->LODDistanceFactor, CurrentLODData, true); check(CurrentLODData.UseCombinedMeshBatch); if (LodBiasParameter.IsBound()) @@ -3502,7 +3499,7 @@ public: if (LodTessellationParameter.IsBound()) { FVector4 LodTessellationParams(ForceInitToZero); - SceneProxy->ComputeTessellationFalloffShaderValues(CurrentLODData, View.ViewMatrices.GetProjectionMatrix(), LodTessellationParams.X, LodTessellationParams.Y); + SceneProxy->ComputeTessellationFalloffShaderValues(CurrentLODData, InView->ViewMatrices.GetProjectionMatrix(), LodTessellationParams.X, LodTessellationParams.Y); ShaderBindings.Add(LodTessellationParameter, LodTessellationParams); } @@ -3521,7 +3518,7 @@ public: for (int32 SubX = 0; SubX < SceneProxy->NumSubsections; SubX++) { int32 NeighborSubSectionIndex = SubX + SubY * SceneProxy->NumSubsections; - SceneProxy->GetShaderCurrentNeighborLOD(View, CurrentLODData.SubSections[NeighborSubSectionIndex].fBatchElementCurrentLOD, SceneProxy->NumSubsections > 1 ? SubX : INDEX_NONE, SceneProxy->NumSubsections > 1 ? SubY : INDEX_NONE, NeighborSubSectionIndex, CurrentNeighborLOD[NeighborSubSectionIndex]); + SceneProxy->GetShaderCurrentNeighborLOD(*InView, CurrentLODData.SubSections[NeighborSubSectionIndex].fBatchElementCurrentLOD, SceneProxy->NumSubsections > 1 ? SubX : INDEX_NONE, SceneProxy->NumSubsections > 1 ? SubY : INDEX_NONE, NeighborSubSectionIndex, CurrentNeighborLOD[NeighborSubSectionIndex]); check(CurrentNeighborLOD[NeighborSubSectionIndex].X != -1.0f); // they should all match so only check the 1st one for simplicity } } diff --git a/Engine/Source/Runtime/Landscape/Private/LandscapeRenderMobile.cpp b/Engine/Source/Runtime/Landscape/Private/LandscapeRenderMobile.cpp index 7775f00e7cdf..27cfdc504eca 100644 --- a/Engine/Source/Runtime/Landscape/Private/LandscapeRenderMobile.cpp +++ b/Engine/Source/Runtime/Landscape/Private/LandscapeRenderMobile.cpp @@ -339,7 +339,7 @@ FLandscapeComponentSceneProxyMobile::FLandscapeComponentSceneProxyMobile(ULandsc BlendableLayerMask = InComponent->MobileBlendableLayerMask; #if WITH_EDITOR - TArray& LayerAllocations = InComponent->MobileWeightmapLayerAllocations.Num() ? InComponent->MobileWeightmapLayerAllocations : InComponent->WeightmapLayerAllocations; + TArray& LayerAllocations = InComponent->MobileWeightmapLayerAllocations.Num() ? InComponent->MobileWeightmapLayerAllocations : InComponent->GetWeightmapLayerAllocations(); LayerColors.Empty(); for (auto& Allocation : LayerAllocations) { diff --git a/Engine/Source/Runtime/Landscape/Public/LandscapeRender.h b/Engine/Source/Runtime/Landscape/Public/LandscapeRender.h index a64eda7550ff..712899856839 100644 --- a/Engine/Source/Runtime/Landscape/Public/LandscapeRender.h +++ b/Engine/Source/Runtime/Landscape/Public/LandscapeRender.h @@ -682,7 +682,7 @@ public: virtual void* InitViewCustomData(const FSceneView& InView, float InViewLODScale, FMemStackBase& InCustomDataMemStack, bool InIsStaticRelevant, bool InIsShadowOnly, const FLODMask* InVisiblePrimitiveLODMask = nullptr, float InMeshScreenSizeSquared = -1.0f) override; virtual void PostInitViewCustomData(const FSceneView& InView, void* InViewCustomData) const override; virtual bool IsUsingCustomLODRules() const override; - virtual FLODMask GetCustomLOD(const FSceneView& View, float InViewLODScale, int32 InForcedLODLevel, float& OutScreenSizeSquared) const override; + virtual FLODMask GetCustomLOD(const FSceneView& InView, float InViewLODScale, int32 InForcedLODLevel, float& OutScreenSizeSquared) const override; virtual bool IsUsingCustomWholeSceneShadowLODRules() const override; virtual FLODMask GetCustomWholeSceneShadowLOD(const FSceneView& InView, float InViewLODScale, int32 InForcedLODLevel, const struct FLODMask& InVisibilePrimitiveLODMask, float InShadowMapTextureResolution, float InShadowMapCascadeSize, int8 InShadowCascadeId, bool InHasSelfShadow) const override; diff --git a/Engine/Source/Runtime/Launch/Private/IOS/LaunchIOS.cpp b/Engine/Source/Runtime/Launch/Private/IOS/LaunchIOS.cpp index 5b806fc3afca..a2323d838287 100644 --- a/Engine/Source/Runtime/Launch/Private/IOS/LaunchIOS.cpp +++ b/Engine/Source/Runtime/Launch/Private/IOS/LaunchIOS.cpp @@ -253,7 +253,7 @@ void FAppEntry::RestartAudio() void FAppEntry::PreInit(IOSAppDelegate* AppDelegate, UIApplication* Application) { // make a controller object - UIViewController* IOSController = [[IOSViewController alloc] init]; + IOSViewController* IOSController = [[IOSViewController alloc] init]; #if PLATFORM_TVOS // @todo tvos: This may need to be exposed to the game so that when you click Menu it will background the app diff --git a/Engine/Source/Runtime/Launch/Private/LaunchEngineLoop.cpp b/Engine/Source/Runtime/Launch/Private/LaunchEngineLoop.cpp index ce44ba09078b..229a47d8b029 100644 --- a/Engine/Source/Runtime/Launch/Private/LaunchEngineLoop.cpp +++ b/Engine/Source/Runtime/Launch/Private/LaunchEngineLoop.cpp @@ -54,6 +54,8 @@ #include "Misc/NetworkVersion.h" #include "Templates/UniquePtr.h" +#include "GenericPlatform/GenericPlatformInstallBundleManager.h" + #if !(IS_PROGRAM || WITH_EDITOR) #include "IPlatformFilePak.h" #endif @@ -215,10 +217,6 @@ class FFeedbackContext; #define RHI_COMMAND_LIST_DEBUG_TRACES 0 #endif -#ifndef AUTOMATICALLY_HANDLE_INSTALLED_CONTENT_AFTER_EARLY_STARTUP_SCREEN -#define AUTOMATICALLY_HANDLE_INSTALLED_CONTENT_AFTER_EARLY_STARTUP_SCREEN 0 -#endif - #if WITH_ENGINE CSV_DECLARE_CATEGORY_MODULE_EXTERN(CORE_API, Basic); #endif @@ -988,7 +986,7 @@ bool IsServerDelegateForOSS(FName WorldContextHandle) #if WITH_ENGINE && CSV_PROFILER static void UpdateCoreCsvStats_BeginFrame() { -#if PLATFORM_WINDOWS +#if PLATFORM_WINDOWS && !UE_BUILD_SHIPPING if (FCsvProfiler::Get()->IsCapturing()) { const uint32 ProcessId = (uint32)GetCurrentProcessId(); @@ -1595,16 +1593,6 @@ int32 FEngineLoop::PreInit(const TCHAR* CmdLine) } } - const bool bShouldHandleContentInstalledDuringEarlyLoadScreen = AUTOMATICALLY_HANDLE_INSTALLED_CONTENT_AFTER_EARLY_STARTUP_SCREEN; - if (bShouldHandleContentInstalledDuringEarlyLoadScreen) - { - UE_LOG(LogInit, Verbose, TEXT("Reapplying ini settings after early loading screen.")); - - extern CORE_API void RecordApplyCVarSettingsFromIni(); - SCOPED_BOOT_TIMING("RecordApplyCVarSettingsFromIni"); - RecordApplyCVarSettingsFromIni(); - } - #if WITH_ENGINE extern ENGINE_API void InitializeRenderingCVarsCaching(); InitializeRenderingCVarsCaching(); @@ -2228,24 +2216,6 @@ int32 FEngineLoop::PreInit(const TCHAR* CmdLine) } } - //If we are NOT expecting to automatically handle content after our EarlyStartupScreen, we can go ahead and load our game shaders now. - //If present they will be loaded, and if not present this will gracefully fail and we can handle loading them in our EarlyStartupScreen. - if (!bShouldHandleContentInstalledDuringEarlyLoadScreen) - { - LLM_SCOPE(ELLMTag::Shaders); - SCOPED_BOOT_TIMING("FShaderCodeLibrary::OpenLibrary"); - - // Open the game library which contains the material shaders. - FShaderCodeLibrary::OpenLibrary(FApp::GetProjectName(), FPaths::ProjectContentDir()); - for (const FString& RootDir : FPlatformMisc::GetAdditionalRootDirectories()) - { - FShaderCodeLibrary::OpenLibrary(FApp::GetProjectName(), FPaths::Combine(RootDir, FApp::GetProjectName(), TEXT("Content"))); - } - - // Now our shader code main library is opened, kick off the precompile. - FShaderPipelineCache::OpenPipelineFileCache(GMaxRHIShaderPlatform); - } - if (GetMoviePlayer()->HasEarlyStartupMovie()) { SCOPED_BOOT_TIMING("EarlyStartupMovie"); @@ -2352,7 +2322,9 @@ int32 FEngineLoop::PreInit(const TCHAR* CmdLine) #endif //Now that our EarlyStartupScreen is finished, lets take the necessary steps to mount paks, apply .ini cvars, and open the shader libraries if we installed content we expect to handle - if (bShouldHandleContentInstalledDuringEarlyLoadScreen) + //If using a bundle manager, assume its handling all this stuff and that we don't have to do it. + IPlatformInstallBundleManager* BundleManager = FPlatformMisc::GetPlatformInstallBundleManager(); + if (BundleManager == nullptr || BundleManager->IsNullInterface()) { // Mount Paks that were installed during EarlyStartupScreen if (FCoreDelegates::OnMountAllPakFiles.IsBound() ) @@ -2367,17 +2339,6 @@ int32 FEngineLoop::PreInit(const TCHAR* CmdLine) FCoreDelegates::OnMountAllPakFiles.Execute(PakFolders); } - //Reapply CVars after our EarlyLoadScreen - { - SCOPED_BOOT_TIMING("ReapplyCVarsFromIniAfterEarlyStartupScreen"); - - extern CORE_API void ReapplyRecordedCVarSettingsFromIni(); - extern CORE_API void DeleteRecordedCVarSettingsFromIni(); - - ReapplyRecordedCVarSettingsFromIni(); - DeleteRecordedCVarSettingsFromIni(); - } - //Handle opening shader library after our EarlyLoadScreen { LLM_SCOPE(ELLMTag::Shaders); diff --git a/Engine/Source/Runtime/Mac/CoreAudio/Private/CoreAudioBuffer.cpp b/Engine/Source/Runtime/Mac/CoreAudio/Private/CoreAudioBuffer.cpp index cdf4ee921b38..1cb61a90d69f 100644 --- a/Engine/Source/Runtime/Mac/CoreAudio/Private/CoreAudioBuffer.cpp +++ b/Engine/Source/Runtime/Mac/CoreAudio/Private/CoreAudioBuffer.cpp @@ -18,6 +18,14 @@ FCoreAudioSoundBuffer. ------------------------------------------------------------------------------------*/ +static int32 UseResourceTrackingCVar = 0; +FAutoConsoleVariableRef CVarUseResourceTracking( + TEXT("au.mac.UseResourceTracking"), + UseResourceTrackingCVar, + TEXT("When set to 1, caches compressed audio on initialied buffers to avoid buffer copies..\n") + TEXT("0: disabled, 1: enabled"), + ECVF_Default); + /** * Constructor * @@ -269,9 +277,11 @@ FCoreAudioSoundBuffer* FCoreAudioSoundBuffer::CreateNativeBuffer( FCoreAudioDevi FAudioDeviceManager* AudioDeviceManager = GEngine->GetAudioDeviceManager(); check(AudioDeviceManager != nullptr); - AudioDeviceManager->TrackResource( Wave, Buffer ); - - Wave->RemoveAudioResource(); + if (UseResourceTrackingCVar) + { + AudioDeviceManager->TrackResource(Wave, Buffer); + Wave->RemoveAudioResource(); + } return( Buffer ); } diff --git a/Engine/Source/Runtime/MoviePlayer/Private/MoviePlayerThreading.cpp b/Engine/Source/Runtime/MoviePlayer/Private/MoviePlayerThreading.cpp index 5cdd84006cea..5434064f20a7 100644 --- a/Engine/Source/Runtime/MoviePlayer/Private/MoviePlayerThreading.cpp +++ b/Engine/Source/Runtime/MoviePlayer/Private/MoviePlayerThreading.cpp @@ -75,7 +75,7 @@ void FSlateLoadingSynchronizationMechanism::DestroySlateThread() { FPlatformApplicationMisc::PumpMessages(false); - FPlatformProcess::Sleep(0.016f); + FPlatformProcess::Sleep(0.f); } delete SlateLoadingThread; @@ -151,7 +151,7 @@ void FSlateLoadingSynchronizationMechanism::SlateThreadRunMainLoop() while (IsSlateDrawPassEnqueued()) { - FPlatformProcess::Sleep(0.1f); + FPlatformProcess::Sleep(1.f / 60.f); } bMainLoopRunning = false; diff --git a/Engine/Source/Runtime/MovieSceneCapture/Private/MovieSceneCapture.cpp b/Engine/Source/Runtime/MovieSceneCapture/Private/MovieSceneCapture.cpp index 18294270dcc0..bed347cf75f6 100644 --- a/Engine/Source/Runtime/MovieSceneCapture/Private/MovieSceneCapture.cpp +++ b/Engine/Source/Runtime/MovieSceneCapture/Private/MovieSceneCapture.cpp @@ -95,6 +95,8 @@ FMovieSceneCaptureSettings::FMovieSceneCaptureSettings() bAllowTurning = false; bShowPlayer = false; bShowHUD = false; + bUsePathTracer = false; + PathTracerSamplePerPixel = 16; #if PLATFORM_MAC MovieExtension = TEXT(".mov"); @@ -292,6 +294,17 @@ void UMovieSceneCapture::Initialize(TSharedPtr InSceneViewport, Settings.bCinematicMode = bOverrideCinematicMode; } + bool bOverridePathTracer; + if (FParse::Bool(FCommandLine::Get(), TEXT("-PathTracer="), bOverridePathTracer)) + { + Settings.bUsePathTracer = bOverridePathTracer; + } + + uint16 OverridePathTracerSamplePerPixel; + if (FParse::Value(FCommandLine::Get(), TEXT("-PathTracerSamplePerPixel="), OverridePathTracerSamplePerPixel)) + { + Settings.PathTracerSamplePerPixel = OverridePathTracerSamplePerPixel; + } bool bProtocolOverride = false; @@ -372,6 +385,11 @@ void UMovieSceneCapture::Initialize(TSharedPtr InSceneViewport, } } + if (!IsRayTracingEnabled()) + { + Settings.bUsePathTracer = false; + } + bFinalizeWhenReady = false; bIsAudioCapturePass = false; @@ -514,8 +532,9 @@ void UMovieSceneCapture::CaptureThisFrame(float DeltaSeconds) } UE_LOG(LogMovieSceneCapture, Verbose, TEXT("Captured frame: %d"), CachedMetrics.Frame); ++CachedMetrics.Frame; - } + } } + void UMovieSceneCapture::Tick(float DeltaSeconds) { if (ImageCaptureProtocol) diff --git a/Engine/Source/Runtime/MovieSceneCapture/Public/MovieSceneCaptureSettings.h b/Engine/Source/Runtime/MovieSceneCapture/Public/MovieSceneCaptureSettings.h index 8e518213d6d7..605a5fd940ac 100644 --- a/Engine/Source/Runtime/MovieSceneCapture/Public/MovieSceneCaptureSettings.h +++ b/Engine/Source/Runtime/MovieSceneCapture/Public/MovieSceneCaptureSettings.h @@ -110,4 +110,12 @@ struct MOVIESCENECAPTURE_API FMovieSceneCaptureSettings /** Whether to show the in-game HUD whilst capturing */ UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category=Cinematic, AdvancedDisplay, meta=(EditCondition="bCinematicMode")) bool bShowHUD; + + /** Whether to use the path tracer (if supported) to render the scene */ + UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category = Cinematic, AdvancedDisplay) + bool bUsePathTracer; + + /** Number of sampler per pixel to be used when rendering the scene with the path tracer (if supported) */ + UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category = Cinematic, AdvancedDisplay, meta = (EditCondition = "bUsePathTracer", ClampMin = 1, UIMin = 1, UIMax = 4096)) + int32 PathTracerSamplePerPixel; }; diff --git a/Engine/Source/Runtime/MovieSceneTracks/Private/Channels/MovieSceneEvent.cpp b/Engine/Source/Runtime/MovieSceneTracks/Private/Channels/MovieSceneEvent.cpp index a0f084509ac9..cc863b30072d 100644 --- a/Engine/Source/Runtime/MovieSceneTracks/Private/Channels/MovieSceneEvent.cpp +++ b/Engine/Source/Runtime/MovieSceneTracks/Private/Channels/MovieSceneEvent.cpp @@ -11,6 +11,16 @@ UK2Node_FunctionEntry* FMovieSceneEvent::GetFunctionEntry() const { + if (SoftBlueprintPath.IsNull()) + { + // The function entry used to be serialized but is now only stored transiently. We use this pointer for the current lifecycle until the asset is saved, when we do the data upgrade. + UK2Node_FunctionEntry* FunctionEntryPtr = CastChecked(FunctionEntry_DEPRECATED.Get(), ECastCheckedType::NullAllowed); + if (FunctionEntryPtr) + { + return FunctionEntryPtr; + } + } + UK2Node_FunctionEntry* Cached = CastChecked(CachedFunctionEntry.Get(), ECastCheckedType::NullAllowed); if (Cached) { @@ -105,9 +115,22 @@ void FMovieSceneEvent::PostSerialize(const FArchive& Ar) { #if WITH_EDITORONLY_DATA if (Ar.IsLoading() && !Ar.HasAnyPortFlags(PPF_Duplicate | PPF_DuplicateForPIE)) + { + // Re-cache the function name when loading in-editor in case of renamed function graphs and the like + CacheFunctionName(); + } +#endif +} + +bool FMovieSceneEvent::Serialize(FArchive& Ar) +{ +#if WITH_EDITORONLY_DATA + if (Ar.IsSaving()) { // --------------------------------------------------------------------------------------- // Data upgrade for content that was saved with FunctionEntry_DEPRECATED instead of SoftFunctionGraph + // We do this on save because there is no reliable way to ensure that FunctionGraph is fully loaded here + // since the track may live inside or outside of a blueprint. When not fully loaded, GraphGuid is not correct. if (SoftBlueprintPath.IsNull()) { // The function entry used to be serialized but is now only stored transiently. If this is set without the soft function graph being set, copy the graph reference over @@ -121,11 +144,11 @@ void FMovieSceneEvent::PostSerialize(const FArchive& Ar) GraphGuid = FunctionGraph->GraphGuid; } } - - // Re-cache the function name when loading in-editor in case of renamed function graphs and the like - CacheFunctionName(); } #endif + + // Return false to ensure that the struct receives default serialization + return false; } bool FMovieSceneEvent::IsValidFunction(UFunction* Function) diff --git a/Engine/Source/Runtime/MovieSceneTracks/Public/Channels/MovieSceneEvent.h b/Engine/Source/Runtime/MovieSceneTracks/Public/Channels/MovieSceneEvent.h index e222694ea364..54a13f0359ad 100644 --- a/Engine/Source/Runtime/MovieSceneTracks/Public/Channels/MovieSceneEvent.h +++ b/Engine/Source/Runtime/MovieSceneTracks/Public/Channels/MovieSceneEvent.h @@ -38,6 +38,10 @@ struct MOVIESCENETRACKS_API FMovieSceneEvent */ void PostSerialize(const FArchive& Ar); + /** + * Called to perform custom serialization logic for this struct. + */ + bool Serialize(FArchive& Ar); /** * Check whether the specified function is valid for a movie scene event @@ -122,6 +126,6 @@ private: template<> struct TStructOpsTypeTraits : TStructOpsTypeTraitsBase2 { - enum { WithPostSerialize = true }; + enum { WithSerializer = true, WithPostSerialize = true }; }; diff --git a/Engine/Source/Runtime/NavigationSystem/Private/NavMesh/PImplRecastNavMesh.cpp b/Engine/Source/Runtime/NavigationSystem/Private/NavMesh/PImplRecastNavMesh.cpp index a99ad827d115..dd28c77fd877 100644 --- a/Engine/Source/Runtime/NavigationSystem/Private/NavMesh/PImplRecastNavMesh.cpp +++ b/Engine/Source/Runtime/NavigationSystem/Private/NavMesh/PImplRecastNavMesh.cpp @@ -2171,7 +2171,9 @@ void FPImplRecastNavMesh::GetDebugGeometry(FRecastDebugGeometry& OutGeometry, in const FRecastNavMeshGenerator* Generator = static_cast(NavMeshOwner->GetGenerator()); - if (Generator && Generator->IsBuildingRestrictedToActiveTiles()) + if (Generator && Generator->IsBuildingRestrictedToActiveTiles() + // if not active tiles try drawing all tiles + && NavMeshOwner->GetActiveTiles().Num() > 0) { const TArray& ActiveTiles = NavMeshOwner->GetActiveTiles(); for (const FIntPoint& TileLocation : ActiveTiles) diff --git a/Engine/Source/Runtime/NavigationSystem/Private/NavMesh/RecastNavMeshGenerator.cpp b/Engine/Source/Runtime/NavigationSystem/Private/NavMesh/RecastNavMeshGenerator.cpp index 21067b85111d..c67926c434b7 100644 --- a/Engine/Source/Runtime/NavigationSystem/Private/NavMesh/RecastNavMeshGenerator.cpp +++ b/Engine/Source/Runtime/NavigationSystem/Private/NavMesh/RecastNavMeshGenerator.cpp @@ -59,28 +59,6 @@ FORCEINLINE bool DoesBoxContainBox(const FBox& BigBox, const FBox& SmallBox) return DoesBoxContainOrOverlapVector(BigBox, SmallBox.Min) && DoesBoxContainOrOverlapVector(BigBox, SmallBox.Max); } -struct FRcTileBox -{ - int32 XMin, XMax, YMin, YMax; - - FRcTileBox(const FBox& UnrealBounds, const FVector& RcNavMeshOrigin, const float TileSizeInWorldUnits) - { - check(TileSizeInWorldUnits > 0); - - const FBox RcAreaBounds = Unreal2RecastBox(UnrealBounds); - XMin = FMath::FloorToInt((RcAreaBounds.Min.X - RcNavMeshOrigin.X) / TileSizeInWorldUnits); - XMax = FMath::FloorToInt((RcAreaBounds.Max.X - RcNavMeshOrigin.X) / TileSizeInWorldUnits); - YMin = FMath::FloorToInt((RcAreaBounds.Min.Z - RcNavMeshOrigin.Z) / TileSizeInWorldUnits); - YMax = FMath::FloorToInt((RcAreaBounds.Max.Z - RcNavMeshOrigin.Z) / TileSizeInWorldUnits); - } - - FORCEINLINE bool Contains(const FIntPoint& Point) const - { - return Point.X >= XMin && Point.X <= XMax - && Point.Y >= YMin && Point.Y <= YMax; - } -}; - int32 GetTilesCountHelper(const dtNavMesh* DetourMesh) { int32 NumTiles = 0; @@ -3243,6 +3221,10 @@ FRecastNavMeshGenerator::FRecastNavMeshGenerator(ARecastNavMesh& InDestNavMesh) , bRestrictBuildingToActiveTiles(false) , Version(0) { +#if TIME_SLICE_NAV_REGEN + TimeSliceDuration = 0.0025; +#endif + INC_DWORD_STAT_BY(STAT_NavigationMemory, sizeof(*this)); } @@ -3920,7 +3902,11 @@ TArray FRecastNavMeshGenerator::AddGeneratedTiles(FRecastTileGenerator& dtNavMesh* DetourMesh = DestNavMesh->GetRecastNavMeshImpl()->GetRecastMesh(); const int32 FirstDirtyTileIndex = TileGenerator.GetDirtyLayersMask().Find(true); - if (DetourMesh != nullptr && IsInActiveSet(FIntPoint(TileX, TileY)) + if (DetourMesh != nullptr + // no longer testing this here, we can live with a stray unwanted tile here + // and there. It will be removed the next time around the invokers get + // updated + // && IsInActiveSet(FIntPoint(TileX, TileY)) && FirstDirtyTileIndex != INDEX_NONE) { TArray TileLayers = TileGenerator.GetNavigationData(); @@ -4075,7 +4061,7 @@ static bool IntersectBounds(const FBox& TestBox, const TNavStatArray& Boun namespace { - FBox CalculateBoxIntercetion(const FBox& BoxA, const FBox& BoxB) + FBox CalculateBoxIntersection(const FBox& BoxA, const FBox& BoxB) { // assumes boxes overlap ensure(BoxA.Intersect(BoxB)); @@ -4165,7 +4151,7 @@ void FRecastNavMeshGenerator::MarkDirtyTiles(const TArray& continue; } - const FBox CutDownArea = CalculateBoxIntercetion(GetTotalBounds(), DirtyArea.Bounds); + const FBox CutDownArea = CalculateBoxIntersection(GetTotalBounds(), DirtyArea.Bounds); AdjustedAreaBounds = GrowBoundingBox(CutDownArea, DirtyArea.HasFlag(ENavigationDirtyFlag::UseAgentHeight)); // @TODO this and the following test share some work in common @@ -4177,7 +4163,7 @@ void FRecastNavMeshGenerator::MarkDirtyTiles(const TArray& // check if any of inclusion volumes encapsulates this box // using CutDownArea not AdjustedAreaBounds since if the area is on the border of navigable space // then FindInclusionBoundEncapsulatingBox can produce false negative - bDoTileInclusionTest = FindInclusionBoundEncapsulatingBox(CutDownArea) == INDEX_NONE; + bDoTileInclusionTest = (FindInclusionBoundEncapsulatingBox(CutDownArea) == INDEX_NONE); } const FRcTileBox TileBox(AdjustedAreaBounds, RcNavMeshOrigin, TileSizeInWorldUnits); @@ -4191,7 +4177,7 @@ void FRecastNavMeshGenerator::MarkDirtyTiles(const TArray& continue; } - if (DirtyArea.HasFlag(ENavigationDirtyFlag::NavigationBounds) == false && bDoTileInclusionTest == true) + if (bDoTileInclusionTest == true && DirtyArea.HasFlag(ENavigationDirtyFlag::NavigationBounds) == false) { const FBox TileBounds = CalculateTileBounds(TileX, TileY, RcNavMeshOrigin, TotalNavBounds, TileSizeInWorldUnits); @@ -4375,16 +4361,16 @@ TArray FRecastNavMeshGenerator::ProcessTileTasksAsync(const int32 NumTas } // Remove submitted element from pending list - PendingDirtyTiles.RemoveAt(ElementIdx); - - // Release memory, list could be quite big after map load - if (PendingDirtyTiles.Num() == 0) - { - PendingDirtyTiles.Empty(32); - } + PendingDirtyTiles.RemoveAt(ElementIdx, 1, /*bAllowShrinking=*/false); NumProcessedTasks++; } } + + // Release memory, list could be quite big after map load + if (NumProcessedTasks > 0 && PendingDirtyTiles.Num() == 0) + { + PendingDirtyTiles.Empty(32); + } // Collect completed tasks and apply generated data to navmesh for (int32 Idx = RunningDirtyTiles.Num() - 1; Idx >=0; --Idx) @@ -4432,10 +4418,9 @@ TArray FRecastNavMeshGenerator::ProcessTileTasksAsync(const int32 NumTas #endif #if TIME_SLICE_NAV_REGEN -bool IsTimeSliceDurationExceeded(const double StartTime) +bool FRecastNavMeshGenerator::IsTimeSliceDurationExceeded(const double StartTime) const { - const float TimeSliceDuration = 0.0025f; - const float CurTime = FPlatformTime::Seconds(); + const double CurTime = FPlatformTime::Seconds(); return CurTime - StartTime >= TimeSliceDuration; } @@ -4451,7 +4436,6 @@ TArray FRecastNavMeshGenerator::ProcessTileTasksSync(const int32 NumTask #if TIME_SLICE_NAV_REGEN const double StartTime = FPlatformTime::Seconds(); - double CurTime = StartTime; //if we are time slice processing a tile (ie we have already done some processing on this tile last frame) bool bIsTimeSliceProcessingTile = TileGeneratorSync.Get() != nullptr; diff --git a/Engine/Source/Runtime/NavigationSystem/Private/NavigationInvokerComponent.cpp b/Engine/Source/Runtime/NavigationSystem/Private/NavigationInvokerComponent.cpp index 1edef03fc2d9..b4c2ad494f40 100644 --- a/Engine/Source/Runtime/NavigationSystem/Private/NavigationInvokerComponent.cpp +++ b/Engine/Source/Runtime/NavigationSystem/Private/NavigationInvokerComponent.cpp @@ -44,3 +44,9 @@ void UNavigationInvokerComponent::RegisterWithNavigationSystem(UNavigationSystem } } } + +void UNavigationInvokerComponent::SetGenerationRadii(const float GenerationRadius, const float RemovalRadius) +{ + TileGenerationRadius = GenerationRadius; + TileRemovalRadius = RemovalRadius; +} diff --git a/Engine/Source/Runtime/NavigationSystem/Private/NavigationSystem.cpp b/Engine/Source/Runtime/NavigationSystem/Private/NavigationSystem.cpp index 9dfd2649a17b..ee9c66079977 100644 --- a/Engine/Source/Runtime/NavigationSystem/Private/NavigationSystem.cpp +++ b/Engine/Source/Runtime/NavigationSystem/Private/NavigationSystem.cpp @@ -981,47 +981,50 @@ void UNavigationSystemV1::Tick(float DeltaSeconds) } INC_FLOAT_STAT_BY(STAT_Navigation_CumulativeBuildTime,(float)ThisTime*1000); } - - if (bGenerateNavigationOnlyAroundNavigationInvokers) + + DirtyAreasUpdateTime += DeltaSeconds; + + if (IsNavigationBuildingLocked() == false) { - UpdateInvokers(); - } - - { - SCOPE_CYCLE_COUNTER(STAT_Navigation_TickMarkDirty); - - DirtyAreasUpdateTime += DeltaSeconds; - const float DirtyAreasUpdateDeltaTime = 1.0f / DirtyAreasUpdateFreq; - const bool bCanRebuildNow = (DirtyAreasUpdateTime >= DirtyAreasUpdateDeltaTime) || !bIsGame; - const bool bIsLocked = IsNavigationBuildingLocked(); - - if (DirtyAreas.Num() > 0 && bCanRebuildNow && !bIsLocked) + if (bGenerateNavigationOnlyAroundNavigationInvokers) { - for (int32 NavDataIndex = 0; NavDataIndex < NavDataSet.Num(); ++NavDataIndex) + UpdateInvokers(); + } + + { + SCOPE_CYCLE_COUNTER(STAT_Navigation_TickMarkDirty); + + const float DirtyAreasUpdateDeltaTime = 1.0f / DirtyAreasUpdateFreq; + const bool bCanRebuildNow = (DirtyAreasUpdateTime >= DirtyAreasUpdateDeltaTime) || !bIsGame; + + if (DirtyAreas.Num() > 0 && bCanRebuildNow) + { + for (int32 NavDataIndex = 0; NavDataIndex < NavDataSet.Num(); ++NavDataIndex) + { + ANavigationData* NavData = NavDataSet[NavDataIndex]; + if (NavData) + { + NavData->RebuildDirtyAreas(DirtyAreas); + } + } + + DirtyAreasUpdateTime = 0; + DirtyAreas.Reset(); + } + } + + // Tick navigation mesh async builders + if (bAsyncBuildPaused == false) + { + SCOPE_CYCLE_COUNTER(STAT_Navigation_TickAsyncBuild); + + for (ANavigationData* NavData : NavDataSet) { - ANavigationData* NavData = NavDataSet[NavDataIndex]; if (NavData) { - NavData->RebuildDirtyAreas(DirtyAreas); + NavData->TickAsyncBuild(DeltaSeconds); } } - - DirtyAreasUpdateTime = 0; - DirtyAreas.Reset(); - } - } - - // Tick navigation mesh async builders - if (!bAsyncBuildPaused) - { - SCOPE_CYCLE_COUNTER(STAT_Navigation_TickAsyncBuild); - - for (ANavigationData* NavData : NavDataSet) - { - if (NavData) - { - NavData->TickAsyncBuild(DeltaSeconds); - } } } @@ -3300,6 +3303,13 @@ void UNavigationSystemV1::Build() // make sure freshly created navigation instances are registered before we try to build them ProcessRegistrationCandidates(); + + // update invokers in case we're not updating navmesh automatically, in which case + // navigation generators wouldn't have up-to-date info. + if (bGenerateNavigationOnlyAroundNavigationInvokers) + { + UpdateInvokers(); + } // and now iterate through all registered and just start building them RebuildAll(); @@ -4211,7 +4221,7 @@ void UNavigationSystemV1::UpdateInvokers() const float CurrentTime = World->GetTimeSeconds(); if (CurrentTime >= NextInvokersUpdateTime) { - TArray InvokerLocations; + InvokerLocations.Reset(); if (Invokers.Num() > 0) { diff --git a/Engine/Source/Runtime/NavigationSystem/Public/NavMesh/RecastNavMesh.h b/Engine/Source/Runtime/NavigationSystem/Public/NavMesh/RecastNavMesh.h index c2b65fc7b6ad..a9a202086c11 100644 --- a/Engine/Source/Runtime/NavigationSystem/Public/NavMesh/RecastNavMesh.h +++ b/Engine/Source/Runtime/NavigationSystem/Public/NavMesh/RecastNavMesh.h @@ -888,8 +888,8 @@ public: bool ShouldGatherDataOnGameThread() const { return bDoFullyAsyncNavDataGathering == false; } int32 GetTileNumberHardLimit() const { return TileNumberHardLimit; } - void UpdateActiveTiles(const TArray& InvokerLocations); - void RemoveTiles(const TArray& Tiles); + virtual void UpdateActiveTiles(const TArray& InvokerLocations); + virtual void RemoveTiles(const TArray& Tiles); void RebuildTile(const TArray& Tiles); protected: diff --git a/Engine/Source/Runtime/NavigationSystem/Public/NavMesh/RecastNavMeshGenerator.h b/Engine/Source/Runtime/NavigationSystem/Public/NavMesh/RecastNavMeshGenerator.h index 04b7c5356186..f30844ab1d5c 100644 --- a/Engine/Source/Runtime/NavigationSystem/Public/NavMesh/RecastNavMeshGenerator.h +++ b/Engine/Source/Runtime/NavigationSystem/Public/NavMesh/RecastNavMeshGenerator.h @@ -11,6 +11,7 @@ #include "Async/AsyncWork.h" #include "UObject/GCObject.h" #include "AI/NavDataGenerator.h" +#include "NavMesh/RecastHelpers.h" #if WITH_RECAST @@ -150,6 +151,28 @@ struct FRecastAreaNavModifierElement TArray PerInstanceTransform; }; +struct FRcTileBox +{ + int32 XMin, XMax, YMin, YMax; + + FRcTileBox(const FBox& UnrealBounds, const FVector& RcNavMeshOrigin, const float TileSizeInWorldUnits) + { + check(TileSizeInWorldUnits > 0); + + const FBox RcAreaBounds = Unreal2RecastBox(UnrealBounds); + XMin = FMath::FloorToInt((RcAreaBounds.Min.X - RcNavMeshOrigin.X) / TileSizeInWorldUnits); + XMax = FMath::FloorToInt((RcAreaBounds.Max.X - RcNavMeshOrigin.X) / TileSizeInWorldUnits); + YMin = FMath::FloorToInt((RcAreaBounds.Min.Z - RcNavMeshOrigin.Z) / TileSizeInWorldUnits); + YMax = FMath::FloorToInt((RcAreaBounds.Max.Z - RcNavMeshOrigin.Z) / TileSizeInWorldUnits); + } + + FORCEINLINE bool Contains(const FIntPoint& Point) const + { + return Point.X >= XMin && Point.X <= XMax + && Point.Y >= YMin && Point.Y <= YMax; + } +}; + enum class ETimeSliceWorkResult : uint8 { Failed, @@ -485,7 +508,7 @@ protected: void UpdateNavigationBounds(); // Sorts pending build tiles by proximity to player, so tiles closer to player will get generated first - void SortPendingBuildTiles(); + virtual void SortPendingBuildTiles(); /** Instantiates dtNavMesh and configures it for tiles generation. Returns false if failed */ bool ConstructTiledNavMesh(); @@ -494,7 +517,7 @@ protected: void CalcNavMeshProperties(int32& MaxTiles, int32& MaxPolys); /** Marks grid tiles affected by specified areas as dirty */ - void MarkDirtyTiles(const TArray& DirtyAreas); + virtual void MarkDirtyTiles(const TArray& DirtyAreas); void RemoveLayers(const FIntPoint& Tile, TArray& UpdatedTiles); @@ -512,6 +535,10 @@ public: /** Adds generated tiles to NavMesh, replacing old ones */ TArray AddGeneratedTiles(FRecastTileGenerator& TileGenerator); +#if TIME_SLICE_NAV_REGEN + bool IsTimeSliceDurationExceeded(const double StartTime) const; +#endif + public: /** Removes all tiles at specified grid location */ TArray RemoveTileLayers(const int32 TileX, const int32 TileY, TMap* OldLayerTileIdMap = nullptr); @@ -530,7 +557,7 @@ public: protected: bool IsInActiveSet(const FIntPoint& Tile) const; - void RestrictBuildingToActiveTiles(bool InRestrictBuildingToActiveTiles); + virtual void RestrictBuildingToActiveTiles(bool InRestrictBuildingToActiveTiles); /** Blocks until build for specified list of tiles is complete and discard results */ void DiscardCurrentBuildingTasks(); @@ -599,6 +626,10 @@ protected: /** Runtime generator's version, increased every time all tile generators get invalidated * like when navmesh size changes */ uint32 Version; + +#if TIME_SLICE_NAV_REGEN + double TimeSliceDuration; +#endif }; #endif // WITH_RECAST diff --git a/Engine/Source/Runtime/NavigationSystem/Public/NavigationInvokerComponent.h b/Engine/Source/Runtime/NavigationSystem/Public/NavigationInvokerComponent.h index fa064ef969b7..9fcb4cb294ac 100644 --- a/Engine/Source/Runtime/NavigationSystem/Public/NavigationInvokerComponent.h +++ b/Engine/Source/Runtime/NavigationSystem/Public/NavigationInvokerComponent.h @@ -27,7 +27,13 @@ public: void RegisterWithNavigationSystem(UNavigationSystemV1& NavSys); -protected: + /** Sets generation/removal ranges. Doesn't force navigation system's update. + * Will get picked up the next time NavigationSystem::UpdateInvokers gets called */ + void SetGenerationRadii(const float GenerationRadius, const float RemovalRadius); + + float GetGenerationRadius() const { return TileGenerationRadius; } + float GetRemovalRadius() const { return TileRemovalRadius; } + virtual void Activate(bool bReset = false) override; virtual void Deactivate() override; }; diff --git a/Engine/Source/Runtime/NavigationSystem/Public/NavigationSystem.h b/Engine/Source/Runtime/NavigationSystem/Public/NavigationSystem.h index d0720e2271e0..09d9269d1bf6 100644 --- a/Engine/Source/Runtime/NavigationSystem/Public/NavigationSystem.h +++ b/Engine/Source/Runtime/NavigationSystem/Public/NavigationSystem.h @@ -217,6 +217,8 @@ protected: private: TMap Invokers; + /** Contains pre-digested and cached invokers' info. Generated by UpdateInvokers */ + TArray InvokerLocations; float NextInvokersUpdateTime; void UpdateInvokers(); @@ -502,12 +504,14 @@ public: //----------------------------------------------------------------------// // Active tiles //----------------------------------------------------------------------// - void RegisterInvoker(AActor& Invoker, float TileGenerationRadius, float TileRemovalRadius); - void UnregisterInvoker(AActor& Invoker); + virtual void RegisterInvoker(AActor& Invoker, float TileGenerationRadius, float TileRemovalRadius); + virtual void UnregisterInvoker(AActor& Invoker); static void RegisterNavigationInvoker(AActor& Invoker, float TileGenerationRadius, float TileRemovalRadius); static void UnregisterNavigationInvoker(AActor& Invoker); + const TArray& GetInvokerLocations() const { return InvokerLocations; } + //----------------------------------------------------------------------// // Bookkeeping //----------------------------------------------------------------------// @@ -578,7 +582,7 @@ public: /** updates bounds of all components implementing INavRelevantInterface */ static void UpdateNavOctreeBounds(AActor* Actor); - void AddDirtyArea(const FBox& NewArea, int32 Flags); + virtual void AddDirtyArea(const FBox& NewArea, int32 Flags); void AddDirtyAreas(const TArray& NewAreas, int32 Flags); bool HasDirtyAreasQueued() const; diff --git a/Engine/Source/Runtime/NetworkFileSystem/Private/NetworkFileServerHttp.cpp b/Engine/Source/Runtime/NetworkFileSystem/Private/NetworkFileServerHttp.cpp index 19cfec570460..b2a9d3b89a93 100644 --- a/Engine/Source/Runtime/NetworkFileSystem/Private/NetworkFileServerHttp.cpp +++ b/Engine/Source/Runtime/NetworkFileSystem/Private/NetworkFileServerHttp.cpp @@ -347,6 +347,7 @@ int FNetworkFileServerHttp::CallBack_HTTP( Buffer, TEXT("HTTP/1.0 200 OK\x0d\x0a") TEXT("Server: Unreal File Server\x0d\x0a") + TEXT("Access-Control-Allow-Origin: *\x0d\x0a") TEXT("Connection: close\x0d\x0a") TEXT("Content-Type: text/html; charset=utf-8\x0d\x0a") TEXT("Content-Length: %u\x0d\x0a\x0d\x0a%s"), @@ -421,6 +422,7 @@ int FNetworkFileServerHttp::CallBack_HTTP( Length = FCString::Sprintf(Header, TEXT("HTTP/1.1 200 OK\x0d\x0a") TEXT("Server: Unreal File Server\x0d\x0a") + TEXT("Access-Control-Allow-Origin: *\x0d\x0a") TEXT("Connection: close\x0d\x0a") TEXT("Content-Type: %s \x0d\x0a") TEXT("Content-Encoding: gzip\x0d\x0a") @@ -432,6 +434,7 @@ int FNetworkFileServerHttp::CallBack_HTTP( Length = FCString::Sprintf(Header, TEXT("HTTP/1.1 200 OK\x0d\x0a") TEXT("Server: Unreal File Server\x0d\x0a") + TEXT("Access-Control-Allow-Origin: *\x0d\x0a") TEXT("Connection: close\x0d\x0a") TEXT("Content-Type: %s \x0d\x0a") TEXT("Content-Length: %u\x0d\x0a\x0d\x0a"), @@ -477,6 +480,7 @@ int FNetworkFileServerHttp::CallBack_HTTP( (ANSICHAR*)Header, "HTTP/1.1 200 OK\x0d\x0a" "Server: Unreal File Server\x0d\x0a" + "Access-Control-Allow-Origin: *\x0d\x0a" "Connection: close\x0d\x0a" "Content-Type: application/octet-stream \x0d\x0a" "Content-Length: %u\x0d\x0a\x0d\x0a", diff --git a/Engine/Source/Runtime/NullInstallBundleManager/Source/NullInstallBundleManager.cpp b/Engine/Source/Runtime/NullInstallBundleManager/Source/NullInstallBundleManager.cpp index 886214d09a9a..bf2f3d0cee0b 100644 --- a/Engine/Source/Runtime/NullInstallBundleManager/Source/NullInstallBundleManager.cpp +++ b/Engine/Source/Runtime/NullInstallBundleManager/Source/NullInstallBundleManager.cpp @@ -64,7 +64,12 @@ class FNullInstallBundleManager : public IPlatformInstallBundleManager } - virtual void CancelBundle(FName BundleName) override + virtual void CancelBundle(FName BundleName, EInstallBundleCancelFlags Flags) override + { + + } + + virtual void CancelAllBundles(EInstallBundleCancelFlags Flags) override { } @@ -74,6 +79,11 @@ class FNullInstallBundleManager : public IPlatformInstallBundleManager return TOptional(); } + virtual bool IsNullInterface() const override + { + return true; + } + private: }; diff --git a/Engine/Source/Runtime/Online/BackgroundHTTP/Private/BackgroundHttpManagerImpl.cpp b/Engine/Source/Runtime/Online/BackgroundHTTP/Private/BackgroundHttpManagerImpl.cpp index 39e2a4005688..09aa568aab5f 100644 --- a/Engine/Source/Runtime/Online/BackgroundHTTP/Private/BackgroundHttpManagerImpl.cpp +++ b/Engine/Source/Runtime/Online/BackgroundHTTP/Private/BackgroundHttpManagerImpl.cpp @@ -1,10 +1,12 @@ // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "BackgroundHttpManagerImpl.h" +#include "HAL/FileManager.h" #include "HAL/PlatformFilemanager.h" #include "HAL/PlatformAtomics.h" #include "HAL/PlatformFile.h" +#include "Misc/ConfigCacheIni.h" #include "Misc/ScopeRWLock.h" DEFINE_LOG_CATEGORY(LogBackgroundHttpManager); @@ -24,6 +26,7 @@ FBackgroundHttpManagerImpl::~FBackgroundHttpManagerImpl() void FBackgroundHttpManagerImpl::Initialize() { + ClearAnyTempFilesFromTimeOut(); } void FBackgroundHttpManagerImpl::Shutdown() @@ -42,6 +45,38 @@ void FBackgroundHttpManagerImpl::Shutdown() } } +void FBackgroundHttpManagerImpl::ClearAnyTempFilesFromTimeOut() +{ + UE_LOG(LogBackgroundHttpManager, Log, TEXT("Checking for BackgroundHTTP temp files that should be deleted due to time out")); + + TArray FilesToCheck; + + //Find all files in our temp folder + IFileManager::Get().FindFiles(FilesToCheck, *FPlatformBackgroundHttp::GetTemporaryRootPath(), nullptr); + + double FileAgeTimeOutSettings = -1; + GConfig->GetDouble(TEXT("BackgroundHttp"), TEXT("BackgroundHttp.TempFileTimeOutSeconds"), FileAgeTimeOutSettings, GEngineIni); + + if (FileAgeTimeOutSettings >= 0) + { + for (const FString& File : FilesToCheck) + { + const double FileAge = IFileManager::Get().GetFileAgeSeconds(*File); + const bool bShouldDelete = (FileAge > FileAgeTimeOutSettings); + + UE_LOG(LogBackgroundHttpManager, Log, TEXT("FoundTempFile: %s with age %lld -- bShouldDelete:%d"), *File, FileAge, (int)bShouldDelete); + + if (bShouldDelete) + { + if (!IFileManager::Get().Delete(*File)) + { + UE_LOG(LogBackgroundHttpManager, Error, TEXT("File %s failed to delete, but should have as as it is %lld seconds old!"), *File); + } + } + } + } +} + void FBackgroundHttpManagerImpl::CleanUpTemporaryFiles() { UE_LOG(LogBackgroundHttpManager, Log, TEXT("Cleaning Up Temporary Files")); diff --git a/Engine/Source/Runtime/Online/BackgroundHTTP/Private/IOS/ApplePlatformBackgroundHttp.cpp b/Engine/Source/Runtime/Online/BackgroundHTTP/Private/IOS/ApplePlatformBackgroundHttp.cpp index e550277c4e06..e041aa05b79f 100644 --- a/Engine/Source/Runtime/Online/BackgroundHTTP/Private/IOS/ApplePlatformBackgroundHttp.cpp +++ b/Engine/Source/Runtime/Online/BackgroundHTTP/Private/IOS/ApplePlatformBackgroundHttp.cpp @@ -41,8 +41,7 @@ const FString FApplePlatformBackgroundHttp::GetTemporaryFilePathFromURL(const FS const FString& FApplePlatformBackgroundHttp::GetTemporaryRootPath() { - static FString BackgroundHttpDir = FPaths::Combine(FPlatformMisc::GamePersistentDownloadDir(), TEXT("BackgroundHttpTemp")); - return BackgroundHttpDir; + return FBackgroundURLSessionHandler::GetBackgroundSessionWorkingDirectoryPath(); } int FApplePlatformBackgroundHttp::GetPlatformMaxActiveDownloads() diff --git a/Engine/Source/Runtime/Online/BackgroundHTTP/Private/IOS/ApplePlatformBackgroundHttpManager.cpp b/Engine/Source/Runtime/Online/BackgroundHTTP/Private/IOS/ApplePlatformBackgroundHttpManager.cpp index 8c511321a585..bf3bfe89d390 100644 --- a/Engine/Source/Runtime/Online/BackgroundHTTP/Private/IOS/ApplePlatformBackgroundHttpManager.cpp +++ b/Engine/Source/Runtime/Online/BackgroundHTTP/Private/IOS/ApplePlatformBackgroundHttpManager.cpp @@ -43,6 +43,8 @@ void FApplePlatformBackgroundHttpManager::Initialize() GConfig->GetInt(TEXT("BackgroundHttp.iOSSettings"), TEXT("BackgroundHttp.RetryResumeDataLimit"), RetryResumeDataLimitSetting, GEngineIni); SetupNSURLSessionResponseDelegates(); + + FBackgroundHttpManagerImpl::Initialize(); } void FApplePlatformBackgroundHttpManager::PopulateUnAssociatedTasks() @@ -57,11 +59,7 @@ void FApplePlatformBackgroundHttpManager::PopulateUnAssociatedTasks() //Store all existing tasks by their URL for (id task in tasks) { - //Only grab running tasks in this list. - if ([task state] == NSURLSessionTaskStateRunning) - { - [UnAssociatedTasks setObject:task forKey:[[[task currentRequest] URL] absoluteString]]; - } + [UnAssociatedTasks setObject:task forKey:[[[task currentRequest] URL] absoluteString]]; } bHasFinishedPopulatingUnassociatedTasks = true; @@ -134,6 +132,12 @@ void FApplePlatformBackgroundHttpManager::AddRequest(const FBackgroundHttpReques FRWScopeLock ScopeLock(ActiveRequestLock, SLT_Write); ActiveRequests.Add(Request); + + //Increment our underlying FBackgroundHttpManagerImpl tracker for active requests as we + //don't implement the method it uses to increase this number. + // NOTE: We don't make use of this number in Apple Platform functions as all requests are "Active" but their + // underlying Task might not be, see NumCurrentlyActiveTasks instead to track how many current Tasks are downloading data. + ++NumCurrentlyActiveRequests; } } @@ -166,7 +170,7 @@ void FApplePlatformBackgroundHttpManager::RemoveURLMapEntriesForRequest(FAppleBa if (FoundRequest == Request) { - UE_LOG(LogBackgroundHttpManager, Display, TEXT("Removing URL Entry -- RequestID:%s | URL:%s"), *Request->GetRequestID(), *URL); + UE_LOG(LogBackgroundHttpManager, Verbose, TEXT("Removing URL Entry -- RequestDebugID:%s | URL:%s"), *Request->GetRequestDebugID(), *URL); URLToRequestMap.Remove(URL); } } @@ -244,23 +248,27 @@ bool FApplePlatformBackgroundHttpManager::CheckForExistingUnAssociatedTask(const NSURLSessionTask* FoundTask = [UnAssociatedTasks valueForKey:URL.GetNSString()]; if (nullptr != FoundTask) { - if ([FoundTask state] != NSURLSessionTaskStateCanceling) - { - UE_LOG(LogBackgroundHttpManager, Display, TEXT("Existing UnAssociateTask found for Request! Attempting to Associate! RequestId:%s"), *(Request->GetRequestID())); + UE_LOG(LogBackgroundHttpManager, Display, TEXT("Existing UnAssociateTask found for Request! Attempting to Associate! RequestDebugID:%s"), *(Request->GetRequestDebugID())); - //Associate with task so that our Request takes over ownership of this task so we can remove it from our UnAssociated Tasks list without it getting GC'd - Request->AssociateWithTask(FoundTask); - [UnAssociatedTasks removeObjectForKey:URL.GetNSString()]; - - //Always set our bWasTaskStartedInBG flag on our Request as true in the UnAssociated case as we don't know when it was really started - FPlatformAtomics::InterlockedExchange(&(Request->bWasTaskStartedInBG), true); - - //Suspend task in case it was running so that we can adhere to our desired platform max tasks - [FoundTask suspend]; - - bDidFindExistingTask = true; - break; - } + //Associate with task so that our Request takes over ownership of this task so we can remove it from our UnAssociated Tasks list without it getting GC'd + if (Request->AssociateWithTask(FoundTask)) + { + //Always set our bWasTaskStartedInBG flag on our Request as true in the UnAssociated case as we don't know when it was really started + FPlatformAtomics::InterlockedExchange(&(Request->bWasTaskStartedInBG), true); + + //Suspend task in case it was running so that we can adhere to our desired platform max tasks + [FoundTask suspend]; + + bDidFindExistingTask = true; + break; + } + else + { + UE_LOG(LogBackgroundHttpManager, Display, TEXT("UnAssociatedTask for request found, but failed to Associate with Task! -- RequestDebugID:%s | URL:%s"), *(Request->GetRequestDebugID()), *URL); + } + + //Still want to remove UnAssociatedTask even though we didn't use it as something else can now be downloading this data and we do not want duplicates + [UnAssociatedTasks removeObjectForKey : URL.GetNSString()]; } } } @@ -314,7 +322,9 @@ void FApplePlatformBackgroundHttpManager::PauseAllActiveTasks() if ([DownloadTask state] == NSURLSessionTaskStateRunning) { FString TaskURL = [[[DownloadTask currentRequest] URL] absoluteString]; - UE_LOG(LogBackgroundHttpManager, Display, TEXT("Pausing Task for URL:%s"), *TaskURL); + int TaskIdentifier = (int)[DownloadTask taskIdentifier]; + + UE_LOG(LogBackgroundHttpManager, Display, TEXT("Pausing Task for URL:%s | TaskIdentifier:%d"), *TaskURL, TaskIdentifier); [DownloadTask suspend]; } @@ -340,7 +350,9 @@ void FApplePlatformBackgroundHttpManager::ResumeAllTasks() if ([DownloadTask state] == NSURLSessionTaskStateSuspended) { FString TaskURL = [[[DownloadTask currentRequest] URL] absoluteString]; - UE_LOG(LogBackgroundHttpManager, Display, TEXT("Resuming Task for URL:%s"), *TaskURL); + int TaskIdentifier = (int)[DownloadTask taskIdentifier]; + + UE_LOG(LogBackgroundHttpManager, Display, TEXT("Resuming Task for URL:%s | TaskIdentifier:%d"), *TaskURL, TaskIdentifier); [DownloadTask resume]; } @@ -352,13 +364,15 @@ void FApplePlatformBackgroundHttpManager::ResumeAllTasks() void FApplePlatformBackgroundHttpManager::OnTask_DidFinishDownloadingToURL(NSURLSessionDownloadTask* Task, NSError* Error, const FString& TempFilePath) { FString TaskURL = [[[Task currentRequest] URL] absoluteString]; + int TaskIdentifier = (int)[Task taskIdentifier]; + const int ErrorCode = [Error code]; const FString ErrorDescription = [Error localizedDescription]; IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); const bool bFileExists = PlatformFile.FileExists(*TempFilePath); - UE_LOG(LogBackgroundHttpManager, Display, TEXT("Recieved Background Session Callback for URL:%s | bFileExists:%d | ErrorCode:%d | ErrorDescription:%s | Location:%s"), *TaskURL, (int)(bFileExists), ErrorCode, *ErrorDescription, *TempFilePath); + UE_LOG(LogBackgroundHttpManager, Display, TEXT("Recieved Background Session Callback for URL:%s | TaskIdentifier:%d | bFileExists:%d | ErrorCode:%d | ErrorDescription:%s | Location:%s"), *TaskURL, TaskIdentifier, (int)(bFileExists), ErrorCode, *ErrorDescription, *TempFilePath); if (bFileExists) { @@ -373,13 +387,13 @@ void FApplePlatformBackgroundHttpManager::OnTask_DidFinishDownloadingToURL(NSURL FoundRequest->SetRequestAsSuccess(TempFilePath); } - UE_LOG(LogBackgroundHttpManager, Display, TEXT("Attempt To Mark Task Complete -- URL:%s | bDidFindTask:%d"), *TaskURL, (int)(FoundRequest.IsValid())); + UE_LOG(LogBackgroundHttpManager, Display, TEXT("Attempt To Mark Task Complete -- URL:%s | TaskIdentifier:%d |bDidFindTask:%d"), *TaskURL, TaskIdentifier, (int)(FoundRequest.IsValid())); } } else { //Forward to the OnCompleteWithError as we don't have our finished file! - UE_LOG(LogBackgroundHttpManager, Display, TEXT("File Not Found For DidFinishDownloadingToURL. Transitioning to DidCompleteWithError -- TaskURL:%s | ErrorCode:%d | ErrorDescription:%s | Location:%s"), *TaskURL, ErrorCode, *ErrorDescription, *TempFilePath); + UE_LOG(LogBackgroundHttpManager, Display, TEXT("File Not Found For DidFinishDownloadingToURL. Transitioning to DidCompleteWithError -- TaskURL:%s | TaskIdentifier:%d| ErrorCode:%d | ErrorDescription:%s | Location:%s"), *TaskURL, TaskIdentifier, ErrorCode, *ErrorDescription, *TempFilePath); OnTask_DidCompleteWithError(Task, Error); } } @@ -406,7 +420,7 @@ void FApplePlatformBackgroundHttpManager::FinishRequest(FAppleBackgroundHttpRequ if (bFileExists) { - UE_LOG(LogBackgroundHttpManager,Display, TEXT("Task Completed Successfully. RequestID:%s TempFileLocation:%s"), *(Request->GetRequestID()), *TempFilePath); + UE_LOG(LogBackgroundHttpManager,Display, TEXT("Task Completed Successfully. RequestDebugID:%s TempFileLocation:%s"), *(Request->GetRequestDebugID()), *TempFilePath); FBackgroundHttpResponsePtr NewResponse = FPlatformBackgroundHttp::ConstructBackgroundResponse(ResponseCode, *TempFilePath); Request->CompleteWithExistingResponseData(NewResponse); } @@ -418,7 +432,7 @@ void FApplePlatformBackgroundHttpManager::FinishRequest(FAppleBackgroundHttpRequ //successfully. Handle this unexpected failure by trying to retry the task. if (!bDidFail) { - UE_LOG(LogBackgroundHttpManager,Error, TEXT("Task finished downloading, but finished temp file was not found! -- RequestId:%s | TempFileLocation:%s"), *(Request->GetRequestID()), *TempFilePath); + UE_LOG(LogBackgroundHttpManager,Error, TEXT("Task finished downloading, but finished temp file was not found! -- RequestDebugID:%s | TempFileLocation:%s"), *(Request->GetRequestDebugID()), *TempFilePath); //Mark our download as not completed as we hit an error so that we don't just keep trying to call FinishRequest FPlatformAtomics::InterlockedExchange(&(Request->bIsCompleted), false); @@ -432,7 +446,7 @@ void FApplePlatformBackgroundHttpManager::FinishRequest(FAppleBackgroundHttpRequ //Expected case where we failed, but expected to fail else { - UE_LOG(LogBackgroundHttpManager, Display, TEXT("Task failed completely -- RequestID:%s"), *(Request->GetRequestID())); + UE_LOG(LogBackgroundHttpManager, Display, TEXT("Task failed completely -- RequestDebugID:%s"), *(Request->GetRequestDebugID())); FBackgroundHttpResponsePtr NewResponse = FPlatformBackgroundHttp::ConstructBackgroundResponse(ResponseCode, TEXT("")); Request->CompleteWithExistingResponseData(NewResponse); @@ -460,7 +474,7 @@ void FApplePlatformBackgroundHttpManager::FinishRequest(FAppleBackgroundHttpRequ } else { - UE_LOG(LogBackgroundHttpManager,Display, TEXT("Not finishing Request as its already sending a finish notification -- RequestID:%s"), *(Request->GetRequestID())); + UE_LOG(LogBackgroundHttpManager,Display, TEXT("Not finishing Request as its already sending a finish notification -- RequestDebugID:%s"), *(Request->GetRequestDebugID())); } } @@ -478,12 +492,12 @@ void FApplePlatformBackgroundHttpManager::RetryRequest(FAppleBackgroundHttpReque const bool bShouldUseRetryData = ShouldUseRequestRetryData(Request, RetryData); if (bShouldUseRetryData) { - UE_LOG(LogBackgroundHttpManager, Display, TEXT("Resuming Task With Resume Data -- RequestID:%s | RetryData Length:%d"), *(Request->GetRequestID()), [RetryData length]); + UE_LOG(LogBackgroundHttpManager, Display, TEXT("Resuming Task With Resume Data -- RequestDebugID:%s | RetryData Length:%d"), *(Request->GetRequestDebugID()), [RetryData length]); NewTask = [BackgroundDownloadSession downloadTaskWithResumeData:RetryData]; } //If not retry data, lets try and just retry on the next CDN - else + if (nullptr == NewTask) { //Since we created a new task instead of using retry data, reset resume data's retry count on the request Request->ResumeDataRetryCount.Reset(); @@ -508,14 +522,14 @@ void FApplePlatformBackgroundHttpManager::RetryRequest(FAppleBackgroundHttpReque Request->ActivateUnderlyingTask(); } - UE_LOG(LogBackgroundHttpManager, Display, TEXT("Created Task for Request -- RequestID:%s | bStartImmediately:%d | bIsAppInBG:%d"), *(Request->GetRequestID()), (int)bShouldStartImmediately, (int)bCopyOfBGState); + UE_LOG(LogBackgroundHttpManager, Display, TEXT("Created Task for Request -- RequestDebugID:%s | bStartImmediately:%d | bIsAppInBG:%d"), *(Request->GetRequestDebugID()), (int)bShouldStartImmediately, (int)bCopyOfBGState); //Always set our bWasTaskStartedInBG flag on our Request so we will know if we need to restart this task next FG Tick. FPlatformAtomics::InterlockedExchange(&(Request->bWasTaskStartedInBG), bCopyOfBGState); } else { - UE_LOG(LogBackgroundHttpManager, Display, TEXT("Marking Request Failed. Out of Retries -- RequestID:%s | bShouldUseRetryData:%d"), *(Request->GetRequestID()), (int)bShouldUseRetryData); + UE_LOG(LogBackgroundHttpManager, Display, TEXT("Marking Request Failed. Out of Retries -- RequestDebugID:%s | bShouldUseRetryData:%d"), *(Request->GetRequestDebugID()), (int)bShouldUseRetryData); Request->SetRequestAsFailed(); } } @@ -551,6 +565,7 @@ void FApplePlatformBackgroundHttpManager::OnTask_DidWriteData(NSURLSessionDownlo if (ensureAlwaysMsgf((nullptr != Task), TEXT("Call to DidWriteData with invalid Task!"))) { FString TaskURL = [[[Task currentRequest] URL] absoluteString]; + int TaskIdentifier = (int)[Task taskIdentifier]; //Find task and update it's download progress { @@ -563,11 +578,11 @@ void FApplePlatformBackgroundHttpManager::OnTask_DidWriteData(NSURLSessionDownlo if (FoundRequest->DownloadProgress < TotalBytesWritten) { int64 DownloadProgress = FPlatformAtomics::AtomicRead(&(FoundRequest->DownloadProgress)); - UE_LOG(LogBackgroundHttpManager, Verbose, TEXT("Updating Task Progress! -- RequestID:%s | Current Progress:%lld | New Progress:%lld"), *(FoundRequest->GetRequestID()), DownloadProgress, TotalBytesWritten); + UE_LOG(LogBackgroundHttpManager, Verbose, TEXT("Updating Task Progress! -- RequestDebugID:%s | TaskIdentifier:%d | Current Progress:%lld | New Progress:%lld"), *(FoundRequest->GetRequestDebugID()), TaskIdentifier, DownloadProgress, TotalBytesWritten); } else { - UE_LOG(LogBackgroundHttpManager, Warning, TEXT("Download Progress tried to go down not up unexpectidly! This could mean a task was unknowingly duplicated! -- RequestID:%s | Current Progress:%lld | New Progress:%lld"), *(FoundRequest->GetRequestID()),FoundRequest->DownloadProgress, TotalBytesWritten); + ensureAlwaysMsgf(false, TEXT("Download Progress tried to go down not up unexpectidly! This could mean a task was unknowingly duplicated! -- RequestDebugID:%s | TaskIdentifier:%d | Current Progress:%lld | New Progress:%lld"), *(FoundRequest->GetRequestDebugID()), TaskIdentifier, FoundRequest->DownloadProgress, TotalBytesWritten); } FoundRequest->UpdateDownloadProgress(TotalBytesWritten, BytesWrittenSinceLastCall); @@ -581,6 +596,8 @@ void FApplePlatformBackgroundHttpManager::OnTask_DidCompleteWithError(NSURLSessi if (ensureAlwaysMsgf((nullptr != Task), TEXT("Call to OnTask_DidCompleteWithError delegate with an invalid task!"))) { FString TaskURL = [[[Task currentRequest] URL] absoluteString]; + int TaskIdentifier = (int)[Task taskIdentifier]; + const bool bDidCompleteWithError = (nullptr != Error); const int ErrorCode = [Error code]; const FString ErrorDescription = [Error localizedDescription]; @@ -593,6 +610,7 @@ void FApplePlatformBackgroundHttpManager::OnTask_DidCompleteWithError(NSURLSessi FString DebugRetryOverrideReason; + //We still come into the function when tasks complete successfully. Only handle actual errors if (bDidCompleteWithError) { FRWScopeLock ScopeLock(URLToRequestMapLock, SLT_ReadOnly); @@ -609,17 +627,17 @@ void FApplePlatformBackgroundHttpManager::OnTask_DidCompleteWithError(NSURLSessi bShouldRetryIncreaseRetryCount = false; DebugRetryOverrideReason = TEXT("Not Connected To Internet"); } - - UE_LOG(LogBackgroundHttpManager, Display, TEXT("DidCompleteWithError for Task. -- URL:%s | bDidFindVaildRequest:%d | bDidCompleteWithError:%d | ErrorCode:%d | bHasResumeData:%d | CancelledReasonKey:%d | RetryOverrideReason:%s | bShouldRetryIncreaseRetryCount:%d | ErrorDescription:%s"), *TaskURL, (int)bDidFindValidRequest, (int)bDidCompleteWithError, ErrorCode, (int)bHasResumeData, CancelledReasonInt, *DebugRetryOverrideReason, (int)bShouldRetryIncreaseRetryCount, *ErrorDescription); + + UE_LOG(LogBackgroundHttpManager, Display, TEXT("DidCompleteWithError for Task. -- URL:%s | TaskIdentifier:%d | bDidFindVaildRequest:%d | bDidCompleteWithError:%d | ErrorCode:%d | bHasResumeData:%d | CancelledReasonKey:%d | RetryOverrideReason:%s | bShouldRetryIncreaseRetryCount:%d | ErrorDescription:%s"), *TaskURL, TaskIdentifier, (int)bDidFindValidRequest, (int)bDidCompleteWithError, ErrorCode, (int)bHasResumeData, CancelledReasonInt, *DebugRetryOverrideReason, (int)bShouldRetryIncreaseRetryCount, *ErrorDescription); if (bDidFindValidRequest) { - RetryRequest(FoundRequest, bShouldRetryIncreaseRetryCount, true, ResumeData); + RetryRequest(FoundRequest, bShouldRetryIncreaseRetryCount, true, ResumeData); } else { - //This can legitimately happen in the case of UnAssociatedTasks completing, so don't error - UE_LOG(LogBackgroundHttpManager, Display, TEXT("No Request Found for Errored Task -- TaskURL:%s"), *TaskURL); + //This can be a valid case because of UnAssociatedTasks, so don't error here + UE_LOG(LogBackgroundHttpManager, Display, TEXT("No request for completing task! -- TaskURL:%s | TaskIdentifier:%d"), *TaskURL, TaskIdentifier); } } } @@ -661,16 +679,16 @@ void FApplePlatformBackgroundHttpManager::TickRequests(float DeltaTime) const bool bWasStartedInBG = FPlatformAtomics::AtomicRead(&(AppleRequest->bWasTaskStartedInBG)); const bool bIsPendingCancel = FPlatformAtomics::AtomicRead(&(AppleRequest->bIsPendingCancel)); - UE_LOG(LogBackgroundHttpManager, VeryVerbose, TEXT("Checking Status of Request on Tick -- RequestID::%s | bIsTaskComplete:%d | bWasStartedInBG:%d"), *(AppleRequest->GetRequestID()), (int)bIsTaskComplete, (int)bWasStartedInBG); + UE_LOG(LogBackgroundHttpManager, VeryVerbose, TEXT("Checking Status of Request on Tick -- RequestDebugID::%s | bIsTaskComplete:%d | bWasStartedInBG:%d"), *(AppleRequest->GetRequestDebugID()), (int)bIsTaskComplete, (int)bWasStartedInBG); if (bIsTaskComplete) { - UE_LOG(LogBackgroundHttpManager, Display, TEXT("Calling FinishRequest On -- RequestID::%s | bIsTaskComplete:%d | bWasStartedInBG:%d"), *(AppleRequest->GetRequestID()), (int)bIsTaskComplete, (int)bWasStartedInBG); + UE_LOG(LogBackgroundHttpManager, Display, TEXT("Calling FinishRequest On -- RequestDebugID::%s | bIsTaskComplete:%d | bWasStartedInBG:%d"), *(AppleRequest->GetRequestDebugID()), (int)bIsTaskComplete, (int)bWasStartedInBG); FinishRequest(AppleRequest); } else if (bWasStartedInBG && !bIsPendingCancel) { - UE_LOG(LogBackgroundHttpManager, Display, TEXT("Cancelling Request Created In BG To Re-Create In FG -- RequestID:%s"), *(AppleRequest->GetRequestID())); + UE_LOG(LogBackgroundHttpManager, Display, TEXT("Cancelling Request Created In BG To Re-Create In FG -- RequestDebugID:%s"), *(AppleRequest->GetRequestDebugID())); //reset to false so we don't run this twice while waiting on recreation FPlatformAtomics::InterlockedExchange(&(AppleRequest->bWasTaskStartedInBG), false); @@ -684,7 +702,7 @@ void FApplePlatformBackgroundHttpManager::TickRequests(float DeltaTime) const bool bShouldTimeOut = AppleRequest->TickTimeOutTimer(DeltaTime); if (bShouldTimeOut) { - UE_LOG(LogBackgroundHttpManager, Display, TEXT("Timing out Request Due To Lack of Server Response -- RequestID:%s"), *(AppleRequest->GetRequestID())); + UE_LOG(LogBackgroundHttpManager, Display, TEXT("Timing out Request Due To Lack of Server Response -- RequestDebugID:%s"), *(AppleRequest->GetRequestDebugID())); //Just cancel the task and let the OnTask_DidCompleteWithError callback handle retrying it if appropriate. AppleRequest->CancelActiveTask(); @@ -736,6 +754,7 @@ void FApplePlatformBackgroundHttpManager::TickTasks(float DeltaTime) if (NewRequestCount <= FPlatformBackgroundHttp::GetPlatformMaxActiveDownloads()) { FString TaskURL = [[[task currentRequest] URL] absoluteString]; + int TaskIdentifier = (int)[task taskIdentifier]; //Try and find Request in map that matches this Task { @@ -747,12 +766,12 @@ void FApplePlatformBackgroundHttpManager::TickTasks(float DeltaTime) if (FoundRequest.IsValid()) { - UE_LOG(LogBackgroundHttpManager, Display, TEXT("Manager Calling to Active Task For Request -- RequestID:%s | TaskURL:%s | CurrentlyActiveRequests:%d"), *(FoundRequest->GetRequestID()), *TaskURL, NewRequestCount); + UE_LOG(LogBackgroundHttpManager, Display, TEXT("Manager Calling to Active Task For Request -- RequestDebugID:%s | TaskURL:%s | TaskIdentifier:%d | CurrentlyActiveRequests:%d"), *(FoundRequest->GetRequestDebugID()), *TaskURL, TaskIdentifier, NewRequestCount); FoundRequest->ActivateUnderlyingTask(); } else { - UE_LOG(LogBackgroundHttpManager, Display, TEXT("Skipping Activating Task as there is no associated Request or Request is paused. Once a Request associates with this task, it can then be activated. -- TaskURL:%s | bIsPaused:%d"), *TaskURL, (int)bIsPaused); + UE_LOG(LogBackgroundHttpManager, Display, TEXT("Skipping Activating Task as there is no associated Request or Request is paused. Once a Request associates with this task, it can then be activated. -- TaskURL:%s | TaskIdentifier:%d| bIsPaused:%d"), *TaskURL, TaskIdentifier, (int)bIsPaused); //Don't activate and remove our increment from above because something put us over the limit before we resumed FPlatformAtomics::InterlockedDecrement(&NumCurrentlyActiveTasks); @@ -762,7 +781,9 @@ void FApplePlatformBackgroundHttpManager::TickTasks(float DeltaTime) else { FString TaskURL = [[[task currentRequest] URL] absoluteString]; - UE_LOG(LogBackgroundHttpManager, Log, TEXT("Task failed to activate as we passed the platform max from another task before we could resume. Task -- TaskURL:%s | CurrentlyActiveRequests:%d"), *TaskURL, NewRequestCount); + int TaskIdentifier = (int)[task taskIdentifier]; + + UE_LOG(LogBackgroundHttpManager, Log, TEXT("Task failed to activate as we passed the platform max from another task before we could resume. Task -- TaskURL:%s | TaskIdentifier:%d | CurrentlyActiveRequests:%d"), *TaskURL, TaskIdentifier, NewRequestCount); //Don't activate and remove our increment from above because something put us over the limit before we resumed FPlatformAtomics::InterlockedDecrement(&NumCurrentlyActiveTasks); diff --git a/Engine/Source/Runtime/Online/BackgroundHTTP/Private/IOS/ApplePlatformBackgroundHttpRequest.cpp b/Engine/Source/Runtime/Online/BackgroundHTTP/Private/IOS/ApplePlatformBackgroundHttpRequest.cpp index a4dbdebcc4da..22a321b268b3 100644 --- a/Engine/Source/Runtime/Online/BackgroundHTTP/Private/IOS/ApplePlatformBackgroundHttpRequest.cpp +++ b/Engine/Source/Runtime/Online/BackgroundHTTP/Private/IOS/ApplePlatformBackgroundHttpRequest.cpp @@ -11,10 +11,12 @@ FApplePlatformBackgroundHttpRequest::FApplePlatformBackgroundHttpRequest() , RetryCount(0) , ResumeDataRetryCount(0) , FirstTask(nullptr) + , FirstTaskIdentifier(0) , bIsTaskActive(false) , bIsTaskPaused(false) , bIsCompleted(false) , bIsFailed(false) + , bIsRequestSwitchingTasks(false) , bWasTaskStartedInBG(false) , bHasAlreadyFinishedRequest(false) , bIsPendingCancel(false) @@ -44,7 +46,7 @@ void FApplePlatformBackgroundHttpRequest::SetRequestAsFailed() void FApplePlatformBackgroundHttpRequest::CompleteRequest_Internal(bool bWasRequestSuccess, const FString& CompletedTempDownloadLocationIn) { - UE_LOG(LogBackgroundHttpRequest, Display, TEXT("Marking Request Complete -- RequestID:%s | bWasRequestSuccess:%d | CompletedTempDownloadLocation:%s"), *GetRequestID(), (int)bWasRequestSuccess, *CompletedTempDownloadLocationIn); + UE_LOG(LogBackgroundHttpRequest, Display, TEXT("Marking Request Complete -- RequestDebugID:%s | bWasRequestSuccess:%d | CompletedTempDownloadLocation:%s"), *GetRequestDebugID(), (int)bWasRequestSuccess, *CompletedTempDownloadLocationIn); FPlatformAtomics::InterlockedExchange(&bIsTaskActive, false); FPlatformAtomics::InterlockedExchange(&bIsCompleted, true); @@ -66,7 +68,7 @@ const FString& FApplePlatformBackgroundHttpRequest::GetURLForRetry(bool bShouldI //If we are out of Retries, just send an empty string if (NewRetryCount > NumberOfTotalRetries) { - UE_LOG(LogBackgroundHttpRequest, Display, TEXT("GetURLForRetry is out of Retries for Request -- RequestID:%s"), *GetRequestID()); + UE_LOG(LogBackgroundHttpRequest, Display, TEXT("GetURLForRetry is out of Retries for Request -- RequestDebugID:%s"), *GetRequestDebugID()); static FString EmptyResponse = TEXT(""); return EmptyResponse; @@ -77,7 +79,7 @@ const FString& FApplePlatformBackgroundHttpRequest::GetURLForRetry(bool bShouldI const int URLIndex = NewRetryCount % URLList.Num(); const FString& URLToReturn = URLList[URLIndex]; - UE_LOG(LogBackgroundHttpRequest, Display, TEXT("GetURLForRetry found valid URL for current retry -- RequestID:%s | NewRetryCount:%d | URLToReturn:%s"), *GetRequestID(), NewRetryCount, *URLToReturn); + UE_LOG(LogBackgroundHttpRequest, Display, TEXT("GetURLForRetry found valid URL for current retry -- RequestDebugID:%s | NewRetryCount:%d | URLToReturn:%s"), *GetRequestDebugID(), NewRetryCount, *URLToReturn); return URLToReturn; } } @@ -96,7 +98,9 @@ void FApplePlatformBackgroundHttpRequest::ActivateUnderlyingTask() if (ensureAlwaysMsgf((nullptr != UnderlyingTask), TEXT("Call to ActivateUnderlyingTask with an invalid task! Need to create underlying task before activating!"))) { FString TaskURL = [[[UnderlyingTask currentRequest] URL] absoluteString]; - UE_LOG(LogBackgroundHttpRequest, Display, TEXT("Activating Task for Request -- RequestID:%s | TaskURL:%s"), *GetRequestID(), *TaskURL); + int TaskIdentifier = (int)[UnderlyingTask taskIdentifier]; + + UE_LOG(LogBackgroundHttpRequest, Display, TEXT("Activating Task for Request -- RequestDebugID:%s | TaskIdentifier:%d | TaskURL:%s"), *GetRequestDebugID(), TaskIdentifier, *TaskURL); FPlatformAtomics::InterlockedExchange(&bIsTaskActive, true); FPlatformAtomics::InterlockedExchange(&bIsTaskPaused, false); @@ -119,7 +123,9 @@ void FApplePlatformBackgroundHttpRequest::PauseUnderlyingTask() if (ensureAlwaysMsgf((nullptr != UnderlyingTask), TEXT("Call to PauseUnderlyingTask with an invalid task! Need to create underlying task before trying to pause!"))) { FString TaskURL = [[[UnderlyingTask currentRequest] URL] absoluteString]; - UE_LOG(LogBackgroundHttpRequest, Display, TEXT("Pausing Task for Request -- RequestID:%s | TaskURL:%s"), *GetRequestID(), *TaskURL); + int TaskIdentifier = (int)[UnderlyingTask taskIdentifier]; + + UE_LOG(LogBackgroundHttpRequest, Display, TEXT("Pausing Task for Request -- RequestDebugID:%s | TaskIdentifier:%d | TaskURL:%s"), *GetRequestDebugID(), TaskIdentifier, *TaskURL); FPlatformAtomics::InterlockedExchange(&bIsTaskActive, false); FPlatformAtomics::InterlockedExchange(&bIsPendingCancel, false); @@ -154,27 +160,48 @@ void FApplePlatformBackgroundHttpRequest::ResetTimeOutTimer() ActiveTimeOutTimer = FApplePlatformBackgroundHttpManager::ActiveTimeOutSetting; } -void FApplePlatformBackgroundHttpRequest::AssociateWithTask(NSURLSessionTask* ExistingTask) +bool FApplePlatformBackgroundHttpRequest::AssociateWithTask(NSURLSessionTask* ExistingTask) { - if (ensureAlwaysMsgf((nullptr != ExistingTask), TEXT("Call to AssociateWithTask with an invalid Task! RequestID:%s"), *GetRequestID())) + bool bDidAssociate = false; + + if (ensureAlwaysMsgf((nullptr != ExistingTask), TEXT("Call to AssociateWithTask with an invalid Task! RequestDebugID:%s"), *GetRequestDebugID())) { - volatile FTaskNode* NewNode = new FTaskNode(); - NewNode->OurTask = ExistingTask; - - //Add a count to our task's reference list so it doesn't get deleted while in our Request's task list - [ExistingTask retain]; - - //Swap our new node and the first one in the list - NewNode->NextNode = (FTaskNode*)FPlatformAtomics::InterlockedExchangePtr((void**)(&FirstTask), (void*)NewNode); - - FString TaskURL = [[[ExistingTask currentRequest] URL] absoluteString]; - UE_LOG(LogBackgroundHttpRequest, Display, TEXT("Associated Request With New Task -- RequestID:%s | TaskURL:%s"), *GetRequestID(), *TaskURL); + const FString TaskURL = [[[ExistingTask currentRequest] URL] absoluteString]; + int TaskIdentifier = (int)[ExistingTask taskIdentifier]; - FPlatformAtomics::InterlockedExchange(&bIsPendingCancel, false); - - ResetTimeOutTimer(); - ResetProgressTracking(); + const bool bWasAlreadySwitching = FPlatformAtomics::InterlockedExchange(&bIsRequestSwitchingTasks, true); + if (!bWasAlreadySwitching) + { + volatile FTaskNode* NewNode = new FTaskNode(); + NewNode->OurTask = ExistingTask; + + //Add a count to our task's reference list so it doesn't get deleted while in our Request's task list + [ExistingTask retain]; + + //Swap our new node and the first one in the list + NewNode->NextNode = (FTaskNode*)FPlatformAtomics::InterlockedExchangePtr((void**)(&FirstTask), (void*)NewNode); + + //Save off our first task's identifier and our CombinedRequestID that includes it + FirstTaskIdentifier = (int)[(FirstTask->OurTask) taskIdentifier]; + CombinedRequestID = FString::Printf(TEXT("%d.%s"), FirstTaskIdentifier, *RequestID); + + UE_LOG(LogBackgroundHttpRequest, Display, TEXT("Associated Request With New Task -- RequestDebugID:%s | TaskIdentifier:%d | TaskURL:%s"), *GetRequestDebugID(), TaskIdentifier, *TaskURL); + + FPlatformAtomics::InterlockedExchange(&bIsPendingCancel, false); + + ResetTimeOutTimer(); + ResetProgressTracking(); + + bDidAssociate = true; + FPlatformAtomics::InterlockedExchange(&bIsRequestSwitchingTasks, false); + } + else + { + UE_LOG(LogBackgroundHttpRequest, Display, TEXT("Failed to Associate Request with new Task as there was already a pending AssociateWithTask running! -- RequestDebugID:%s | TaskIdentifier:%d | TaskURL:%s"), *GetRequestDebugID(), TaskIdentifier, *TaskURL); + } } + + return bDidAssociate; } void FApplePlatformBackgroundHttpRequest::PauseRequest() @@ -195,7 +222,9 @@ void FApplePlatformBackgroundHttpRequest::CancelActiveTask() if (nullptr != TaskNodeWeAreCancelling->OurTask) { FString TaskURL = [[[TaskNodeWeAreCancelling->OurTask currentRequest] URL] absoluteString]; - UE_LOG(LogBackgroundHttpRequest, Display, TEXT("Cancelling Task -- RequestID:%s | TaskURL:%s"), *GetRequestID(), *TaskURL); + int TaskIdentifier = (int)[TaskNodeWeAreCancelling->OurTask taskIdentifier]; + + UE_LOG(LogBackgroundHttpRequest, Display, TEXT("Cancelling Task -- RequestDebugID:%s | TaskIdentifier:%d | TaskURL:%s"), *GetRequestDebugID(), TaskIdentifier, *TaskURL); FPlatformAtomics::InterlockedExchange(&bIsPendingCancel, true); @@ -206,7 +235,7 @@ void FApplePlatformBackgroundHttpRequest::CancelActiveTask() void FApplePlatformBackgroundHttpRequest::UpdateDownloadProgress(int64_t TotalDownloaded,int64_t DownloadedSinceLastUpdate) { - UE_LOG(LogBackgroundHttpRequest, VeryVerbose, TEXT("Request Update Progress -- RequestID:%s | OldProgress:%lld | NewProgress:%lld | ProgressSinceLastUpdate:%lld"), *GetRequestID(), DownloadProgress, TotalDownloaded, DownloadedSinceLastUpdate); + UE_LOG(LogBackgroundHttpRequest, VeryVerbose, TEXT("Request Update Progress -- RequestDebugID:%s | OldProgress:%lld | NewProgress:%lld | ProgressSinceLastUpdate:%lld"), *GetRequestDebugID(), DownloadProgress, TotalDownloaded, DownloadedSinceLastUpdate); FPlatformAtomics::AtomicStore(&DownloadProgress, TotalDownloaded); FPlatformAtomics::InterlockedAdd(&DownloadProgressSinceLastUpdateSent, DownloadedSinceLastUpdate); @@ -228,6 +257,12 @@ void FApplePlatformBackgroundHttpRequest::SendDownloadProgressUpdate() } } +const FString& FApplePlatformBackgroundHttpRequest::GetRequestDebugID() const +{ + //We use CombinedRequestID to append a TaskIdentifier on the end of the RequestID. If we have one, use that. Otherwise fallback on what is already set + return CombinedRequestID.IsEmpty() ? RequestID : CombinedRequestID; +} + bool FApplePlatformBackgroundHttpRequest::IsTaskComplete() const { const bool bDidRequestFail = FPlatformAtomics::AtomicRead(&bIsFailed); diff --git a/Engine/Source/Runtime/Online/BackgroundHTTP/Public/BackgroundHttpManagerImpl.h b/Engine/Source/Runtime/Online/BackgroundHTTP/Public/BackgroundHttpManagerImpl.h index 3e73cd7983e7..0a09ea795c9b 100644 --- a/Engine/Source/Runtime/Online/BackgroundHTTP/Public/BackgroundHttpManagerImpl.h +++ b/Engine/Source/Runtime/Online/BackgroundHTTP/Public/BackgroundHttpManagerImpl.h @@ -37,6 +37,7 @@ protected: virtual bool CheckForExistingCompletedDownload(const FBackgroundHttpRequestPtr Request, FString& ExistingFilePathOut, int64& ExistingFileSizeOut); virtual void ActivatePendingRequests(); + virtual void ClearAnyTempFilesFromTimeOut(); protected: /** List of Background Http requests that we have called AddRequest on, but have not yet started due to platform active download limits **/ diff --git a/Engine/Source/Runtime/Online/BackgroundHTTP/Public/IOS/ApplePlatformBackgroundHttpRequest.h b/Engine/Source/Runtime/Online/BackgroundHTTP/Public/IOS/ApplePlatformBackgroundHttpRequest.h index 4d2843cf2ca5..6c29137e7108 100644 --- a/Engine/Source/Runtime/Online/BackgroundHTTP/Public/IOS/ApplePlatformBackgroundHttpRequest.h +++ b/Engine/Source/Runtime/Online/BackgroundHTTP/Public/IOS/ApplePlatformBackgroundHttpRequest.h @@ -23,7 +23,11 @@ public: virtual bool IsTaskComplete() const; - + //Used to provide some extra debug information over normal GetRequestID() + //Returns string in format of X.Y where X is the underlying Task Identifier if set and Y is what was set in the SetRequestID() call if this reqeust has associated with a task. + //Returns the same as GetRequestID() if no task has been associated yet. + const FString& GetRequestDebugID() const; + private: //Super simple linked list with volatile pointers to next element struct FTaskNode @@ -34,7 +38,7 @@ private: private: const FString& GetURLForRetry(bool bShouldIncrementRetryCountFirst); - void AssociateWithTask(NSURLSessionTask* ExistingTask); + bool AssociateWithTask(NSURLSessionTask* ExistingTask); void SetRequestAsSuccess(const FString& CompletedTempDownloadLocation); void SetRequestAsFailed(); void CompleteRequest_Internal(bool bWasRequestSuccess, const FString& CompletedTempDownloadLocation); @@ -60,18 +64,22 @@ private: FThreadSafeCounter ResumeDataRetryCount; volatile FTaskNode* FirstTask; + + volatile int FirstTaskIdentifier; + FString CombinedRequestID; volatile int32 bIsTaskActive; volatile int32 bIsTaskPaused; volatile int32 bIsCompleted; volatile int32 bIsFailed; + volatile int32 bIsRequestSwitchingTasks; volatile int32 bWasTaskStartedInBG; volatile int32 bHasAlreadyFinishedRequest; volatile int32 bIsPendingCancel; volatile int64 DownloadProgress; volatile int64 DownloadProgressSinceLastUpdateSent; - + friend class FApplePlatformBackgroundHttpManager; }; diff --git a/Engine/Source/Runtime/Online/HTTP/Public/Interfaces/IHttpRequest.h b/Engine/Source/Runtime/Online/HTTP/Public/Interfaces/IHttpRequest.h index 38a7f6179dd8..c6f0f47f8a6f 100644 --- a/Engine/Source/Runtime/Online/HTTP/Public/Interfaces/IHttpRequest.h +++ b/Engine/Source/Runtime/Online/HTTP/Public/Interfaces/IHttpRequest.h @@ -62,8 +62,14 @@ namespace EHttpRequestStatus } } -typedef TSharedPtr FHttpRequestPtr; -typedef TSharedPtr FHttpResponsePtr; +class IHttpRequest; +class IHttpResponse; + +typedef TSharedPtr FHttpRequestPtr; +typedef TSharedPtr FHttpResponsePtr; + +typedef TSharedRef FHttpRequestRef; +typedef TSharedRef FHttpResponseRef; /** * Delegate called when an Http request completes diff --git a/Engine/Source/Runtime/Online/Voice/Private/VoicePrivate.h b/Engine/Source/Runtime/Online/Voice/Private/VoicePrivate.h index 31125482aeb9..e8b97a08c457 100644 --- a/Engine/Source/Runtime/Online/Voice/Private/VoicePrivate.h +++ b/Engine/Source/Runtime/Online/Voice/Private/VoicePrivate.h @@ -23,7 +23,7 @@ THIRD_PARTY_INCLUDES_END #endif // PLATFORM_WINDOWS #define ANDROIDVOICE_SUPPORTED_PLATFORMS (PLATFORM_ANDROID && (PLATFORM_ANDROID_ARM || PLATFORM_ANDROID_ARM64 || PLATFORM_ANDROID_X64) && !PLATFORM_LUMIN) -#define PLATFORM_SUPPORTS_VOICE_CAPTURE (PLATFORM_WINDOWS || PLATFORM_MAC || ANDROIDVOICE_SUPPORTED_PLATFORMS || (PLATFORM_LINUX && VOICE_MODULE_WITH_CAPTURE)) +#define PLATFORM_SUPPORTS_VOICE_CAPTURE (PLATFORM_WINDOWS || PLATFORM_MAC || ANDROIDVOICE_SUPPORTED_PLATFORMS || (PLATFORM_UNIX && VOICE_MODULE_WITH_CAPTURE)) #define PLATFORM_SUPPORTS_OPUS_CODEC (!PLATFORM_HTML5 && !PLATFORM_TVOS) // Module includes diff --git a/Engine/Source/Runtime/Online/XMPP/Private/XmppConnection.cpp b/Engine/Source/Runtime/Online/XMPP/Private/XmppConnection.cpp index 67bdf45fc047..4ac5fdd12737 100644 --- a/Engine/Source/Runtime/Online/XMPP/Private/XmppConnection.cpp +++ b/Engine/Source/Runtime/Online/XMPP/Private/XmppConnection.cpp @@ -15,6 +15,33 @@ enum class XMPP_RESOURCE_VERSION : uint8 LATEST = VERSION_PLUSONE - 1 }; +FXmppUserJid FXmppUserJid::FromFullJid(const FString& JidString) +{ + FString User; + FString Domain; + FString Resource; + + FString DomainAndResource; + if (JidString.Split(TEXT("@"), &User, &DomainAndResource, ESearchCase::CaseSensitive, ESearchDir::FromStart)) + { + if (!DomainAndResource.Split(TEXT("/"), &Domain, &Resource, ESearchCase::CaseSensitive, ESearchDir::FromEnd)) + { + // If we don't have a resource, the domain is all of DomainAndResource + Domain = MoveTemp(DomainAndResource); + } + } + else + { + if (!JidString.Split(TEXT("/"), &Domain, &Resource, ESearchCase::CaseSensitive, ESearchDir::FromEnd)) + { + // If we don't have a resource, we only have the Domain in the JidString + Domain = JidString; + } + } + + return FXmppUserJid(MoveTemp(User), MoveTemp(Domain), MoveTemp(Resource)); +} + bool FXmppUserJid::ParseResource(const FString& InResource, FString& OutAppId, FString& OutPlatform, FString& OutPlatformUserId) { OutAppId.Empty(); diff --git a/Engine/Source/Runtime/Online/XMPP/Private/XmppStrophe/XmppStrophe.cpp b/Engine/Source/Runtime/Online/XMPP/Private/XmppStrophe/XmppStrophe.cpp index 92382148a491..6b1e6fae4efc 100644 --- a/Engine/Source/Runtime/Online/XMPP/Private/XmppStrophe/XmppStrophe.cpp +++ b/Engine/Source/Runtime/Online/XMPP/Private/XmppStrophe/XmppStrophe.cpp @@ -34,29 +34,7 @@ FString FXmppStrophe::JidToString(const FXmppUserJid& UserJid) FXmppUserJid FXmppStrophe::JidFromString(const FString& JidString) { - FString User; - FString Domain; - FString Resource; - - FString DomainAndResource; - if (JidString.Split(TEXT("@"), &User, &DomainAndResource, ESearchCase::CaseSensitive, ESearchDir::FromStart)) - { - if (!DomainAndResource.Split(TEXT("/"), &Domain, &Resource, ESearchCase::CaseSensitive, ESearchDir::FromEnd)) - { - // If we don't have a resource, the domain is all of DomainAndResource - Domain = MoveTemp(DomainAndResource); - } - } - else - { - if (!JidString.Split(TEXT("/"), &Domain, &Resource, ESearchCase::CaseSensitive, ESearchDir::FromEnd)) - { - // If we don't have a resource, we only have the Domain in the JidString - Domain = JidString; - } - } - - return FXmppUserJid(MoveTemp(User), MoveTemp(Domain), MoveTemp(Resource)); + return FXmppUserJid::FromFullJid(JidString); } FXmppUserJid FXmppStrophe::JidFromStropheString(const char* StropheJidString) diff --git a/Engine/Source/Runtime/Online/XMPP/Public/XmppConnection.h b/Engine/Source/Runtime/Online/XMPP/Public/XmppConnection.h index c54d4fccfe8c..f953b94cf159 100644 --- a/Engine/Source/Runtime/Online/XMPP/Public/XmppConnection.h +++ b/Engine/Source/Runtime/Online/XMPP/Public/XmppConnection.h @@ -103,6 +103,15 @@ public: { } + /** + * Create an FXmppUserJid from a formatted JID + * e.g., user@domain/resource + * + * @param JidString the formatted JID + * @return FXmppUserJid populated from the formatted JID + */ + static FXmppUserJid FromFullJid(const FString& JidString); + /** unique id for the user */ FString Id; /** domain user has access to */ diff --git a/Engine/Source/Runtime/OpenGLDrv/Private/OpenGLShaders.cpp b/Engine/Source/Runtime/OpenGLDrv/Private/OpenGLShaders.cpp index 0210e569e397..ad7c2f002738 100644 --- a/Engine/Source/Runtime/OpenGLDrv/Private/OpenGLShaders.cpp +++ b/Engine/Source/Runtime/OpenGLDrv/Private/OpenGLShaders.cpp @@ -2151,7 +2151,7 @@ class FGLProgramCacheLRU else { RHIGetPanicDelegate().ExecuteIfBound(FName("FailedBinaryProgramCreate")); - UE_LOG(LogRHI, Fatal, TEXT("RestoreGLProgramFromBinary : Failed to restore GL program from binary data!")); + UE_LOG(LogRHI, Fatal, TEXT("RestoreGLProgramFromBinary : Failed to restore GL program from binary data! [%s]"), *LinkedProgram->Config.ProgramKey.ToString()); } } @@ -3748,7 +3748,7 @@ FBoundShaderStateRHIRef FOpenGLDynamicRHI::RHICreateBoundShaderState_OnThisThrea #endif //DEBUG_GL_SHADERS FName LinkFailurePanic = bFromPSOFileCache ? FName("FailedProgramLinkDuringPrecompile") : FName("FailedProgramLink"); RHIGetPanicDelegate().ExecuteIfBound(LinkFailurePanic); - UE_LOG(LogRHI, Fatal, TEXT("Failed to link program. Current total programs: %d, precompile: %d"), GNumPrograms, (uint32)bFromPSOFileCache); + UE_LOG(LogRHI, Fatal, TEXT("Failed to link program [%s]. Current total programs: %d, precompile: %d"), *Config.ProgramKey.ToString(), GNumPrograms, (uint32)bFromPSOFileCache); } GetOpenGLProgramsCache().Add(Config.ProgramKey, LinkedProgram); @@ -5121,7 +5121,7 @@ bool FOpenGLProgramBinaryCache::UseCachedProgram_internal(GLuint& ProgramOUT, co if (!bSuccess) { RHIGetPanicDelegate().ExecuteIfBound(FName("FailedBinaryProgramCreateFromOldCache")); - UE_LOG(LogRHI, Fatal, TEXT("UseCachedProgram : Failed to create GL program from binary data while BuildingCacheFileWithMove!")); + UE_LOG(LogRHI, Fatal, TEXT("UseCachedProgram : Failed to create GL program from binary data while BuildingCacheFileWithMove! [%s]"), *ProgramKey.ToString()); } SetNewProgramStats(ProgramOUT); // Now write to new cache, we're returning true here so no attempt will be made to add it back to the cache later. @@ -5264,7 +5264,7 @@ void FOpenGLProgramBinaryCache::CompleteLoadedGLProgramRequest_internal(FGLProgr if(!bSuccess) { RHIGetPanicDelegate().ExecuteIfBound(FName("FailedBinaryProgramCreate")); - UE_LOG(LogRHI, Fatal, TEXT("CompleteLoadedGLProgramRequest_internal : Failed to create GL program from binary data!")); + UE_LOG(LogRHI, Fatal, TEXT("CompleteLoadedGLProgramRequest_internal : Failed to create GL program from binary data! [%s]"), *ProgramKey.ToString()); } VerifyProgramPipeline(PendingGLCreate->GLProgramId); FOpenGLLinkedProgram* NewLinkedProgram = new FOpenGLLinkedProgram(ProgramKey, PendingGLCreate->GLProgramId); diff --git a/Engine/Source/Runtime/OpenGLDrv/Private/Windows/OpenGLWindows.cpp b/Engine/Source/Runtime/OpenGLDrv/Private/Windows/OpenGLWindows.cpp index a4db19866c10..f0268d251179 100644 --- a/Engine/Source/Runtime/OpenGLDrv/Private/Windows/OpenGLWindows.cpp +++ b/Engine/Source/Runtime/OpenGLDrv/Private/Windows/OpenGLWindows.cpp @@ -178,13 +178,18 @@ static void PlatformCreateDummyGLWindow(FPlatformOpenGLContext* OutContext) check(ClassAtom); } + int32 WinX = 0; + int32 WinY = 0; + FParse::Value(FCommandLine::Get(), TEXT("WinX="), WinX); + FParse::Value(FCommandLine::Get(), TEXT("WinY="), WinY); + // Create a dummy window. OutContext->WindowHandle = CreateWindowEx( WS_EX_WINDOWEDGE, WindowClassName, NULL, WS_POPUP, - 0, 0, 1, 1, + WinX, WinY, 1, 1, NULL, NULL, NULL, NULL); check(OutContext->WindowHandle); OutContext->bReleaseWindowOnDestroy = true; diff --git a/Engine/Source/Runtime/PakFile/PakFile.Build.cs b/Engine/Source/Runtime/PakFile/PakFile.Build.cs index ce3793c01c5e..619e33831706 100644 --- a/Engine/Source/Runtime/PakFile/PakFile.Build.cs +++ b/Engine/Source/Runtime/PakFile/PakFile.Build.cs @@ -7,5 +7,6 @@ public class PakFile : ModuleRules public PakFile(ReadOnlyTargetRules Target) : base(Target) { PrivateDependencyModuleNames.Add("Core"); + PublicDependencyModuleNames.Add("RSA"); } } diff --git a/Engine/Source/Runtime/PakFile/Private/IPlatformFilePak.cpp b/Engine/Source/Runtime/PakFile/Private/IPlatformFilePak.cpp index 38f3df439009..cab55c40c6da 100644 --- a/Engine/Source/Runtime/PakFile/Private/IPlatformFilePak.cpp +++ b/Engine/Source/Runtime/PakFile/Private/IPlatformFilePak.cpp @@ -152,7 +152,7 @@ TPakChunkHash ComputePakChunkHash(const void* InData, int64 InDataSizeInBytes) return FCrc::MemCrc32(InData, InDataSizeInBytes); #else FSHAHash Hash; - FSHA1::HashBuffer(InData, InDataSizeInBytes, Hash); + FSHA1::HashBuffer(InData, InDataSizeInBytes, Hash.Hash); return Hash; #endif } @@ -213,17 +213,26 @@ void FPakPlatformFile::GetPakEncryptionKey(FAES::FAESKey& OutKey, const FGuid& I } } -void FPakPlatformFile::GetPakSigningKeys(FEncryptionKey& OutKey) +TSharedPtr FPakPlatformFile::GetPakSigningKey() { - FCoreDelegates::FPakSigningKeysDelegate& Delegate = FCoreDelegates::GetPakSigningKeysDelegate(); - if (Delegate.IsBound()) + static TSharedPtr Key; + static FCriticalSection Lock; + Lock.Lock(); + + if (!Key.IsValid()) { - uint8 Exponent[sizeof(TEncryptionInt)]; - uint8 Modulus[sizeof(TEncryptionInt)]; - Delegate.Execute(Exponent, Modulus); - OutKey.Exponent = TEncryptionInt((uint32*)Exponent); - OutKey.Modulus = TEncryptionInt((uint32*)Modulus); + FCoreDelegates::FPakSigningKeysDelegate& Delegate = FCoreDelegates::GetPakSigningKeysDelegate(); + if (Delegate.IsBound()) + { + TArray Exponent; + TArray Modulus; + Delegate.Execute(Exponent, Modulus); + Key = FRSA::CreateKey(Exponent, TArray(), Modulus); + } } + + Lock.Unlock(); + return Key; } DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("PakCache Sync Decrypts (Uncompressed Path)"), STAT_PakCache_SyncDecrypts, STATGROUP_PakFile); @@ -1038,8 +1047,7 @@ class FPakPrecacher TIntervalTreeIndex InRequests[AIOP_NUM][(int32)EInRequestStatus::Num]; TIntervalTreeIndex CacheBlocks[(int32)EBlockStatus::Num]; - TArray ChunkHashes; - TPakChunkHash OriginalSignatureFileHash; + FPakSignatureFile Signatures; FPakData(IAsyncReadFileHandle* InHandle, FName InName, int64 InTotalSize) : Handle(InHandle) @@ -1048,7 +1056,6 @@ class FPakPrecacher , MaxShift(0) , BytesToBitsShift(0) , Name(InName) - , OriginalSignatureFileHash(0) { check(Handle && TotalSize > 0 && Name != NAME_None); for (int32 Index = 0; Index < AIOP_NUM; Index++) @@ -1129,17 +1136,16 @@ class FPakPrecacher uint32 Loads; uint32 Frees; uint64 LoadSize; - FEncryptionKey EncryptionKey; - bool bSigned; + FRSA::TKeyPtr SigningKey; EAsyncIOPriorityAndFlags AsyncMinPriority; FCriticalSection SetAsyncMinimumPriorityScopeLock; public: - static void Init(IPlatformFile* InLowerLevel, const FEncryptionKey& InEncryptionKey) + static void Init(IPlatformFile* InLowerLevel, FRSA::TKeyPtr InSigningKey) { if (!PakPrecacherSingleton) { - verify(!FPlatformAtomics::InterlockedCompareExchangePointer((void**)&PakPrecacherSingleton, new FPakPrecacher(InLowerLevel, InEncryptionKey), nullptr)); + verify(!FPlatformAtomics::InterlockedCompareExchangePointer((void**)&PakPrecacherSingleton, new FPakPrecacher(InLowerLevel, InSigningKey), nullptr)); } check(PakPrecacherSingleton); } @@ -1175,7 +1181,7 @@ public: return *PakPrecacherSingleton; } - FPakPrecacher(IPlatformFile* InLowerLevel, const FEncryptionKey& InEncryptionKey) + FPakPrecacher(IPlatformFile* InLowerLevel, FRSA::TKeyPtr InSigningKey) : LowerLevel(InLowerLevel) , LastReadRequest(0) , NextUniqueID(1) @@ -1185,8 +1191,7 @@ public: , Loads(0) , Frees(0) , LoadSize(0) - , EncryptionKey(InEncryptionKey) - , bSigned(!InEncryptionKey.Exponent.IsZero() && !InEncryptionKey.Modulus.IsZero()) + , SigningKey(InSigningKey) , AsyncMinPriority(AIOP_MIN) { check(LowerLevel && FPlatformProcess::SupportsMultithreading()); @@ -1213,42 +1218,33 @@ public: uint16* PakIndexPtr = CachedPaks.Find(File); if (!PakIndexPtr) { + FString PakFilename = File.ToString(); check(CachedPakData.Num() < MAX_uint16); - IAsyncReadFileHandle* Handle = LowerLevel->OpenAsyncRead(*File.ToString()); + IAsyncReadFileHandle* Handle = LowerLevel->OpenAsyncRead(*PakFilename); if (!Handle) { return nullptr; } CachedPakData.Add(FPakData(Handle, File, PakFileSize)); PakIndexPtr = &CachedPaks.Add(File, CachedPakData.Num() - 1); - UE_LOG(LogPakFile, Log, TEXT("New pak file %s added to pak precacher."), *File.ToString()); + UE_LOG(LogPakFile, Log, TEXT("New pak file %s added to pak precacher."), *PakFilename); FPakData& Pak = CachedPakData[*PakIndexPtr]; - if (bSigned) + if (SigningKey.IsValid()) { // Load signature data - FString SignaturesFilename = FPaths::ChangeExtension(File.ToString(), TEXT("sig")); + FString SignaturesFilename = FPaths::ChangeExtension(*PakFilename, TEXT("sig")); IFileHandle* SignaturesFile = LowerLevel->OpenRead(*SignaturesFilename); ensure(SignaturesFile); - FArchiveFileReaderGeneric* Reader = new FArchiveFileReaderGeneric(SignaturesFile, *SignaturesFilename, SignaturesFile->Size()); - FEncryptedSignature MasterSignature; - *Reader << MasterSignature; - *Reader << Pak.ChunkHashes; + Pak.Signatures.Serialize(*Reader); delete Reader; + Pak.Signatures.DecryptSignatureAndValidate(SigningKey, PakFilename); // Check that we have the correct match between signature and pre-cache granularity int64 NumPakChunks = Align(PakFileSize, FPakInfo::MaxChunkDataSize) / FPakInfo::MaxChunkDataSize; - ensure(NumPakChunks == Pak.ChunkHashes.Num()); - - // Decrypt signature hash - FDecryptedSignature DecryptedSignature; - FEncryption::DecryptSignature(MasterSignature, DecryptedSignature, EncryptionKey); - - // Check the signatures are still as we expected them - Pak.OriginalSignatureFileHash = ComputePakChunkHash(&Pak.ChunkHashes[0], Pak.ChunkHashes.Num() * sizeof(TPakChunkHash)); - ensure(Pak.OriginalSignatureFileHash == DecryptedSignature.Data); + ensure(NumPakChunks == Pak.Signatures.ChunkHashes.Num()); } } return PakIndexPtr; @@ -1261,10 +1257,9 @@ public: for (FPakData& PakData : CachedPakData) { - for (TPakChunkHash& Hash : PakData.ChunkHashes) + for (TPakChunkHash& Hash : PakData.Signatures.ChunkHashes) { - Hash |= (uint32)FMath::Rand(); - Hash &= (uint32)FMath::Rand(); + *((uint8*)&Hash) |= 0x1; } } } @@ -2069,7 +2064,7 @@ private: // below here we assume CachedFilesScopeLock until we get to the next s FAsyncFileCallBack CallbackFromLower = [this, IndexToFill, bDoCheck](bool bWasCanceled, IAsyncReadRequest* Request) { - if (bSigned && bDoCheck) + if (SigningKey.IsValid() && bDoCheck) { StartSignatureCheck(bWasCanceled, Request, IndexToFill); } @@ -3117,9 +3112,14 @@ void FPakPrecacher::DoSignatureCheck(bool bWasCanceled, IAsyncReadRequest* Reque int64 RequestSize = 0; int64 RequestOffset = 0; uint16 PakIndex; - TPakChunkHash MasterSignatureHash = 0; + FSHAHash MasterSignatureHash; static const int64 MaxHashesToCache = 16; + +#if PAKHASH_USE_CRC TPakChunkHash HashCache[MaxHashesToCache] = { 0 }; +#else + TPakChunkHash HashCache[MaxHashesToCache]; +#endif { // Try and keep lock for as short a time as possible. Find our request and copy out the data we need @@ -3140,11 +3140,11 @@ void FPakPrecacher::DoSignatureCheck(bool bWasCanceled, IAsyncReadRequest* Reque SignatureIndex = RequestOffset / FPakInfo::MaxChunkDataSize; FPakData& PakData = CachedPakData[PakIndex]; - MasterSignatureHash = PakData.OriginalSignatureFileHash; + MasterSignatureHash = PakData.Signatures.DecryptedHash; for (int32 CacheIndex = 0; CacheIndex < FMath::Min(NumSignaturesToCheck, MaxHashesToCache); ++CacheIndex) { - HashCache[CacheIndex] = PakData.ChunkHashes[SignatureIndex + CacheIndex]; + HashCache[CacheIndex] = PakData.Signatures.ChunkHashes[SignatureIndex + CacheIndex]; } } @@ -3164,7 +3164,7 @@ void FPakPrecacher::DoSignatureCheck(bool bWasCanceled, IAsyncReadRequest* Reque FPakData& PakData = CachedPakData[PakIndex]; for (int32 CacheIndex = 0; (CacheIndex < MaxHashesToCache) && ((SignedChunkIndex + CacheIndex) < NumSignaturesToCheck); ++CacheIndex) { - HashCache[CacheIndex] = PakData.ChunkHashes[SignatureIndex + CacheIndex]; + HashCache[CacheIndex] = PakData.Signatures.ChunkHashes[SignatureIndex + CacheIndex]; } } @@ -3179,11 +3179,10 @@ void FPakPrecacher::DoSignatureCheck(bool bWasCanceled, IAsyncReadRequest* Reque FScopeLock Lock(&CachedFilesScopeLock); FPakData* PakData = &CachedPakData[PakIndex]; - UE_LOG(LogPakFile, Warning, TEXT("Pak chunk signing mismatch on chunk [%i/%i]! Expected 0x%8X, Received 0x%8X"), SignatureIndex, PakData->ChunkHashes.Num(), PakData->OriginalSignatureFileHash, ThisHash); + UE_LOG(LogPakFile, Warning, TEXT("Pak chunk signing mismatch on chunk [%i/%i]! Expected 0x%8X, Received 0x%8X"), SignatureIndex, PakData->Signatures.ChunkHashes.Num(), *LexToString(PakData->Signatures.ChunkHashes[SignatureIndex]), *LexToString(ThisHash)); // Check the signatures are still as we expected them - TPakChunkHash CurrentSignatureHash = ComputePakChunkHash(&PakData->ChunkHashes[0], PakData->ChunkHashes.Num() * sizeof(TPakChunkHash)); - if (PakData->OriginalSignatureFileHash != CurrentSignatureHash) + if (PakData->Signatures.DecryptedHash != PakData->Signatures.ComputeCurrentMasterHash()) { UE_LOG(LogPakFile, Warning, TEXT("Master signature table has changed since initialization!")); } @@ -4272,6 +4271,7 @@ FArchive* FPakFile::SetupSignedPakReader(FArchive* ReaderArchive, const TCHAR* F void FPakFile::Initialize(FArchive* Reader) { CachedTotalSize = Reader->TotalSize(); + bool bShouldLoad = false; int32 CompatibleVersion = FPakInfo::PakFile_Version_Latest; LLM_SCOPE(ELLMTag::FileSystem); @@ -4279,27 +4279,36 @@ void FPakFile::Initialize(FArchive* Reader) // Serialize trailer and check if everything is as expected. // start up one to offset the -- below CompatibleVersion++; + int64 FileInfoPos = -1; do - { + { // try the next version down - CompatibleVersion--; - // go to start - Reader->Seek(CachedTotalSize - Info.GetSerializedSize(CompatibleVersion)); + CompatibleVersion--; - // read it in (this will check size, etc, and is considered safe) - Info.Serialize(*Reader, CompatibleVersion); - } - while (Info.Magic != FPakInfo::PakFile_Magic && CompatibleVersion >= FPakInfo::PakFile_Version_Initial); + FileInfoPos = CachedTotalSize - Info.GetSerializedSize(CompatibleVersion); + if (FileInfoPos >= 0) + { + Reader->Seek(FileInfoPos); + // Serialize trailer and check if everything is as expected. + Info.Serialize(*Reader, CompatibleVersion); + if (Info.Magic == FPakInfo::PakFile_Magic) + { + bShouldLoad = true; + } + } + } while (!bShouldLoad && CompatibleVersion >= FPakInfo::PakFile_Version_Initial); + + if (bShouldLoad) + { UE_CLOG(Info.Magic != FPakInfo::PakFile_Magic, LogPakFile, Fatal, TEXT("Trailing magic number (%ud) in '%s' is different than the expected one. Verify your installation."), Info.Magic, *PakFilename); UE_CLOG(!(Info.Version >= FPakInfo::PakFile_Version_Initial && Info.Version <= CompatibleVersion), LogPakFile, Fatal, TEXT("Invalid pak file version (%d) in '%s'. Verify your installation."), Info.Version, *PakFilename); - UE_CLOG((Info.bEncryptedIndex == 1) && (!FCoreDelegates::GetPakEncryptionKeyDelegate().IsBound()), LogPakFile, Fatal, TEXT("Index of pak file '%s' is encrypted, but this executable doesn't have any valid decryption keys"), *PakFilename); + UE_CLOG((Info.bEncryptedIndex == 1) && (!FCoreDelegates::GetPakEncryptionKeyDelegate().IsBound()), LogPakFile, Fatal, TEXT("Index of pak file '%s' is encrypted, but this executable doesn't have any valid decryption keys"), *PakFilename); UE_CLOG(!(Info.IndexOffset >= 0 && Info.IndexOffset < CachedTotalSize), LogPakFile, Fatal, TEXT("Index offset for pak file '%s' is invalid (%lld)"), *PakFilename, Info.IndexOffset); UE_CLOG(!((Info.IndexOffset + Info.IndexSize) >= 0 && (Info.IndexOffset + Info.IndexSize) <= CachedTotalSize), LogPakFile, Fatal, TEXT("Index end offset for pak file '%s' is invalid (%lld)"), *PakFilename, Info.IndexOffset + Info.IndexSize); // If we aren't using a dynamic encryption key, process the pak file using the embedded key if (!Info.EncryptionKeyGuid.IsValid() || GetRegisteredEncryptionKeys().HasKey(Info.EncryptionKeyGuid)) - { LoadIndex(Reader); @@ -4312,6 +4321,7 @@ void FPakFile::Initialize(FArchive* Reader) bIsValid = true; } } +} void FPakFile::LoadIndex(FArchive* Reader) { @@ -5395,11 +5405,8 @@ bool FPakPlatformFile::Initialize(IPlatformFile* Inner, const TCHAR* CmdLine) GameUserSettingsIniFilename = TEXT("GameUserSettings.ini"); #endif - FEncryptionKey DecryptionKey; - GetPakSigningKeys(DecryptionKey); - // signed if we have keys, and are not running with fileopenlog (currently results in a deadlock). - bSigned = !DecryptionKey.Exponent.IsZero() && !DecryptionKey.Modulus.IsZero() && !FParse::Param(FCommandLine::Get(), TEXT("fileopenlog"));; + bSigned = GetPakSigningKey().IsValid() && !FParse::Param(FCommandLine::Get(), TEXT("fileopenlog"));; // Find and mount pak files from the specified directories. TArray PakFolders; @@ -5456,10 +5463,7 @@ void FPakPlatformFile::InitializeNewAsyncIO() #if !WITH_EDITOR if (FPlatformProcess::SupportsMultithreading() && !FParse::Param(FCommandLine::Get(), TEXT("FileOpenLog"))) { - FEncryptionKey DecryptionKey; - GetPakSigningKeys(DecryptionKey); - - FPakPrecacher::Init(LowerLevel, DecryptionKey); + FPakPrecacher::Init(LowerLevel, GetPakSigningKey()); } else #endif @@ -5950,6 +5954,7 @@ public: virtual void StartupModule() override { Singleton = MakeUnique(); + FModuleManager::LoadModuleChecked(TEXT("RSA")); } virtual void ShutdownModule() override diff --git a/Engine/Source/Runtime/PakFile/Private/SignedArchiveReader.cpp b/Engine/Source/Runtime/PakFile/Private/SignedArchiveReader.cpp index 3458c5d1168a..83f2f16aa753 100644 --- a/Engine/Source/Runtime/PakFile/Private/SignedArchiveReader.cpp +++ b/Engine/Source/Runtime/PakFile/Private/SignedArchiveReader.cpp @@ -36,8 +36,6 @@ FChunkCacheWorker::FChunkCacheWorker(FArchive* InReader, const TCHAR* Filename) , QueuedRequestsEvent(nullptr) , ChunkRequestAvailable(nullptr) { - SetupDecryptionKey(); - FString SigFileFilename = FPaths::ChangeExtension(Filename, TEXT("sig")); FArchive* SigFileReader = IFileManager::Get().CreateFileReader(*SigFileFilename); @@ -46,18 +44,9 @@ FChunkCacheWorker::FChunkCacheWorker(FArchive* InReader, const TCHAR* Filename) UE_LOG(LogPakFile, Fatal, TEXT("Couldn't find pak signature file '%s'"), *SigFileFilename); } - FEncryptedSignature MasterSignature; - *SigFileReader << MasterSignature; - *SigFileReader << ChunkHashes; + Signatures.Serialize(*SigFileReader); delete SigFileReader; - - OriginalSignatureFileHash = ComputePakChunkHash(&ChunkHashes[0], ChunkHashes.Num() * sizeof(TPakChunkHash)); - FDecryptedSignature DecryptedMasterSignature; - FEncryption::DecryptSignature(MasterSignature, DecryptedMasterSignature, DecryptionKey); - if (DecryptedMasterSignature.Data != OriginalSignatureFileHash) - { - FPakPlatformFile::GetPakMasterSignatureTableCheckFailureHandler().Broadcast(Filename); - } + Signatures.DecryptSignatureAndValidate(Filename); const bool bEnableMultithreading = FPlatformProcess::SupportsMultithreading(); if (bEnableMultithreading) @@ -89,31 +78,6 @@ bool FChunkCacheWorker::Init() return true; } -void FChunkCacheWorker::SetupDecryptionKey() -{ - FPakPlatformFile::GetPakSigningKeys(DecryptionKey); - - // Public key should never be zero at this point. - UE_CLOG(DecryptionKey.Exponent.IsZero() || DecryptionKey.Modulus.IsZero(), LogPakFile, Fatal, TEXT("Invalid decryption key detected")); - // Public key should produce decrypted results - check for identity keys - static TEncryptionInt TestValues[] = - { - 11, - 23, - 67, - 121, - 180, - 211 - }; - bool bIdentical = true; - for (int32 Index = 0; bIdentical && Index < ARRAY_COUNT(TestValues); ++Index) - { - TEncryptionInt DecryptedValue = FEncryption::ModularPow(TestValues[Index], DecryptionKey.Exponent, DecryptionKey.Modulus); - bIdentical = (DecryptedValue == TestValues[Index]); - } - UE_CLOG(bIdentical, LogPakFile, Fatal, TEXT("Decryption key produces identical results to source data.")); -} - uint32 FChunkCacheWorker::Run() { check(QueuedRequestsEvent); @@ -265,7 +229,7 @@ bool FChunkCacheWorker::CheckSignature(const FChunkRequest& ChunkInfo) { SCOPE_SECONDS_ACCUMULATOR(STAT_FChunkCacheWorker_CheckSignature); - TPakChunkHash ChunkHash = 0; + TPakChunkHash ChunkHash; { SCOPE_SECONDS_ACCUMULATOR(STAT_FChunkCacheWorker_Serialize); @@ -277,18 +241,17 @@ bool FChunkCacheWorker::CheckSignature(const FChunkRequest& ChunkInfo) ChunkHash = ComputePakChunkHash(ChunkInfo.Buffer->Data, ChunkInfo.Size); } - bool bChunkHashesMatch = (ChunkHash == ChunkHashes[ChunkInfo.Index]); + bool bChunkHashesMatch = (ChunkHash == Signatures.ChunkHashes[ChunkInfo.Index]); if (!bChunkHashesMatch) { - UE_LOG(LogPakFile, Warning, TEXT("Pak chunk signing mismatch on chunk [%i/%i]! Expected 0x%8X, Received 0x%8X"), ChunkInfo.Index, ChunkHashes.Num(), ChunkHashes[ChunkInfo.Index], ChunkHash); + UE_LOG(LogPakFile, Warning, TEXT("Pak chunk signing mismatch on chunk [%i/%i]! Expected 0x%8X, Received 0x%8X"), ChunkInfo.Index, Signatures.ChunkHashes.Num(), *LexToString(Signatures.ChunkHashes[ChunkInfo.Index]), *LexToString(ChunkHash)); - TPakChunkHash CurrentSignatureHash = ComputePakChunkHash(&ChunkHashes[0], ChunkHashes.Num() * sizeof(TPakChunkHash)); - if (OriginalSignatureFileHash != CurrentSignatureHash) + if (Signatures.DecryptedHash != Signatures.ComputeCurrentMasterHash()) { UE_LOG(LogPakFile, Warning, TEXT("Master signature table has changed since initialization!")); } - const FPakChunkSignatureCheckFailedData Data(Reader->GetArchiveName(), ChunkHashes[ChunkInfo.Index], ChunkHash, ChunkInfo.Index); + const FPakChunkSignatureCheckFailedData Data(Reader->GetArchiveName(), Signatures.ChunkHashes[ChunkInfo.Index], ChunkHash, ChunkInfo.Index); FPakPlatformFile::GetPakChunkSignatureCheckFailedHandler().Broadcast(Data); } diff --git a/Engine/Source/Runtime/PakFile/Private/SignedArchiveReader.h b/Engine/Source/Runtime/PakFile/Private/SignedArchiveReader.h index 2826cbc52509..c10b3f341113 100644 --- a/Engine/Source/Runtime/PakFile/Private/SignedArchiveReader.h +++ b/Engine/Source/Runtime/PakFile/Private/SignedArchiveReader.h @@ -105,7 +105,7 @@ class FChunkCacheWorker : public FRunnable }; /** Reference hashes */ - TArray ChunkHashes; + FPakSignatureFile Signatures; /** Hash of the sig file data. Used to check that nothing was corrupted when a signature check fails */ TPakChunkHash OriginalSignatureFileHash; /** Thread to run the worker FRunnable on */ @@ -130,8 +130,6 @@ class FChunkCacheWorker : public FRunnable FThreadSafeCounter StopTaskCounter; /** Available chunk requests */ TLockFreePointerListUnordered FreeChunkRequests; - /** Public decryption key */ - FEncryptionKey DecryptionKey; /** * Process requested chunks diff --git a/Engine/Source/Runtime/PakFile/Public/IPlatformFilePak.h b/Engine/Source/Runtime/PakFile/Public/IPlatformFilePak.h index ec9e7343fdd5..e29d07d726fd 100644 --- a/Engine/Source/Runtime/PakFile/Public/IPlatformFilePak.h +++ b/Engine/Source/Runtime/PakFile/Public/IPlatformFilePak.h @@ -11,6 +11,8 @@ #include "Templates/UniquePtr.h" #include "Math/BigInt.h" #include "Misc/AES.h" +#include "RSA.h" +#include "Misc/SecureHash.h" #include "GenericPlatform/GenericPlatformChunkInstall.h" class FChunkCacheWorker; @@ -21,9 +23,10 @@ DECLARE_FLOAT_ACCUMULATOR_STAT_EXTERN(TEXT("Total pak file read time"), STAT_Pak DECLARE_DWORD_ACCUMULATOR_STAT_EXTERN(TEXT("Num open pak file handles"), STAT_PakFile_NumOpenHandles, STATGROUP_PakFile, PAKFILE_API); -#define PAKHASH_USE_CRC 1 #define PAK_TRACKER 0 +// Define the type of a chunk hash. Currently selectable between SHA1 and CRC32. +#define PAKHASH_USE_CRC 1 #if PAKHASH_USE_CRC typedef uint32 TPakChunkHash; #else @@ -1511,9 +1514,9 @@ public: static void GetPakEncryptionKey(FAES::FAESKey& OutKey, const FGuid& InEncryptionKeyGuid); /** - * Helper function for accessing pak signing keys + * Helper function for accessing pak the signing public key */ - static void GetPakSigningKeys(FEncryptionKey& OutKey); + static TSharedPtr GetPakSigningKey(); /** * Constructor. @@ -2326,3 +2329,105 @@ public: #endif }; +/** + * Structure which describes the content of the pak .sig files + */ +struct FPakSignatureFile +{ + // Magic number that tells us we're dealing with the new format sig files + static const uint32 Magic = 0x73832DAA; + + enum class EVersion + { + Legacy, + First, + + Last, + Latest = Last - 1 + }; + + // Sig file version. Set to Legacy if the sig file is of an old version + EVersion Version = EVersion::Latest; + + // RSA encrypted hash + TArray EncryptedHash; + + // SHA1 hash of the chunk CRC data. Only valid after calling DecryptSignatureAndValidate + FSHAHash DecryptedHash; + + // CRCs of each contiguous 64kb block of the pak file + TArray ChunkHashes; + + /** + * Initialize and hash the CRC list then use the provided private key to encrypt the hash + */ + void SetChunkHashesAndSign(const TArray& InChunkHashes, const FRSA::TKeyPtr InKey) + { + ChunkHashes = InChunkHashes; + DecryptedHash = ComputeCurrentMasterHash(); + FRSA::Encrypt(FRSA::EKeyType::Private, DecryptedHash.Hash, ARRAY_COUNT(FSHAHash::Hash), EncryptedHash, InKey); + } + + /** + * Serialize/deserialize this object to/from an FArchive + */ + void Serialize(FArchive& Ar) + { + int64 StartPos = Ar.Tell(); + uint32 FileMagic = Magic; + Ar << FileMagic; + + if (Ar.IsLoading() && FileMagic != Magic) + { + // Old format with no versioning! Go back to where we were and mark our version as legacy + Ar.Seek(StartPos); + Version = EVersion::Legacy; + TEncryptionInt LegacyEncryptedCRC; + Ar << LegacyEncryptedCRC; + Ar << ChunkHashes; + // Note that we don't do any actual signature checking here because this is old data that won't have been + // encrypted with the new larger keys. + return; + } + + Ar << Version; + Ar << EncryptedHash; + Ar << ChunkHashes; + } + + /** + * Decrypt the chunk CRCs hash and validate that it matches the current one + */ + bool DecryptSignatureAndValidate(const FString& InFilename) + { + TSharedPtr PublicKey = FPakPlatformFile::GetPakSigningKey(); + return DecryptSignatureAndValidate(PublicKey, InFilename); + } + + /** + * Decrypt the chunk CRCs hash and validate that it matches the current one + */ + bool DecryptSignatureAndValidate(FRSA::TKeyPtr InKey, const FString& InFilename) + { + FRSA::Decrypt(FRSA::EKeyType::Public, EncryptedHash, DecryptedHash.Hash, ARRAY_COUNT(FSHAHash::Hash), InKey); + + FSHAHash CurrentHash = ComputeCurrentMasterHash(); + if (DecryptedHash != CurrentHash) + { + FPakPlatformFile::GetPakMasterSignatureTableCheckFailureHandler().Broadcast(InFilename); + return false; + } + + return true; + } + + /** + * Helper function for computing the SHA1 hash of the current chunk CRC array + */ + FSHAHash ComputeCurrentMasterHash() const + { + FSHAHash CurrentHash; + FSHA1::HashBuffer(ChunkHashes.GetData(), ChunkHashes.Num() * sizeof(TPakChunkHash), CurrentHash.Hash); + return CurrentHash; + } +}; \ No newline at end of file diff --git a/Engine/Source/Runtime/Projects/Private/PluginManager.cpp b/Engine/Source/Runtime/Projects/Private/PluginManager.cpp index 9fb0c369f4ed..1438855a7a65 100644 --- a/Engine/Source/Runtime/Projects/Private/PluginManager.cpp +++ b/Engine/Source/Runtime/Projects/Private/PluginManager.cpp @@ -604,7 +604,8 @@ bool FPluginManager::ConfigureEnabledPlugins() for (const FString& ConfigFile : PluginConfigs) { FString PlaformName = FPlatformProperties::PlatformName(); - PluginConfigFilename = FString::Printf(TEXT("%s%s/%s.ini"), *FPaths::GeneratedConfigDir(), *PlaformName, *FPaths::GetBaseFilename(ConfigFile)); + // Use GetDestIniFilename to find the proper config file to combine into, since it manages command line overrides and path sanitization + PluginConfigFilename = FConfigCacheIni::GetDestIniFilename(*FPaths::GetBaseFilename(ConfigFile), *PlaformName, *FPaths::GeneratedConfigDir()); FConfigFile* FoundConfig = GConfig->Find(PluginConfigFilename, false); if (FoundConfig != nullptr) { diff --git a/Engine/Source/Runtime/RHI/Private/DynamicRHI.cpp b/Engine/Source/Runtime/RHI/Private/DynamicRHI.cpp index 5657951dc315..be4687c4dbbb 100644 --- a/Engine/Source/Runtime/RHI/Private/DynamicRHI.cpp +++ b/Engine/Source/Runtime/RHI/Private/DynamicRHI.cpp @@ -265,7 +265,7 @@ static void BaseRHISetGPUCaptureOptions(const TArray& Args, UWorld* Wor } else { - UE_LOG(LogRHI, Display, TEXT("Usage: r.PS4.EnableCaptureMode 0 or r.PS4.EnableCaptureMode 1")); + UE_LOG(LogRHI, Display, TEXT("Usage: r.RHISetGPUCaptureOptions 0 or r.RHISetGPUCaptureOptions 1")); } } diff --git a/Engine/Source/Runtime/RHI/Private/PipelineStateCache.cpp b/Engine/Source/Runtime/RHI/Private/PipelineStateCache.cpp index 97049192989a..5c7dfecc2e0d 100644 --- a/Engine/Source/Runtime/RHI/Private/PipelineStateCache.cpp +++ b/Engine/Source/Runtime/RHI/Private/PipelineStateCache.cpp @@ -582,7 +582,11 @@ FGraphicsPipelineCache GGraphicsPipelineCache; FAutoConsoleTaskPriority CPrio_FCompilePipelineStateTask( TEXT("TaskGraph.TaskPriorities.CompilePipelineStateTask"), TEXT("Task and thread priority for FCompilePipelineStateTask."), +#if PLATFORM_MAC + ENamedThreads::NormalThreadPriority, // On Mac, use normal thread priority to not freeze the loading screen for extended period of time +#else ENamedThreads::HighThreadPriority, // if we have high priority task threads, then use them... +#endif ENamedThreads::NormalTaskPriority, // .. at normal task priority ENamedThreads::HighTaskPriority, // if we don't have hi pri threads, then use normal priority threads at high task priority instead EPowerSavingEligibility::NotEligible // Not eligible for downgrade when power saving is requested. diff --git a/Engine/Source/Runtime/RHI/Public/RHIResources.h b/Engine/Source/Runtime/RHI/Public/RHIResources.h index e6c732013214..41bfd3dd097a 100644 --- a/Engine/Source/Runtime/RHI/Public/RHIResources.h +++ b/Engine/Source/Runtime/RHI/Public/RHIResources.h @@ -413,6 +413,13 @@ public: } const FRHIUniformBufferLayout& GetLayout() const { return *Layout; } + /** Same layout but different address */ + void UpdateLayoutReference(const FRHIUniformBufferLayout* NewRef) + { + check(*Layout == *NewRef); + Layout = NewRef; + } + #if VALIDATE_UNIFORM_BUFFER_LIFETIME mutable int32 NumMeshCommandReferencesForDebugging = 0; #endif diff --git a/Engine/Source/Runtime/RSA/Private/RSA.cpp b/Engine/Source/Runtime/RSA/Private/RSA.cpp new file mode 100644 index 000000000000..e44f6a8c6aa6 --- /dev/null +++ b/Engine/Source/Runtime/RSA/Private/RSA.cpp @@ -0,0 +1,410 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +#include "RSA.h" +#include "Misc/ScopeLock.h" +#include "Modules/ModuleManager.h" +#include "Math/BigInt.h" + +#ifndef RSA_USE_OPENSSL +#define RSA_USE_OPENSSL 0 +#endif + +#if RSA_USE_OPENSSL +#if PLATFORM_WINDOWS +#include "Windows/AllowWindowsPlatformTypes.h" +#endif + +#include +#include +#include + +#if PLATFORM_WINDOWS +#include "Windows/HideWindowsPlatformTypes.h" +#endif + +// Some platforms were upgraded to OpenSSL 1.1.1 while the others were left on a previous version. There are some minor differences we have to account for +// in the older version, so declare a handy define that we can use to gate the code +#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000L +#define USE_LEGACY_OPENSSL 1 +#else +#define USE_LEGACY_OPENSSL 0 +#endif + +struct FRSA::FKey +{ + FKey() + : KeySize(0) + , KeySizeInBytes(0) + , MaxDataSize(0) + , RSAKey(RSA_new()) + { + } + + ~FKey() + { + RSA_free(RSAKey); + } + + int32 GetKeySizeInBits() const + { + return KeySize; + } + + int32 GetKeySizeInBytes() const + { + return KeySizeInBytes; + } + + int32 GetMaxDataSize() const + { + return MaxDataSize; + } + + int32 KeySize; + int32 KeySizeInBytes; + int32 MaxDataSize; + RSA* RSAKey; +}; + +/** + * Wrapper function for extracting the bytes of an OpenSSL big num in a consistent form + */ +void LoadBinaryIntoBigNum(const uint8* InData, int64 InDataSize, BIGNUM* InBigNum) +{ +#if USE_LEGACY_OPENSSL + TArray Bytes(InData, InDataSize); + Algo::Reverse(Bytes); + BN_bin2bn(Bytes.GetData(), Bytes.Num(), InBigNum); +#else + BN_lebin2bn(InData, InDataSize, InBigNum); +#endif +} + +FRSA::TKeyPtr FRSA::CreateKey(const TArray& InPublicExponent, const TArray& InPrivateExponent, const TArray& InModulus) +{ + FRSA::TKeyPtr Key = MakeShared(); + BIGNUM* PublicExponent = InPublicExponent.Num() > 0 ? BN_new() : nullptr; + BIGNUM* PrivateExponent = InPrivateExponent.Num() > 0 ? BN_new() : nullptr; + BIGNUM* Modulus = BN_new(); + + if (InPublicExponent.Num()) + { + LoadBinaryIntoBigNum(InPublicExponent.GetData(), InPublicExponent.Num(), PublicExponent); + } + + if (InPrivateExponent.Num()) + { + LoadBinaryIntoBigNum(InPrivateExponent.GetData(), InPrivateExponent.Num(), PrivateExponent); + } + + LoadBinaryIntoBigNum(InModulus.GetData(), InModulus.Num(), Modulus); +#if USE_LEGACY_OPENSSL + Key->RSAKey->n = Modulus; + Key->RSAKey->e = PublicExponent; + Key->RSAKey->d = PrivateExponent; +#else + RSA_set0_key(Key->RSAKey, Modulus, PublicExponent, PrivateExponent); +#endif + + Key->KeySizeInBytes = RSA_size(Key->RSAKey); + Key->KeySize = Key->KeySizeInBytes * 8; + Key->MaxDataSize = Key->KeySizeInBytes - RSA_PKCS1_PADDING_SIZE; + return Key; +} + +bool FRSA::Encrypt(EKeyType InKeyType, const uint8* InSource, int32 InSourceSizeInBytes, TArray& OutDestination, FRSA::TKeyPtr InKey) +{ + OutDestination.SetNum(InKey->KeySizeInBytes); + + int NumEncryptedBytes = 0; + + switch (InKeyType) + { + case EKeyType::Public: + { + NumEncryptedBytes = RSA_public_encrypt(InSourceSizeInBytes, InSource, OutDestination.GetData(), InKey->RSAKey, RSA_PKCS1_PADDING); + break; + } + + case EKeyType::Private: + { + NumEncryptedBytes = RSA_private_encrypt(InSourceSizeInBytes, InSource, OutDestination.GetData(), InKey->RSAKey, RSA_PKCS1_PADDING); + break; + } + } + + if (NumEncryptedBytes == InKey->KeySizeInBytes) + { + return true; + } + else + { + OutDestination.Empty(); + return false; + } +} + +bool FRSA::Decrypt(EKeyType InKeyType, const TArray& InSource, uint8* OutDestination, int32 OutDestinationSizeInBytes, FRSA::TKeyPtr InKey) +{ + if (InSource.Num() == InKey->GetKeySizeInBytes() && OutDestinationSizeInBytes <= InKey->GetKeySizeInBytes()) + { + int NumDecryptedBytes = 0; + + switch (InKeyType) + { + case EKeyType::Public: + { + NumDecryptedBytes = RSA_public_decrypt(InSource.Num(), InSource.GetData(), OutDestination, InKey->RSAKey, RSA_PKCS1_PADDING); + break; + } + + case EKeyType::Private: + { + NumDecryptedBytes = RSA_private_decrypt(InSource.Num(), InSource.GetData(), OutDestination, InKey->RSAKey, RSA_PKCS1_PADDING); + break; + } + } + + if (NumDecryptedBytes >= 0 && NumDecryptedBytes <= OutDestinationSizeInBytes) + { + return true; + } + else + { + long Error = ERR_get_error(); + const char* Message = ERR_error_string(Error, nullptr); + return false; + } + } + + return false; +} + +#else // !RSA_USE_OPENSSL + +//#pragma message ("Using TBigInt for RSA") +#include "Math/RandomStream.h" + +struct FRSA::FKey +{ + virtual int32 GetKeySizeInBits() const = 0; + virtual int32 GetKeySizeInBytes() const = 0; + virtual int32 GetMaxDataSize() const = 0; + virtual bool Encrypt(FRSA::EKeyType InKeyType, const uint8* InSource, int32 InSourceSizeInBytes, TArray& OutDestination) const = 0; + virtual bool Decrypt(FRSA::EKeyType InKeyType, const TArray& InSource, uint8* OutDestination, int32 OutDestinationSizeInBytes) const = 0; +}; + +template +struct FFixedKey : public FRSA::FKey +{ + typedef TBigInt TInternalBigInt; + TInternalBigInt PublicExponent; + TInternalBigInt PrivateExponent; + TInternalBigInt Modulus; + + FFixedKey(const TArray& InPublicExponent, const TArray& InPrivateExponent, const TArray& InModulus) + { + if (InPublicExponent.Num()) + { + PublicExponent = TInternalBigInt(InPublicExponent.GetData(), InPublicExponent.Num()); + } + + if (InPrivateExponent.Num()) + { + PrivateExponent = TInternalBigInt(InPrivateExponent.GetData(), InPrivateExponent.Num()); + } + + if (InModulus.Num()) + { + Modulus = TInternalBigInt(InModulus.GetData(), InModulus.Num()); + } + } + + int32 GetKeySizeInBits() const override + { + return KEY_SIZE; + } + + int32 GetKeySizeInBytes() const override + { + return KEY_SIZE / 8; + } + + /** + * Return the maximum amount of data that can be encrypted within the key, as you would if you were using a proper + * RSA padding scheme. Because this is a legacy system and should be deprecated soon, we're just going to use random byte + * padding, but we'll reflect the correct RSA rules anyway + */ + int32 GetMaxDataSize() const override + { + return GetKeySizeInBytes() - 11; + } + + bool Encrypt(FRSA::EKeyType InKeyType, const uint8* InSource, int32 InSourceSizeInBytes, TArray& OutDestination) const override + { + if (InSourceSizeInBytes <= GetMaxDataSize()) + { + TInternalBigInt Source(InSource, InSourceSizeInBytes); + int32 PaddingBytesRequired = GetKeySizeInBytes() - InSourceSizeInBytes; + uint8* PaddingStart = (uint8*)Source.GetBits() + InSourceSizeInBytes; + // Not perfect, but an improvement on before, and soon to be defuncted anyway + for (int32 PaddingIndex = 0; PaddingIndex < PaddingBytesRequired; ++PaddingIndex) + { + PaddingStart[PaddingIndex] = (uint8)FMath::RandRange(0, 255); + } + PaddingStart[PaddingBytesRequired - 1] &= 0x3f; // keep top two bits clear because this seems to cause the decryption to fail. TODO: Find out why... presumably some bug in TBigInt + TInternalBigInt Result = FEncryption::ModularPow(Source, InKeyType == FRSA::EKeyType::Public ? PublicExponent : PrivateExponent, Modulus); + TInternalBigInt Test = FEncryption::ModularPow(Result, InKeyType == FRSA::EKeyType::Public ? PrivateExponent : PublicExponent, Modulus); + check(Source == Test); + OutDestination.SetNum(GetKeySizeInBytes()); + FMemory::Memcpy(OutDestination.GetData(), Result.GetBits(), GetKeySizeInBytes()); + } + else + { + OutDestination.Empty(); + } + + return true; + } + + bool Decrypt(FRSA::EKeyType InKeyType, const TArray& InSource, uint8* OutDestination, int32 OutDestinationSizeInBytes) const override + { + if (InSource.Num() == GetKeySizeInBytes() && OutDestinationSizeInBytes <= GetKeySizeInBytes()) + { + TInternalBigInt Source(InSource.GetData(), InSource.Num()); + TInternalBigInt Result = FEncryption::ModularPow(Source, InKeyType == FRSA::EKeyType::Public ? PublicExponent : PrivateExponent, Modulus); + FMemory::Memcpy(OutDestination, Result.GetBits(), OutDestinationSizeInBytes); + return true; + } + else + { + FMemory::Memset(OutDestination, 0, OutDestinationSizeInBytes); + return false; + } + } +}; + +int32 NumElementsIgnoringTrailingZeroes(const TArray& InBytes) +{ + int32 Count = InBytes.Num(); + + while (Count > 0 && InBytes[Count - 1] == 0) + { + Count--; + } + + return Count; +} + +FRSA::TKeyPtr FRSA::CreateKey(const TArray& InPublicExponent, const TArray& InPrivateExponent, const TArray& InModulus) +{ + // The key data generated by openSSL is little endian, which is great for the latest version of OpenSSL and for our TBigInt. The older + // version of OpenSSL still in use by Linux/Max doesn't have the same little-endian import functions so we have to swizzle the data + // in that case. OpenSSL should be upgraded on those platforms at some point, at which point we can remove this sub-optimal behavior + int32 NumPublicExponentBytes = NumElementsIgnoringTrailingZeroes(InPublicExponent); + int32 NumPrivateExponentBytes = NumElementsIgnoringTrailingZeroes(InPrivateExponent); + int32 NumModulusBytes = NumElementsIgnoringTrailingZeroes(InModulus); + + int32 RequiredNumBytes = FMath::Max(NumModulusBytes, FMath::Max(NumPublicExponentBytes, NumPrivateExponentBytes)); + RequiredNumBytes = FMath::RoundUpToPowerOfTwo(RequiredNumBytes); + int32 RequiredNumBits = RequiredNumBytes * 8; + + // With the legacy fixed key system, which is based on a templated large integer class, we can't be that + // dynamic with the key lengths. Eventually, we'll move over to the totally dynamic version, but this is still an + // expansion on the previous functionality + FRSA::TKeyPtr Result; + + switch (RequiredNumBits) + { + case 4096: Result = MakeShared, FRSA::KeyThreadMode>(InPublicExponent, InPrivateExponent, InModulus); break; + case 2048: Result = MakeShared, FRSA::KeyThreadMode>(InPublicExponent, InPrivateExponent, InModulus); break; + case 1024: Result = MakeShared, FRSA::KeyThreadMode>(InPublicExponent, InPrivateExponent, InModulus); break; + case 512: Result = MakeShared, FRSA::KeyThreadMode>(InPublicExponent, InPrivateExponent, InModulus); break; + case 256: Result = MakeShared, FRSA::KeyThreadMode>(InPublicExponent, InPrivateExponent, InModulus); break; + default: break; + } + + return Result; +} + +bool FRSA::Encrypt(FRSA::EKeyType InKeyType, const uint8* InSource, int32 InSourceSizeInBytes, TArray& OutDestination, FRSA::TKeyPtr InKey) +{ + return InKey->Encrypt(InKeyType, InSource, InSourceSizeInBytes, OutDestination); +} + +bool FRSA::Decrypt(FRSA::EKeyType InKeyType, const TArray& InSource, uint8* OutDestination, int32 OutDestinationSizeInBytes, FRSA::TKeyPtr InKey) +{ + return InKey->Decrypt(InKeyType, InSource, OutDestination, OutDestinationSizeInBytes); +} +#endif + +int32 FRSA::GetKeySizeInBits(FRSA::TKeyPtr InKey) +{ + return InKey->GetKeySizeInBits(); +} + +int32 FRSA::GetMaxDataSizeInBytes(FRSA::TKeyPtr InKey) +{ + return InKey->GetMaxDataSize(); +} + +bool FRSA::Encrypt(EKeyType InKeyType, const TArray& InSource, TArray& OutDestination, FRSA::TKeyPtr InKey) +{ + return Encrypt(InKeyType, InSource.GetData(), InSource.Num(), OutDestination, InKey); +} + +bool FRSA::Decrypt(EKeyType InKeyType, const TArray& InSource, TArray& OutDestination, FRSA::TKeyPtr InKey) +{ + OutDestination.SetNum(InKey->GetKeySizeInBytes()); + return Decrypt(InKeyType, InSource, OutDestination.GetData(), OutDestination.Num(), InKey); +} + +#if RSA_USE_OPENSSL && USE_LEGACY_OPENSSL +/** + * Module interface for RSA. + */ +class FRSAModule : public FDefaultModuleImpl +{ +public: + + virtual void StartupModule() override + { + // Provide a locking callback so that an RSA key can be used from multiple threads safely. + GetLocks().AddDefaulted(CRYPTO_num_locks()); + CRYPTO_set_locking_callback(LockingCallback); + } + + virtual void ShutdownModule() override + { + CRYPTO_set_locking_callback(nullptr); + + // Just in case + for (FCriticalSection& CriticalSection : GetLocks()) + { + CriticalSection.Unlock(); + } + } + + static TArray& GetLocks() + { + static TArray Locks; + return Locks; + } + + static void LockingCallback(int mode, int n, const char *file, int line) + { + TArray& Locks = GetLocks(); + check(Locks.Num() > 0); + + if (mode & CRYPTO_LOCK) + Locks[n].Lock(); + else + Locks[n].Unlock(); + } +}; + +IMPLEMENT_MODULE(FRSAModule, RSA); +#else +// OpenSSL 1.1.1+ handles thread safety of keys internally, so there is no need to provide any custom handling and we can just use a default module implementation +IMPLEMENT_MODULE(FDefaultModuleImpl, RSA); +#endif \ No newline at end of file diff --git a/Engine/Source/Runtime/RSA/Public/RSA.h b/Engine/Source/Runtime/RSA/Public/RSA.h new file mode 100644 index 000000000000..2a2a2864ea07 --- /dev/null +++ b/Engine/Source/Runtime/RSA/Public/RSA.h @@ -0,0 +1,49 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +#pragma once + +#include "CoreTypes.h" +#include "Templates/SharedPointer.h" + +struct RSA_API FRSA +{ + struct FKey; + static const ESPMode KeyThreadMode = ESPMode::ThreadSafe; + typedef TSharedPtr TKeyPtr; + + /** + * Enumerate which key to use when performing encrypt/decrpt operations + */ + enum class EKeyType + { + Public, + Private, + }; + + /** + * Create a new RSA public/private key from the supplied exponents and modulus binary data. Each of these arrays should contain a single little endian + * large integer value + */ + static TKeyPtr CreateKey(const TArray& InPublicExponent, const TArray& InPrivateExponent, const TArray& InModulus); + + /** + * Returns the size in bits of the supplied key + */ + static int32 GetKeySizeInBits(TKeyPtr InKey); + + /** + * Returns the maximum number of bytes that can be encrypted in a single payload + */ + static int32 GetMaxDataSizeInBytes(TKeyPtr InKey); + + /** + * Encrypt the supplied byte data using the given key + */ + static bool Encrypt(EKeyType InKeyType, const uint8* InSource, int32 InSourceSizeInBytes, TArray& OutDestination, TKeyPtr InKey); + static bool Encrypt(EKeyType InKeyType, const TArray& InSource, TArray& OutDestination, TKeyPtr InKey); + + /** + * Decrypt the supplied byte data using the given key + */ + static bool Decrypt(EKeyType InKeyType, const TArray& InSource, uint8* OutDestination, int32 OutDestinationSizeInBytes, TKeyPtr InKey); + static bool Decrypt(EKeyType InKeyType, const TArray& InSource, TArray& OutDestination, TKeyPtr InKey); +}; \ No newline at end of file diff --git a/Engine/Source/Runtime/RSA/RSA.Build.cs b/Engine/Source/Runtime/RSA/RSA.Build.cs new file mode 100644 index 000000000000..2ddfd70fb15a --- /dev/null +++ b/Engine/Source/Runtime/RSA/RSA.Build.cs @@ -0,0 +1,29 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class RSA : ModuleRules +{ + public RSA(ReadOnlyTargetRules Target) : base(Target) + { + PrivateDependencyModuleNames.Add("Core"); + + switch (Target.Platform) + { + case UnrealTargetPlatform.Win64: + case UnrealTargetPlatform.Win32: + case UnrealTargetPlatform.Mac: + case UnrealTargetPlatform.Linux: + { + PrivateDependencyModuleNames.Add("OpenSSL"); + PrivateDefinitions.Add("RSA_USE_OPENSSL=1"); + break; + } + default: + { + PrivateDefinitions.Add("RSA_USE_OPENSSL=0"); + break; + } + } + } +} diff --git a/Engine/Source/Runtime/RenderCore/Private/RenderTargetPool.cpp b/Engine/Source/Runtime/RenderCore/Private/RenderTargetPool.cpp index ffa4ecb0469e..0267515e7384 100644 --- a/Engine/Source/Runtime/RenderCore/Private/RenderTargetPool.cpp +++ b/Engine/Source/Runtime/RenderCore/Private/RenderTargetPool.cpp @@ -16,7 +16,7 @@ TGlobalResource GRenderTargetPool; DEFINE_LOG_CATEGORY_STATIC(LogRenderTargetPool, Warning, All); -static void DumpRenderTargetPoolMemory(FOutputDevice& OutputDevice) +RENDERCORE_API void DumpRenderTargetPoolMemory(FOutputDevice& OutputDevice) { GRenderTargetPool.DumpMemoryUsage(OutputDevice); } @@ -381,6 +381,10 @@ bool FRenderTargetPool::FindFreeElement(FRHICommandList& RHICmdList, const FPool for(uint32 i = 0, Num = (uint32)PooledRenderTargets.Num(); i < Num; ++i) { FPooledRenderTarget* Element = PooledRenderTargets[i]; + if(Element && Element->GetDesc().Compare(Desc, bExactMatch)) + { + int a = 0; + } if(Element && Element->IsFree() && Element->GetDesc().Compare(Desc, bExactMatch)) { @@ -1143,6 +1147,36 @@ void FRenderTargetPool::DumpMemoryUsage(FOutputDevice& OutputDevice) uint32 PoolKB=0; GetStats(NumTargets,PoolKB,UsedKB); OutputDevice.Logf(TEXT("%.3fMB total, %.3fMB used, %d render targets"), PoolKB / 1024.f, UsedKB / 1024.f, NumTargets); + + + uint32 DeferredTotal = 0; + OutputDevice.Logf(TEXT("Deferred Render Targets:")); + for (int32 i = 0; i < DeferredDeleteArray.Num(); ++i) + { + FPooledRenderTarget* Element = DeferredDeleteArray[i]; + + if (Element) + { + check(!Element->IsSnapshot()); + OutputDevice.Logf( + TEXT(" %6.3fMB %4dx%4d%s%s %2dmip(s) %s (%s) %s %s"), + ComputeSizeInKB(*Element) / 1024.0f, + Element->Desc.Extent.X, + Element->Desc.Extent.Y, + Element->Desc.Depth > 1 ? *FString::Printf(TEXT("x%3d"), Element->Desc.Depth) : (Element->Desc.IsCubemap() ? TEXT("cube") : TEXT(" ")), + Element->Desc.bIsArray ? *FString::Printf(TEXT("[%3d]"), Element->Desc.ArraySize) : TEXT(" "), + Element->Desc.NumMips, + Element->Desc.DebugName, + GPixelFormats[Element->Desc.Format].Name, + Element->IsTransient() ? TEXT("(transient)") : TEXT(""), + GSupportsTransientResourceAliasing ? *FString::Printf(TEXT("Frames since last discard: %d"), GFrameNumberRenderThread - Element->FrameNumberLastDiscard) : TEXT("") + ); + uint32 SizeInKB = ComputeSizeInKB(*Element); + DeferredTotal += SizeInKB; + } + } + OutputDevice.Logf(TEXT("%.3fMB Deferred total"), DeferredTotal / 1024.f); + } uint32 FPooledRenderTarget::AddRef() const diff --git a/Engine/Source/Runtime/RenderCore/Private/RenderUtils.cpp b/Engine/Source/Runtime/RenderCore/Private/RenderUtils.cpp index 4554d87be467..713d6ff3fc83 100644 --- a/Engine/Source/Runtime/RenderCore/Private/RenderUtils.cpp +++ b/Engine/Source/Runtime/RenderCore/Private/RenderUtils.cpp @@ -1040,7 +1040,7 @@ RENDERCORE_API FIndexBufferRHIRef& GetUnitCubeIndexBuffer() RENDERCORE_API void QuantizeSceneBufferSize(const FIntPoint& InBufferSize, FIntPoint& OutBufferSize) { // Ensure sizes are dividable by the ideal group size for 2d tiles to make it more convenient. - const uint32 DividableBy = FComputeShaderUtils::kGolden2DGroupSize; + const uint32 DividableBy = 4; check(DividableBy % 4 == 0); // A lot of graphic algorithms where previously assuming DividableBy == 4. diff --git a/Engine/Source/Runtime/RenderCore/Private/Shader.cpp b/Engine/Source/Runtime/RenderCore/Private/Shader.cpp index a76bccd70bbe..a46c53688cdd 100644 --- a/Engine/Source/Runtime/RenderCore/Private/Shader.cpp +++ b/Engine/Source/Runtime/RenderCore/Private/Shader.cpp @@ -18,6 +18,7 @@ #include "Misc/ConfigCacheIni.h" #include "UObject/RenderingObjectVersion.h" #include "UObject/FortniteMainBranchObjectVersion.h" +#include "Misc/ScopeLock.h" #if WITH_EDITORONLY_DATA #include "Interfaces/IShaderFormat.h" @@ -976,7 +977,7 @@ void FShaderResource::ReleaseRHI() DEC_DWORD_STAT_BY(STAT_Shaders_NumShadersUsedForRendering, 1); #if RHI_RAYTRACING - if (IsInitialized() && Target.Frequency == SF_RayHitGroup) + if (IsInitialized() && RayTracingMaterialLibraryIndex != UINT_MAX) { RemoveFromRayTracingLibrary(RayTracingMaterialLibraryIndex); RayTracingMaterialLibraryIndex = UINT_MAX; diff --git a/Engine/Source/Runtime/RenderCore/Private/ShaderCodeLibrary.cpp b/Engine/Source/Runtime/RenderCore/Private/ShaderCodeLibrary.cpp index 481a32a70029..3b6e6018709e 100644 --- a/Engine/Source/Runtime/RenderCore/Private/ShaderCodeLibrary.cpp +++ b/Engine/Source/Runtime/RenderCore/Private/ShaderCodeLibrary.cpp @@ -20,6 +20,7 @@ ShaderCodeLibrary.cpp: Bound shader state cache implementation. #include "Interfaces/IShaderFormatArchive.h" #include "ShaderPipelineCache.h" #include "Misc/FileHelper.h" +#include "Misc/ConfigCacheIni.h" #if WITH_EDITORONLY_DATA #include "Modules/ModuleManager.h" @@ -1918,7 +1919,10 @@ void FShaderCodeLibrary::InitForRuntime(EShaderPlatform ShaderPlatform) } // Cannot be enabled by the server, pointless if we can't ever render and not compatible with cook-on-the-fly - bool bEnable = !FPlatformProperties::IsServerOnly() && FApp::CanEverRender(); + bool bArchive = false; + GConfig->GetBool(TEXT("/Script/UnrealEd.ProjectPackagingSettings"), TEXT("bShareMaterialShaderCode"), bArchive, GGameIni); + + bool bEnable = !FPlatformProperties::IsServerOnly() && FApp::CanEverRender() && bArchive; #if !UE_BUILD_SHIPPING FString FileHostIP; const bool bCookOnTheFly = FParse::Value(FCommandLine::Get(), TEXT("filehostip"), FileHostIP); @@ -1946,6 +1950,9 @@ void FShaderCodeLibrary::InitForRuntime(EShaderPlatform ShaderPlatform) } else { +#if !WITH_EDITOR + UE_LOG(LogShaderLibrary, Fatal, TEXT("Failed to initialise ShaderCodeLibrary required by the project because part of the Global shader library is missing from %s."), *FPaths::ProjectContentDir()); +#endif Shutdown(); } } diff --git a/Engine/Source/Runtime/RenderCore/Public/RenderGraphBuilder.h b/Engine/Source/Runtime/RenderCore/Public/RenderGraphBuilder.h index 8d45ac48331e..96bc897658cc 100644 --- a/Engine/Source/Runtime/RenderCore/Public/RenderGraphBuilder.h +++ b/Engine/Source/Runtime/RenderCore/Public/RenderGraphBuilder.h @@ -353,7 +353,7 @@ public: return OutParameterPtr; } - /** Adds a lambda pass to the graph. + /** Adds a hard coded lambda pass to the graph. * * The Name of the pass should be generated with enough information to identify it's purpose and GPU cost, to be clear * for GPU profiling tools. @@ -402,6 +402,31 @@ public: #endif } + /** Adds a procedurally created pass to the render graph. + * + * Note: You want to use this only when the layout of the pass might be procedurally generated from data driven, as opose to AddPass() that have, + * constant hard coded pass layout. + * + * Caution: You are on your own to have correct memory lifetime of the FRenderGraphPass. + */ + void AddProcedurallyCreatedPass(FRenderGraphPass* NewPass) + { + #if RENDER_GRAPH_DEBUGGING + { + checkf(!bHasExecuted, TEXT("Render graph pass %s needs to be added before the builder execution."), NewPass->GetName()); + } + #endif + + // TODO(RDG): perhaps the CurrentScope could be set here instead of GetCurrentScope(), to not allow user code to start adding pass to random scopes. + Passes.Emplace(NewPass); + + #if RENDER_GRAPH_DEBUGGING || SUPPORTS_VISUALIZE_TEXTURE + { + DebugPass(NewPass); + } + #endif + } + /** Queue a texture extraction. This will set *OutTexturePtr with the internal pooled render target at the Execute(). * * Note: even when the render graph uses the immediate debugging mode (executing passes as they get added), the texture extrations @@ -451,6 +476,11 @@ public: */ void Execute(); + /** Returns the draw event scope, where passes are currently being added in. */ + const FRDGEventScope* GetCurrentScope() const + { + return CurrentScope; + } public: /** The RHI command list used for the render graph. */ diff --git a/Engine/Source/Runtime/RenderCore/Public/RenderGraphResources.h b/Engine/Source/Runtime/RenderCore/Public/RenderGraphResources.h index 372a7104e3cf..10fb72db33f2 100644 --- a/Engine/Source/Runtime/RenderCore/Public/RenderGraphResources.h +++ b/Engine/Source/Runtime/RenderCore/Public/RenderGraphResources.h @@ -66,6 +66,9 @@ public: FRDGResource(const FRDGResource&) = delete; void operator = (const FRDGResource&) = delete; + /** Boolean to track at runtime whether a ressource is actually used by the lambda of a pass or not, to detect unnecessary resource dependencies on passes. */ + mutable bool bIsActuallyUsedByPass = false; + private: /** Number of references in passes and deferred queries. */ mutable int32 ReferenceCount = 0; @@ -74,9 +77,6 @@ private: mutable bool bWritable = false; mutable bool bCompute = false; - /** Boolean to track at runtime whether a ressource is actually used by the lambda of a pass or not, to detect unnecessary resource dependencies on passes. */ - mutable bool bIsActuallyUsedByPass = false; - #if RENDER_GRAPH_DEBUGGING /** Boolean to track at wiring time if a resource has ever been produced by a pass, to error out early if accessing a resource that has not been produced. */ mutable bool bHasEverBeenProduced = false; @@ -122,13 +122,20 @@ public: /** Descriptor of the graph tracked texture. */ const FPooledRenderTargetDesc Desc; - /** Returns the allocated pooled render target. */ + /** Returns the allocated pooled render target. Must only be called within a pass's lambda. */ inline IPooledRenderTarget* GetPooledRenderTarget() const { check(PooledRenderTarget); return PooledRenderTarget; } + /** Returns the allocated RHI texture. Must only be called within a pass's lambda. */ + inline FTextureRHIParamRef GetRHITexture() const + { + check(PooledRenderTarget); + return PooledRenderTarget->GetRenderTargetItem().ShaderResourceTexture; + } + private: /** This is not a TRefCountPtr<> because FRDGTexture is allocated on the FMemStack * FGraphBuilder::AllocatedTextures is actually keeping the reference. @@ -140,13 +147,6 @@ private: , Desc(InDesc) { } - /** Returns the allocated RHI texture. */ - inline FTextureRHIParamRef GetRHITexture() const - { - check(PooledRenderTarget); - return PooledRenderTarget->GetRenderTargetItem().ShaderResourceTexture; - } - friend class FRDGBuilder; template diff --git a/Engine/Source/Runtime/RenderCore/Public/Shader.h b/Engine/Source/Runtime/RenderCore/Public/Shader.h index 03bf354b8cce..ccf3325a489b 100644 --- a/Engine/Source/Runtime/RenderCore/Public/Shader.h +++ b/Engine/Source/Runtime/RenderCore/Public/Shader.h @@ -2192,13 +2192,7 @@ public: check(Type); checkSlow(FName(Type->GetName()) != NAME_None); Ar << Type; -#if WITH_EDITOR - TRefCountPtr* Found = Shaders.Find(Key); - checkf(Found, TEXT("Unable to find FShaderType %s!"), Type->GetName()); - FShader* CurrentShader = *Found; -#else FShader* CurrentShader = Shaders.FindChecked(Key); -#endif SerializeShaderForSaving(CurrentShader, Ar, bHandleShaderKeyChanges, bInlineShaderResource); } diff --git a/Engine/Source/Runtime/Renderer/Private/DeferredShadingRenderer.h b/Engine/Source/Runtime/Renderer/Private/DeferredShadingRenderer.h index ae2122536d6c..fdf5e7741cde 100644 --- a/Engine/Source/Runtime/Renderer/Private/DeferredShadingRenderer.h +++ b/Engine/Source/Runtime/Renderer/Private/DeferredShadingRenderer.h @@ -445,6 +445,7 @@ private: const FViewInfo& View, FRDGTextureRef* OutColorTexture, FRDGTextureRef* OutRayHitDistanceTexture, + FRDGTextureRef* OutRayImaginaryDepthTexture, int32 SamplePerPixel, int32 HeightFog, float ResolutionFraction); diff --git a/Engine/Source/Runtime/Renderer/Private/DistanceFieldAmbientOcclusion.cpp b/Engine/Source/Runtime/Renderer/Private/DistanceFieldAmbientOcclusion.cpp index 7bd559a825ec..deffaf2d495a 100644 --- a/Engine/Source/Runtime/Renderer/Private/DistanceFieldAmbientOcclusion.cpp +++ b/Engine/Source/Runtime/Renderer/Private/DistanceFieldAmbientOcclusion.cpp @@ -722,7 +722,7 @@ bool ShouldRenderDeferredDynamicSkyLight(const FScene* Scene, const FSceneViewFa { return Scene->SkyLight && Scene->SkyLight->ProcessedTexture - && !ShouldRenderRayTracingSkyLight(Scene->SkyLight) + && !ShouldRenderRayTracingSkyLight(Scene->SkyLight) // Disable diffuse sky contribution if evaluated by RT Sky. && !Scene->SkyLight->bWantsStaticShadowing && !Scene->SkyLight->bHasStaticLighting && ViewFamily.EngineShowFlags.SkyLighting diff --git a/Engine/Source/Runtime/Renderer/Private/MobileBasePass.cpp b/Engine/Source/Runtime/Renderer/Private/MobileBasePass.cpp index 89e034f15d63..3083787b4d27 100644 --- a/Engine/Source/Runtime/Renderer/Private/MobileBasePass.cpp +++ b/Engine/Source/Runtime/Renderer/Private/MobileBasePass.cpp @@ -618,7 +618,7 @@ void FMobileBasePassMeshProcessor::Process( FBaseDS, TMobileBasePassPSPolicyParamType> BasePassShaders; - bool bEnableSkyLight = ShadingModel != MSM_Unlit && Scene->ShouldRenderSkylightInBasePass(BlendMode); + bool bEnableSkyLight = ShadingModel != MSM_Unlit && Scene && Scene->ShouldRenderSkylightInBasePass(BlendMode); int32 NumMovablePointLights = MobileBasePass::CalcNumMovablePointLights(MaterialResource, PrimitiveSceneProxy); MobileBasePass::GetShaders( @@ -649,7 +649,7 @@ void FMobileBasePassMeshProcessor::Process( else { // Background primitives will be rendered last in masked/non-masked buckets - bool bBackground = PrimitiveSceneProxy->TreatAsBackgroundForOcclusion(); + bool bBackground = PrimitiveSceneProxy ? PrimitiveSceneProxy->TreatAsBackgroundForOcclusion() : false; // Default static sort key separates masked and non-masked geometry, generic mesh sorting will also sort by PSO // if platform wants front to back sorting, this key will be recomputed in InitViews SortKey = GetBasePassStaticSortKey(BlendMode, bBackground); diff --git a/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessEyeAdaptation.cpp b/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessEyeAdaptation.cpp index 9d98b04b9748..c1778e4202a5 100644 --- a/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessEyeAdaptation.cpp +++ b/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessEyeAdaptation.cpp @@ -470,6 +470,9 @@ void FSceneViewState::UpdatePreExposure(FViewInfo& View) bUpdateLastExposure = true; } } + + // Update the pre-exposure value on the actual view + View.PreExposure = PreExposure; } FPooledRenderTargetDesc FRCPassPostProcessEyeAdaptation::ComputeOutputDesc(EPassOutputId InPassOutputId) const diff --git a/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessMaterial.cpp b/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessMaterial.cpp index 5551b5675a29..f1cc2368e416 100644 --- a/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessMaterial.cpp +++ b/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessMaterial.cpp @@ -197,6 +197,13 @@ FRCPassPostProcessMaterial::FRCPassPostProcessMaterial(UMaterialInterface* InMat { MaterialInterface = UMaterial::GetDefaultMaterial(MD_PostProcess); } + + if (Material && (Material->IsStencilTestEnabled() || Material->GetBlendableOutputAlpha())) + { + // Only allowed to have blend/stencil test if output format is compatible with ePId_Input0. + // PF_Unknown implies output format is that of EPId_Input0 + ensure(OutputFormat == PF_Unknown); + } } /** The filter vertex declaration resource type. */ @@ -224,7 +231,7 @@ public: TGlobalResource GPostProcessMaterialVertexDeclaration; template -FShader* SetMobileShaders(const FMaterialShaderMap* MaterialShaderMap, FGraphicsPipelineStateInitializer &GraphicsPSOInit, FRenderingCompositePassContext &Context, FMaterialRenderProxy* Proxy, uint32 StencilRefValue) +FShader* SetMobileShaders(const FMaterialShaderMap* MaterialShaderMap, FGraphicsPipelineStateInitializer &GraphicsPSOInit, FRenderingCompositePassContext &Context, FMaterialRenderProxy* Proxy) { TPixelShader* PixelShader_Mobile = MaterialShaderMap->GetShader(); FPostProcessMaterialVS_Mobile* VertexShader_Mobile = MaterialShaderMap->GetShader(); @@ -234,7 +241,6 @@ FShader* SetMobileShaders(const FMaterialShaderMap* MaterialShaderMap, FGraphics GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(PixelShader_Mobile); SetGraphicsPipelineState(Context.RHICmdList, GraphicsPSOInit); - Context.RHICmdList.SetStencilRef(StencilRefValue); VertexShader_Mobile->SetParameters(Context.RHICmdList, Context, Proxy); PixelShader_Mobile->SetParameters(Context.RHICmdList, Context, Proxy); @@ -268,12 +274,12 @@ void FRCPassPostProcessMaterial::Process(FRenderingCompositePassContext& Context bool AllowStencilTestWithCopy = AllowStencilTest == 2; // do the stencil test if - // >= SM5 + // not SM4 // OR // reads DS but allowed make DS copy // OR // DS not read at all. - bDoStencilTest = (FeatureLevel >= ERHIFeatureLevel::SM5) || + bDoStencilTest = (FeatureLevel != ERHIFeatureLevel::SM4) || ((bReadsCustomDepthStencil == AllowStencilTestWithCopy) || AllowStencilTestWithCopy); } else @@ -290,8 +296,6 @@ void FRCPassPostProcessMaterial::Process(FRenderingCompositePassContext& Context // Copy of custom depth/stencil buffer if HW does not support simultaneously a texture bound as DepthRead_StencilRead and SRV TRefCountPtr CustomDepthStencilCopy; - // The PP target - either from the render target pool or the ePId_Input0 - const FSceneRenderTargetItem* DestRenderTarget = nullptr; const FSceneRenderTargetItem* CustomDepthStencilTarget = nullptr; FDepthStencilStateRHIParamRef DepthStencilState; @@ -302,7 +306,7 @@ void FRCPassPostProcessMaterial::Process(FRenderingCompositePassContext& Context CustomDepthStencilTarget = &SceneContext.CustomDepth->GetRenderTargetItem(); // SM4 HW lacks support for texture bound as DepthRead_StencilRead and SRV simultaneously thus make a copy of DS buffer - if (FeatureLevel < ERHIFeatureLevel::SM5 && bReadsCustomDepthStencil) + if (FeatureLevel == ERHIFeatureLevel::SM4 && bReadsCustomDepthStencil) { // Dest param of CopyResource() call can only be an SRV (No render target flags) on DX10.0 (SM4) FPooledRenderTargetDesc DSCopyDesc = SceneContext.CustomDepth->GetDesc(); @@ -323,27 +327,6 @@ void FRCPassPostProcessMaterial::Process(FRenderingCompositePassContext& Context Swap(SceneContext.CustomDepth, CustomDepthStencilCopy); } - // essentially only care about format and dimensions - FPooledRenderTargetDesc TargetDesc = PassOutputs[0].RenderTargetDesc; - TargetDesc.AutoWritable = InputDesc->AutoWritable; - - // write directly into PPI0 if material does not read from it, otherwise make a copy of PPI0. - if (InputDesc->Compare(TargetDesc, false) && !MaterialShaderMap->UsesSceneTexture(PPI_PostProcessInput0)) - { - PassOutputs[0].PooledRenderTarget = GetInput(ePId_Input0)->GetOutput()->RequestInput(); - DestRenderTarget = &PassOutputs[0].RequestSurface(Context); - } - else - { - DestRenderTarget = &PassOutputs[0].RequestSurface(Context); - - Context.RHICmdList.CopyTexture( - GetInput(ePId_Input0)->GetOutput()->RequestSurface(Context).ShaderResourceTexture, - DestRenderTarget->TargetableTexture, - FRHICopyTextureInfo() - ); - } - static const FDepthStencilStateRHIParamRef StencilStates[] = { TStaticDepthStencilState::GetRHI(), @@ -362,12 +345,12 @@ void FRCPassPostProcessMaterial::Process(FRenderingCompositePassContext& Context } else { - DestRenderTarget = &PassOutputs[0].RequestSurface(Context); DepthStencilState = TStaticDepthStencilState::GetRHI(); } FBlendStateRHIParamRef BlendState = TStaticBlendState<>::GetRHI(); - if (Material->GetBlendableOutputAlpha() && CVarPostProcessAllowBlendModes.GetValueOnRenderThread() != 0) + bool bDoOutputBlend = Material->GetBlendableOutputAlpha() && CVarPostProcessAllowBlendModes.GetValueOnRenderThread() != 0; + if (bDoOutputBlend) { static const FBlendStateRHIParamRef BlendStates[] = { @@ -383,6 +366,36 @@ void FRCPassPostProcessMaterial::Process(FRenderingCompositePassContext& Context BlendState = BlendStates[Material->GetBlendMode()]; } + // The PP target - either from the render target pool or the ePId_Input0 + const FSceneRenderTargetItem* DestRenderTarget = nullptr; + ERenderTargetLoadAction DestRenderTargetLoadAction = ERenderTargetLoadAction::Num; + + if (bDoStencilTest || bDoOutputBlend) + { + if (!MaterialShaderMap->UsesSceneTexture(PPI_PostProcessInput0)) + { + PassOutputs[0].PooledRenderTarget = GetInput(ePId_Input0)->GetOutput()->RequestInput(); + DestRenderTarget = &PassOutputs[0].RequestSurface(Context); + } + else + { + DestRenderTarget = &PassOutputs[0].RequestSurface(Context); + + Context.RHICmdList.CopyTexture( + GetInput(ePId_Input0)->GetOutput()->RequestSurface(Context).ShaderResourceTexture, + DestRenderTarget->TargetableTexture, + FRHICopyTextureInfo() + ); + } + + DestRenderTargetLoadAction = ERenderTargetLoadAction::ELoad; + } + else + { + DestRenderTarget = &PassOutputs[0].RequestSurface(Context); + DestRenderTargetLoadAction = Context.GetLoadActionForRenderTarget(*DestRenderTarget); + } + FIntRect SrcRect = Context.SceneColorViewRect; FIntRect DestRect = Context.GetSceneColorDestRect(*DestRenderTarget); checkf(DestRect.Size() == SrcRect.Size(), TEXT("Post process material should not be used as upscaling pass.")); @@ -396,7 +409,7 @@ void FRCPassPostProcessMaterial::Process(FRenderingCompositePassContext& Context { RPInfo = FRHIRenderPassInfo( DestRenderTarget->TargetableTexture, - MakeRenderTargetActions(Context.GetLoadActionForRenderTarget(*DestRenderTarget), ERenderTargetStoreAction::EStore), + MakeRenderTargetActions(DestRenderTargetLoadAction, ERenderTargetStoreAction::EStore), CustomDepthStencilTarget->TargetableTexture, MakeDepthStencilTargetActions( MakeRenderTargetActions(ERenderTargetLoadAction::ENoAction, ERenderTargetStoreAction::ENoAction), @@ -451,12 +464,13 @@ void FRCPassPostProcessMaterial::Process(FRenderingCompositePassContext& Context // use mobile's post process material. if (bViewSizeMatchesBufferSize) { - VertexShader = SetMobileShaders< FPostProcessMaterialPS_Mobile0>(MaterialShaderMap, GraphicsPSOInit, Context, Proxy, StencilRefValue); + VertexShader = SetMobileShaders< FPostProcessMaterialPS_Mobile0>(MaterialShaderMap, GraphicsPSOInit, Context, Proxy); } else { - VertexShader = SetMobileShaders< FPostProcessMaterialPS_Mobile1>(MaterialShaderMap, GraphicsPSOInit, Context, Proxy, StencilRefValue); + VertexShader = SetMobileShaders< FPostProcessMaterialPS_Mobile1>(MaterialShaderMap, GraphicsPSOInit, Context, Proxy); } + Context.RHICmdList.SetStencilRef(StencilRefValue); } // Uses highend post process material that assumed ViewSize == BufferSize. else if (bViewSizeMatchesBufferSize) diff --git a/Engine/Source/Runtime/Renderer/Private/PostProcess/SceneRenderTargets.cpp b/Engine/Source/Runtime/Renderer/Private/PostProcess/SceneRenderTargets.cpp index 26ca96dec36b..3b690c21b5ae 100644 --- a/Engine/Source/Runtime/Renderer/Private/PostProcess/SceneRenderTargets.cpp +++ b/Engine/Source/Runtime/Renderer/Private/PostProcess/SceneRenderTargets.cpp @@ -304,31 +304,44 @@ inline const TCHAR* GetSceneColorTargetName(EShadingPath ShadingPath) #if PREVENT_RENDERTARGET_SIZE_THRASHING -static inline void UpdateHistoryFlags(uint8& Flags, bool bIsSceneCapture, bool bIsReflectionCapture) +namespace ERenderTargetHistory { - Flags |= bIsSceneCapture ? 1 : 0; - Flags |= bIsReflectionCapture ? (1 << 1) : 0; + enum Type + { + RTH_SceneCapture = 0x1, + RTH_ReflectionCapture = 0x2, + RTH_HighresScreenshot = 0x4, + RTH_MaskAll = 0x7, + }; +} + +static inline void UpdateHistoryFlags(uint8& Flags, bool bIsSceneCapture, bool bIsReflectionCapture, bool bIsHighResScreenShot) +{ + Flags |= bIsSceneCapture ? ERenderTargetHistory::RTH_SceneCapture : 0; + Flags |= bIsReflectionCapture ? ERenderTargetHistory::RTH_ReflectionCapture : 0; + Flags |= bIsHighResScreenShot ? ERenderTargetHistory::RTH_HighresScreenshot : 0; } template -static bool AnyCaptureRenderedRecently(const uint8* HitoryFlags) +static bool AnyCaptureRenderedRecently(const uint8* HistoryFlags, uint8 Mask) { uint8 Result = 0; for (uint32 Idx = 0; Idx < NumEntries; ++Idx) { - Result |= HitoryFlags[Idx]; + Result |= (HistoryFlags[Idx] & Mask); } return Result != 0; } -#define UPDATE_HISTORY_FLAGS(Flags, bIsSceneCapture, bIsReflectionCapture) UpdateHistoryFlags(Flags, bIsSceneCapture, bIsReflectionCapture) -#define ANY_CAPTURE_RENDERED_RECENTLY(HistoryFlags, NumEntries) AnyCaptureRenderedRecently(HistoryFlags) +#define UPDATE_HISTORY_FLAGS(Flags, bIsSceneCapture, bIsReflectionCapture, bIsHighResScreenShot) UpdateHistoryFlags(Flags, bIsSceneCapture, bIsReflectionCapture, bIsHighResScreenShot) +#define ANY_CAPTURE_RENDERED_RECENTLY(HistoryFlags, NumEntries) AnyCaptureRenderedRecently(HistoryFlags, ERenderTargetHistory::RTH_MaskAll) +#define ANY_HIGHRES_CAPTURE_RENDERED_RECENTLY(HistoryFlags, NumEntries) AnyCaptureRenderedRecently(HistoryFlags, ERenderTargetHistory::RTH_HighresScreenshot) #else -#define UPDATE_HISTORY_FLAGS(Flags, bIsSceneCapture, bIsReflectionCapture) +#define UPDATE_HISTORY_FLAGS(Flags, bIsSceneCapture, bIsReflectionCapture, bIsHighResScreenShot) #define ANY_CAPTURE_RENDERED_RECENTLY(HistoryFlags, NumEntries) (false) - +#define ANY_HIGHRES_CAPTURE_RENDERED_RECENTLY(HistoryFlags, NumEntries) (false) #endif FIntPoint FSceneRenderTargets::ComputeDesiredSize(const FSceneViewFamily& ViewFamily) @@ -405,7 +418,8 @@ FIntPoint FSceneRenderTargets::ComputeDesiredSize(const FSceneViewFamily& ViewFa // this allows The BufferSize to not grow below the SceneCapture requests (happen before scene rendering, in the same frame with a Grow request) FIntPoint& LargestDesiredSizeThisFrame = LargestDesiredSizes[CurrentDesiredSizeIndex]; LargestDesiredSizeThisFrame = LargestDesiredSizeThisFrame.ComponentMax(DesiredBufferSize); - UPDATE_HISTORY_FLAGS(HistoryFlags[CurrentDesiredSizeIndex], bIsSceneCapture, bIsReflectionCapture); + bool bIsHighResScreenshot = GIsHighResScreenshot; + UPDATE_HISTORY_FLAGS(HistoryFlags[CurrentDesiredSizeIndex], bIsSceneCapture, bIsReflectionCapture, bIsHighResScreenshot); // we want to shrink the buffer but as we can have multiple scenecaptures per frame we have to delay that a frame to get all size requests // Don't save buffer size in history while making high-res screenshot. @@ -441,6 +455,11 @@ FIntPoint FSceneRenderTargets::ComputeDesiredSize(const FSceneViewFamily& ViewFa } } } + const bool bAnyHighresScreenshotRecently = ANY_HIGHRES_CAPTURE_RENDERED_RECENTLY(HistoryFlags, FrameSizeHistoryCount); + if(bAnyHighresScreenshotRecently != GIsHighResScreenshot) + { + bAllowDelayResize = false; + } if(bAllowDelayResize) { diff --git a/Engine/Source/Runtime/Renderer/Private/PrimitiveSceneInfo.cpp b/Engine/Source/Runtime/Renderer/Private/PrimitiveSceneInfo.cpp index f90459502402..65d3dcf4eb58 100644 --- a/Engine/Source/Runtime/Renderer/Private/PrimitiveSceneInfo.cpp +++ b/Engine/Source/Runtime/Renderer/Private/PrimitiveSceneInfo.cpp @@ -126,6 +126,7 @@ FPrimitiveSceneInfo::FPrimitiveSceneInfo(UPrimitiveComponent* InComponent,FScene PackedIndex(INDEX_NONE), ComponentForDebuggingOnly(InComponent), bNeedsStaticMeshUpdate(false), + bNeedsStaticMeshUpdateWithoutVisibilityCheck(false), bNeedsUniformBufferUpdate(false), bIndirectLightingCacheBufferDirty(false), LightmapDataOffset(INDEX_NONE), @@ -628,15 +629,22 @@ void FPrimitiveSceneInfo::RemoveFromScene(bool bUpdateStaticDrawLists) DEC_MEMORY_STAT_BY(STAT_PrimitiveInfoMemory, sizeof(*this) + StaticMeshes.GetAllocatedSize() + StaticMeshRelevances.GetAllocatedSize() + Proxy->GetMemoryFootprint()); - if (bNeedsStaticMeshUpdate) - { - Scene->PrimitivesNeedingStaticMeshUpdate.Remove(this); - - bNeedsStaticMeshUpdate = false; - } - if (bUpdateStaticDrawLists) { + if (bNeedsStaticMeshUpdate) + { + Scene->PrimitivesNeedingStaticMeshUpdate.Remove(this); + + bNeedsStaticMeshUpdate = false; + } + + if (bNeedsStaticMeshUpdateWithoutVisibilityCheck) + { + Scene->PrimitivesNeedingStaticMeshUpdateWithoutVisibilityCheck.Remove(this); + + bNeedsStaticMeshUpdateWithoutVisibilityCheck = false; + } + // IndirectLightingCacheUniformBuffer may be cached inside cached mesh draw commands, so we // can't delete it unless we also update cached mesh command. IndirectLightingCacheUniformBuffer.SafeRelease(); @@ -663,6 +671,13 @@ void FPrimitiveSceneInfo::UpdateStaticMeshes(FRHICommandListImmediate& RHICmdLis } } + if (!bNeedsStaticMeshUpdate && bNeedsStaticMeshUpdateWithoutVisibilityCheck) + { + Scene->PrimitivesNeedingStaticMeshUpdateWithoutVisibilityCheck.Remove(this); + + bNeedsStaticMeshUpdateWithoutVisibilityCheck = false; + } + RemoveCachedMeshDrawCommands(); if (bReAddToDrawLists) { @@ -688,6 +703,16 @@ void FPrimitiveSceneInfo::BeginDeferredUpdateStaticMeshes() } } +void FPrimitiveSceneInfo::BeginDeferredUpdateStaticMeshesWithoutVisibilityCheck() +{ + if (bNeedsStaticMeshUpdate && !bNeedsStaticMeshUpdateWithoutVisibilityCheck) + { + bNeedsStaticMeshUpdateWithoutVisibilityCheck = true; + + Scene->PrimitivesNeedingStaticMeshUpdateWithoutVisibilityCheck.Add(this); + } +} + void FPrimitiveSceneInfo::LinkLODParentComponent() { if (LODParentComponentId.IsValid()) diff --git a/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingLighting.cpp b/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingLighting.cpp index 74c5fbb0558d..bb4796b77625 100644 --- a/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingLighting.cpp +++ b/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingLighting.cpp @@ -6,14 +6,15 @@ #if RHI_RAYTRACING -IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FRaytracingLightData, "RaytracingLightsData"); +IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FRaytracingLightDataPacked, "RaytracingLightsDataPacked"); -void SetupRaytracingLightData( +void SetupRaytracingLightDataPacked( const TSparseArray& Lights, const FViewInfo& View, - FRaytracingLightData* LightData) + FRaytracingLightDataPacked* LightData) { TMap IESLightProfilesMap; + TMap RectTextureMap; LightData->Count = 0; LightData->LTCMatTexture = GSystemTextures.LTCMat->GetRenderTargetItem().ShaderResourceTexture; @@ -30,8 +31,8 @@ void SetupRaytracingLightData( LightData->RectLightTexture5 = DymmyWhiteTexture; LightData->RectLightTexture6 = DymmyWhiteTexture; LightData->RectLightTexture7 = DymmyWhiteTexture; - const uint32 RectLightTextureSlotCount = 8; - uint32 CurrentRectLightIndex = 0; + static constexpr uint32 MaxRectLightTextureSlos = 8; + static constexpr uint32 InvalidTextureIndex = 99; // #dxr_todo: share this definition with ray tracing shaders for (auto Light : Lights) { @@ -65,28 +66,53 @@ void SetupRaytracingLightData( } } - LightData->Type[LightData->Count] = Light.LightType; - LightData->LightPosition[LightData->Count] = LightParameters.Position; - LightData->LightInvRadius[LightData->Count] = LightParameters.InvRadius; - LightData->LightColor[LightData->Count] = LightParameters.Color; - LightData->LightFalloffExponent[LightData->Count] = LightParameters.FalloffExponent; - LightData->Direction[LightData->Count] = LightParameters.Direction; - LightData->Tangent[LightData->Count] = LightParameters.Tangent; - LightData->SpotAngles[LightData->Count] = LightParameters.SpotAngles; - LightData->SpecularScale[LightData->Count] = LightParameters.SpecularScale; - LightData->SourceRadius[LightData->Count] = LightParameters.SourceRadius; - LightData->SourceLength[LightData->Count] = LightParameters.SourceLength; - LightData->SoftSourceRadius[LightData->Count] = LightParameters.SoftSourceRadius; - LightData->LightProfileIndex[LightData->Count] = IESLightProfileIndex; - LightData->RectLightTextureIndex[LightData->Count] = 99; - LightData->RectLightBarnCosAngle[LightData->Count] = LightParameters.RectLightBarnCosAngle; - LightData->RectLightBarnLength[LightData->Count] = LightParameters.RectLightBarnLength; + LightData->Type_LightProfileIndex_RectLightTextureIndex[LightData->Count].X = Light.LightType; + LightData->Type_LightProfileIndex_RectLightTextureIndex[LightData->Count].Y = IESLightProfileIndex; + LightData->Type_LightProfileIndex_RectLightTextureIndex[LightData->Count].Z = InvalidTextureIndex; - const bool bAllocateRectTextureSlot = Light.LightType == ELightComponentType::LightType_Rect && LightParameters.SourceTexture && CurrentRectLightIndex < RectLightTextureSlotCount; - if (bAllocateRectTextureSlot) + LightData->LightPosition_InvRadius[LightData->Count] = LightParameters.Position; + LightData->LightPosition_InvRadius[LightData->Count].W = LightParameters.InvRadius; + + LightData->LightColor_SpecularScale[LightData->Count] = LightParameters.Color; + LightData->LightColor_SpecularScale[LightData->Count].W = LightParameters.SpecularScale; + + LightData->Direction_FalloffExponent[LightData->Count] = LightParameters.Direction; + LightData->Direction_FalloffExponent[LightData->Count].W = LightParameters.FalloffExponent; + + LightData->Tangent_SourceRadius[LightData->Count] = LightParameters.Tangent; + LightData->Tangent_SourceRadius[LightData->Count].W = LightParameters.SourceRadius; + + FVector4 SpotAngles_SourceLength_SoftSourceRadius = FVector4(LightParameters.SpotAngles, FVector2D(LightParameters.SourceLength, LightParameters.SoftSourceRadius)); + LightData->SpotAngles_SourceLength_SoftSourceRadius[LightData->Count] = SpotAngles_SourceLength_SoftSourceRadius; + + const FVector2D FadeParams = Light.LightSceneInfo->Proxy->GetDirectionalLightDistanceFadeParameters(View.GetFeatureLevel(), Light.LightSceneInfo->IsPrecomputedLightingValid(), View.MaxShadowCascades); + + FVector4 DistanceFadeMAD_RectLightBarnCosAngle_RectLightBarnLength = FVector4(FadeParams.Y, -FadeParams.X * FadeParams.Y, LightParameters.RectLightBarnCosAngle, LightParameters.RectLightBarnLength); + LightData->DistanceFadeMAD_RectLightBarnCosAngle_RectLightBarnLength[LightData->Count] = DistanceFadeMAD_RectLightBarnCosAngle_RectLightBarnLength; + + const bool bRequireTexture = Light.LightType == ELightComponentType::LightType_Rect && LightParameters.SourceTexture; + uint32 RectLightTextureIndex = InvalidTextureIndex; + if (bRequireTexture) { - LightData->RectLightTextureIndex[LightData->Count] = CurrentRectLightIndex; - switch (CurrentRectLightIndex) + const uint32* IndexFound = RectTextureMap.Find(LightParameters.SourceTexture); + if (!IndexFound) + { + if (RectTextureMap.Num() < MaxRectLightTextureSlos) + { + RectLightTextureIndex = RectTextureMap.Num(); + RectTextureMap.Add(LightParameters.SourceTexture, RectLightTextureIndex); + } + } + else + { + RectLightTextureIndex = *IndexFound; + } + } + + if (RectLightTextureIndex != InvalidTextureIndex) + { + LightData->Type_LightProfileIndex_RectLightTextureIndex[LightData->Count].Z = RectLightTextureIndex; + switch (RectLightTextureIndex) { case 0: LightData->RectLightTexture0 = LightParameters.SourceTexture; break; case 1: LightData->RectLightTexture1 = LightParameters.SourceTexture; break; @@ -97,13 +123,8 @@ void SetupRaytracingLightData( case 6: LightData->RectLightTexture6 = LightParameters.SourceTexture; break; case 7: LightData->RectLightTexture7 = LightParameters.SourceTexture; break; } - ++CurrentRectLightIndex; - } - const FVector2D FadeParams = Light.LightSceneInfo->Proxy->GetDirectionalLightDistanceFadeParameters(View.GetFeatureLevel(), Light.LightSceneInfo->IsPrecomputedLightingValid(), View.MaxShadowCascades); - LightData->DistanceFadeMAD[LightData->Count] = FVector2D(FadeParams.Y, -FadeParams.X * FadeParams.Y); - LightData->Count++; if (LightData->Count >= GRaytracingLightCountMaximum) break; @@ -124,10 +145,10 @@ void SetupRaytracingLightData( } } -TUniformBufferRef CreateLightDataUniformBuffer(const TSparseArray& Lights, const class FViewInfo& View, EUniformBufferUsage Usage) +TUniformBufferRef CreateLightDataPackedUniformBuffer(const TSparseArray& Lights, const class FViewInfo& View, EUniformBufferUsage Usage) { - FRaytracingLightData LightData; - SetupRaytracingLightData(Lights, View, &LightData); + FRaytracingLightDataPacked LightData; + SetupRaytracingLightDataPacked(Lights, View, &LightData); return CreateUniformBufferImmediate(LightData, Usage); } diff --git a/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingLighting.h b/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingLighting.h index 36e01b007fea..cfd6040be5c7 100644 --- a/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingLighting.h +++ b/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingLighting.h @@ -12,25 +12,15 @@ const static uint32 GRaytracingLightCountMaximum = 64; -BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FRaytracingLightData, ) +BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FRaytracingLightDataPacked, ) SHADER_PARAMETER(uint32, Count) - SHADER_PARAMETER_ARRAY(uint32, Type, [GRaytracingLightCountMaximum]) - SHADER_PARAMETER_ARRAY(FVector, LightPosition, [GRaytracingLightCountMaximum]) - SHADER_PARAMETER_ARRAY(float, LightInvRadius, [GRaytracingLightCountMaximum]) - SHADER_PARAMETER_ARRAY(FVector, LightColor, [GRaytracingLightCountMaximum]) - SHADER_PARAMETER_ARRAY(float, LightFalloffExponent, [GRaytracingLightCountMaximum]) - SHADER_PARAMETER_ARRAY(FVector, Direction, [GRaytracingLightCountMaximum]) - SHADER_PARAMETER_ARRAY(FVector, Tangent, [GRaytracingLightCountMaximum]) - SHADER_PARAMETER_ARRAY(FVector2D, SpotAngles, [GRaytracingLightCountMaximum]) - SHADER_PARAMETER_ARRAY(float, SpecularScale, [GRaytracingLightCountMaximum]) - SHADER_PARAMETER_ARRAY(float, SourceRadius, [GRaytracingLightCountMaximum]) - SHADER_PARAMETER_ARRAY(float, SourceLength, [GRaytracingLightCountMaximum]) - SHADER_PARAMETER_ARRAY(float, SoftSourceRadius, [GRaytracingLightCountMaximum]) - SHADER_PARAMETER_ARRAY(uint32, LightProfileIndex, [GRaytracingLightCountMaximum]) - SHADER_PARAMETER_ARRAY(FVector2D, DistanceFadeMAD, [GRaytracingLightCountMaximum]) - SHADER_PARAMETER_ARRAY(uint32, RectLightTextureIndex, [GRaytracingLightCountMaximum]) - SHADER_PARAMETER_ARRAY(float, RectLightBarnCosAngle, [GRaytracingLightCountMaximum]) - SHADER_PARAMETER_ARRAY(float, RectLightBarnLength, [GRaytracingLightCountMaximum]) + SHADER_PARAMETER_ARRAY(FIntVector, Type_LightProfileIndex_RectLightTextureIndex, [GRaytracingLightCountMaximum]) + SHADER_PARAMETER_ARRAY(FVector4, LightPosition_InvRadius, [GRaytracingLightCountMaximum]) + SHADER_PARAMETER_ARRAY(FVector4, LightColor_SpecularScale, [GRaytracingLightCountMaximum]) + SHADER_PARAMETER_ARRAY(FVector4, Direction_FalloffExponent, [GRaytracingLightCountMaximum]) + SHADER_PARAMETER_ARRAY(FVector4, Tangent_SourceRadius, [GRaytracingLightCountMaximum]) + SHADER_PARAMETER_ARRAY(FVector4, SpotAngles_SourceLength_SoftSourceRadius, [GRaytracingLightCountMaximum]) + SHADER_PARAMETER_ARRAY(FVector4, DistanceFadeMAD_RectLightBarnCosAngle_RectLightBarnLength, [GRaytracingLightCountMaximum]) SHADER_PARAMETER_TEXTURE(Texture2D, LTCMatTexture) SHADER_PARAMETER_SAMPLER(SamplerState, LTCMatSampler) SHADER_PARAMETER_TEXTURE(Texture2D, LTCAmpTexture) @@ -45,11 +35,11 @@ BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FRaytracingLightData, ) SHADER_PARAMETER_TEXTURE(Texture2D, RectLightTexture7) END_GLOBAL_SHADER_PARAMETER_STRUCT() -void SetupRaytracingLightData( +void SetupRaytracingLightDataPacked( const TSparseArray& Lights, const FViewInfo& View, - FRaytracingLightData* LightData); + FRaytracingLightDataPacked* LightData); -TUniformBufferRef CreateLightDataUniformBuffer(const TSparseArray& Lights, const class FViewInfo& View, EUniformBufferUsage Usage); +TUniformBufferRef CreateLightDataPackedUniformBuffer(const TSparseArray& Lights, const class FViewInfo& View, EUniformBufferUsage Usage); #endif \ No newline at end of file diff --git a/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingRectLight.cpp b/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingRectLight.cpp index f602c668dd11..0094ad72463b 100644 --- a/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingRectLight.cpp +++ b/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingRectLight.cpp @@ -74,6 +74,139 @@ DECLARE_GPU_STAT_NAMED(RayTracingRectLight, TEXT("Ray Tracing RectLight")); IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FRectLightData, "RectLight"); + + +class FBuildRectLightMipTreeCS : public FGlobalShader +{ + DECLARE_SHADER_TYPE(FBuildRectLightMipTreeCS, Global) + +public: + static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) + { + return ShouldCompileRayTracingShadersForProject(Parameters.Platform); + } + + static uint32 GetGroupSize() + { + return 16; + } + + static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) + { + FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); + OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), GetGroupSize()); + } + + FBuildRectLightMipTreeCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) + : FGlobalShader(Initializer) + { + TextureParameter.Bind(Initializer.ParameterMap, TEXT("RectLightTexture")); + TextureSamplerParameter.Bind(Initializer.ParameterMap, TEXT("TextureSampler")); + DimensionsParameter.Bind(Initializer.ParameterMap, TEXT("Dimensions")); + MipLevelParameter.Bind(Initializer.ParameterMap, TEXT("MipLevel")); + MipTreeParameter.Bind(Initializer.ParameterMap, TEXT("MipTree")); + } + + FBuildRectLightMipTreeCS() {} + + void SetParameters( + FRHICommandList& RHICmdList, + FTextureRHIRef Texture, + const FIntVector& Dimensions, + uint32 MipLevel, + FRWBuffer& MipTree + ) + { + FComputeShaderRHIParamRef ShaderRHI = GetComputeShader(); + + SetShaderValue(RHICmdList, ShaderRHI, DimensionsParameter, Dimensions); + SetShaderValue(RHICmdList, ShaderRHI, MipLevelParameter, MipLevel); + SetTextureParameter(RHICmdList, ShaderRHI, TextureParameter, TextureSamplerParameter, TStaticSamplerState::GetRHI(), Texture); + + check(MipTreeParameter.IsBound()); + MipTreeParameter.SetBuffer(RHICmdList, ShaderRHI, MipTree); + } + + void UnsetParameters( + FRHICommandList& RHICmdList, + EResourceTransitionAccess TransitionAccess, + EResourceTransitionPipeline TransitionPipeline, + FRWBuffer& MipTree, + FComputeFenceRHIParamRef Fence) + { + FComputeShaderRHIParamRef ShaderRHI = GetComputeShader(); + + MipTreeParameter.UnsetUAV(RHICmdList, ShaderRHI); + RHICmdList.TransitionResource(TransitionAccess, TransitionPipeline, MipTree.UAV, Fence); + } + + virtual bool Serialize(FArchive& Ar) + { + bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); + Ar << TextureParameter; + Ar << TextureSamplerParameter; + Ar << DimensionsParameter; + Ar << MipLevelParameter; + Ar << MipTreeParameter; + return bShaderHasOutdatedParameters; + } + +private: + FShaderResourceParameter TextureParameter; + FShaderResourceParameter TextureSamplerParameter; + + FShaderParameter DimensionsParameter; + FShaderParameter MipLevelParameter; + FRWShaderParameter MipTreeParameter; +}; + +IMPLEMENT_SHADER_TYPE(, FBuildRectLightMipTreeCS, TEXT("/Engine/Private/Raytracing/BuildMipTreeCS.usf"), TEXT("BuildRectLightMipTreeCS"), SF_Compute) + +DECLARE_GPU_STAT_NAMED(BuildRectLightMipTreeStat, TEXT("build RectLight MipTree")); + +FRectLightRayTracingData BuildRectLightMipTree(FRHICommandListImmediate& RHICmdList, UTexture* SourceTexture) +{ + SCOPED_GPU_STAT(RHICmdList, BuildRectLightMipTreeStat); + + check(IsInRenderingThread()); + FRectLightRayTracingData Data; + FTextureRHIRef RhiTexture = SourceTexture ? SourceTexture->Resource->TextureRHI : GWhiteTexture->TextureRHI; + + const auto ShaderMap = GetGlobalShaderMap(ERHIFeatureLevel::SM5); + TShaderMapRef BuildRectLightMipTreeComputeShader(ShaderMap); + RHICmdList.SetComputeShader(BuildRectLightMipTreeComputeShader->GetComputeShader()); + + // Allocate MIP tree + FIntVector TextureSize = RhiTexture->GetSizeXYZ(); + uint32 MipLevelCount = FMath::Min(FMath::CeilLogTwo(TextureSize.X), FMath::CeilLogTwo(TextureSize.Y)); + Data.RectLightMipTreeDimensions = FIntVector(1 << MipLevelCount, 1 << MipLevelCount, 1); + uint32 NumElements = Data.RectLightMipTreeDimensions.X * Data.RectLightMipTreeDimensions.Y; + for (uint32 MipLevel = 1; MipLevel <= MipLevelCount; ++MipLevel) + { + uint32 NumElementsInLevel = (Data.RectLightMipTreeDimensions.X >> MipLevel) * (Data.RectLightMipTreeDimensions.Y >> MipLevel); + NumElements += NumElementsInLevel; + } + + Data.RectLightMipTree.Initialize(sizeof(float), NumElements, PF_R32_FLOAT, BUF_UnorderedAccess | BUF_ShaderResource); + + // Execute hierarchical build + for (uint32 MipLevel = 0; MipLevel <= MipLevelCount; ++MipLevel) + { + FComputeFenceRHIRef MipLevelFence = RHICmdList.CreateComputeFence(TEXT("RectLightMipTree Build")); + BuildRectLightMipTreeComputeShader->SetParameters(RHICmdList, RhiTexture, Data.RectLightMipTreeDimensions, MipLevel, Data.RectLightMipTree); + FIntVector MipLevelDimensions = FIntVector(Data.RectLightMipTreeDimensions.X >> MipLevel, Data.RectLightMipTreeDimensions.Y >> MipLevel, 1); + FIntVector NumGroups = FIntVector::DivideAndRoundUp(MipLevelDimensions, FBuildRectLightMipTreeCS::GetGroupSize()); + DispatchComputeShader(RHICmdList, *BuildRectLightMipTreeComputeShader, NumGroups.X, NumGroups.Y, 1); + BuildRectLightMipTreeComputeShader->UnsetParameters(RHICmdList, EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EComputeToCompute, Data.RectLightMipTree, MipLevelFence); + } + FComputeFenceRHIRef TransitionFence = RHICmdList.CreateComputeFence(TEXT("RectLightMipTree Transition")); + BuildRectLightMipTreeComputeShader->UnsetParameters(RHICmdList, EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EComputeToCompute, Data.RectLightMipTree, TransitionFence); + + return Data; +} + + + template class FRectLightRGS : public FGlobalShader { @@ -336,6 +469,21 @@ void RenderRayTracingRectLightInternal( check(RectLightSceneInfo.Proxy->IsRectLight()); FRectLightSceneProxy* RectLightSceneProxy = (FRectLightSceneProxy*)RectLightSceneInfo.Proxy; + check(RectLightSceneProxy->RayTracingData); + if( !RectLightSceneProxy->RayTracingData->bInitialised // Test needed in case GRayTracingStochasticRectLight is turned on in editor, + || RectLightSceneProxy->SourceTexture && RectLightSceneProxy->SourceTexture->GetLightingGuid() != RectLightSceneProxy->RayTracingData->TextureLightingGuid) + { + // We ignore TextureImportanceSampling and RectLightSceneProxy->HasSourceTexture() because uniform buffer expect a resource. + // So we always update. + // dxr-todo: cache texture RayTracingData render side based on GUID in a database (render thread safe and avoid duplicating the work for each light using the same texture). + *RectLightSceneProxy->RayTracingData = BuildRectLightMipTree(RHICmdList, RectLightSceneProxy->SourceTexture); + RectLightSceneProxy->RayTracingData->bInitialised = true; + if (RectLightSceneProxy->SourceTexture) + { + RectLightSceneProxy->RayTracingData->TextureLightingGuid = RectLightSceneProxy->SourceTexture->GetLightingGuid(); + } + } + FLightShaderParameters LightShaderParameters; RectLightSceneProxy->GetLightShaderParameters(LightShaderParameters); @@ -359,8 +507,8 @@ void RenderRayTracingRectLightInternal( RectLightData.Height = 2.0f * LightShaderParameters.SourceLength; RectLightData.Texture = LightShaderParameters.SourceTexture; RectLightData.TextureSampler = RHICreateSamplerState(FSamplerStateInitializerRHI(SF_Bilinear, AM_Border, AM_Border, AM_Border)); - RectLightData.MipTree = RectLightSceneProxy->RectLightMipTree.SRV; - RectLightData.MipTreeDimensions = RectLightSceneProxy->RectLightMipTreeDimensions; + RectLightData.MipTree = RectLightSceneProxy->RayTracingData->RectLightMipTree.SRV; + RectLightData.MipTreeDimensions = RectLightSceneProxy->RayTracingData->RectLightMipTreeDimensions; RectLightData.MaxNormalBias = GetRaytracingMaxNormalBias(); RectLightData.BarnCosAngle = FMath::Cos(FMath::DegreesToRadians(RectLightSceneProxy->BarnDoorAngle)); RectLightData.BarnLength = RectLightSceneProxy->BarnDoorLength; diff --git a/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingReflections.cpp b/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingReflections.cpp index 6bd651778a01..f85ef8416dcd 100644 --- a/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingReflections.cpp +++ b/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingReflections.cpp @@ -135,7 +135,7 @@ class FRayTracingReflectionsRGS : public FGlobalShader SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_STRUCT_REF(FSceneTexturesUniformParameters, SceneTexturesStruct) - SHADER_PARAMETER_STRUCT_REF(FRaytracingLightData, LightData) + SHADER_PARAMETER_STRUCT_REF(FRaytracingLightDataPacked, LightDataPacked) SHADER_PARAMETER_STRUCT_REF(FReflectionUniformParameters, ReflectionStruct) SHADER_PARAMETER_STRUCT_REF(FFogUniformParameters, FogUniformParameters) SHADER_PARAMETER_STRUCT_REF(FIESLightProfileParameters, IESLightProfileParameters) @@ -145,6 +145,7 @@ class FRayTracingReflectionsRGS : public FGlobalShader SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, ColorOutput) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RayHitDistanceOutput) + SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RayImaginaryDepthOutput) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) @@ -206,6 +207,7 @@ void FDeferredShadingSceneRenderer::RenderRayTracingReflections( const FViewInfo& View, FRDGTextureRef* OutColorTexture, FRDGTextureRef* OutRayHitDistanceTexture, + FRDGTextureRef* OutRayImaginaryDepthTexture, int32 SamplePerPixel, int32 HeightFog, float ResolutionFraction) @@ -232,6 +234,7 @@ void FDeferredShadingSceneRenderer::RenderRayTracingReflections( Desc.Format = PF_R16F; *OutRayHitDistanceTexture = GraphBuilder.CreateTexture(Desc, TEXT("RayTracingReflectionsHitDistance")); + *OutRayImaginaryDepthTexture = GraphBuilder.CreateTexture(Desc, TEXT("RayTracingReflectionsImaginaryDepth")); } // When deferred materials are used, we need to dispatch the reflection shader twice: @@ -273,13 +276,14 @@ void FDeferredShadingSceneRenderer::RenderRayTracingReflections( CommonParameters.TLAS = View.RayTracingScene.RayTracingSceneRHI->GetShaderResourceView(); CommonParameters.ViewUniformBuffer = View.ViewUniformBuffer; - CommonParameters.LightData = CreateLightDataUniformBuffer(Scene->Lights, View, EUniformBufferUsage::UniformBuffer_SingleFrame); + CommonParameters.LightDataPacked = CreateLightDataPackedUniformBuffer(Scene->Lights, View, EUniformBufferUsage::UniformBuffer_SingleFrame); CommonParameters.SceneTexturesStruct = CreateSceneTextureUniformBuffer( SceneContext, FeatureLevel, ESceneTextureSetupMode::All, EUniformBufferUsage::UniformBuffer_SingleFrame); CommonParameters.ReflectionStruct = CreateReflectionUniformBuffer(View, EUniformBufferUsage::UniformBuffer_SingleFrame); CommonParameters.FogUniformParameters = CreateFogUniformBuffer(View, EUniformBufferUsage::UniformBuffer_SingleFrame); CommonParameters.IESLightProfileParameters = CreateIESLightProfilesUniformBuffer(View, EUniformBufferUsage::UniformBuffer_SingleFrame); CommonParameters.ColorOutput = GraphBuilder.CreateUAV(*OutColorTexture); CommonParameters.RayHitDistanceOutput = GraphBuilder.CreateUAV(*OutRayHitDistanceTexture); + CommonParameters.RayImaginaryDepthOutput = GraphBuilder.CreateUAV(*OutRayImaginaryDepthTexture); CommonParameters.SortTileSize = SortTileSize; for (uint32 PassIndex = 0; PassIndex < NumPasses; ++PassIndex) diff --git a/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingTranslucency.cpp b/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingTranslucency.cpp index d110df620726..057f1abb4fa6 100644 --- a/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingTranslucency.cpp +++ b/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingTranslucency.cpp @@ -121,7 +121,7 @@ class FRayTracingTranslucencyRGS : public FGlobalShader SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_STRUCT_REF(FSceneTexturesUniformParameters, SceneTexturesStruct) - SHADER_PARAMETER_STRUCT_REF(FRaytracingLightData, LightData) + SHADER_PARAMETER_STRUCT_REF(FRaytracingLightDataPacked, LightDataPacked) SHADER_PARAMETER_STRUCT_REF(FReflectionUniformParameters, ReflectionStruct) SHADER_PARAMETER_STRUCT_REF(FFogUniformParameters, FogUniformParameters) SHADER_PARAMETER_STRUCT_REF(FIESLightProfileParameters, IESLightProfileParameters) @@ -369,7 +369,7 @@ void FDeferredShadingSceneRenderer::RenderRayTracingTranslucencyView( PassParameters->TLAS = View.RayTracingScene.RayTracingSceneRHI->GetShaderResourceView(); PassParameters->ViewUniformBuffer = View.ViewUniformBuffer; - PassParameters->LightData = CreateLightDataUniformBuffer(Scene->Lights, View, EUniformBufferUsage::UniformBuffer_SingleFrame); + PassParameters->LightDataPacked = CreateLightDataPackedUniformBuffer(Scene->Lights, View, EUniformBufferUsage::UniformBuffer_SingleFrame); PassParameters->SceneTexturesStruct = CreateSceneTextureUniformBuffer(SceneContext, FeatureLevel, ESceneTextureSetupMode::All, EUniformBufferUsage::UniformBuffer_SingleFrame); PassParameters->ReflectionStruct = CreateReflectionUniformBuffer(View, EUniformBufferUsage::UniformBuffer_SingleFrame); PassParameters->FogUniformParameters = CreateFogUniformBuffer(View, EUniformBufferUsage::UniformBuffer_SingleFrame); diff --git a/Engine/Source/Runtime/Renderer/Private/ReflectionEnvironment.cpp b/Engine/Source/Runtime/Renderer/Private/ReflectionEnvironment.cpp index 3ab8f29f9f96..58fa3da4644b 100644 --- a/Engine/Source/Runtime/Renderer/Private/ReflectionEnvironment.cpp +++ b/Engine/Source/Runtime/Renderer/Private/ReflectionEnvironment.cpp @@ -519,7 +519,7 @@ class FReflectionEnvironmentSkyLightingPS : public FGlobalShader class FSkyLight : SHADER_PERMUTATION_BOOL("ENABLE_SKY_LIGHT"); class FDynamicSkyLight : SHADER_PERMUTATION_BOOL("ENABLE_DYNAMIC_SKY_LIGHT"); class FSkyShadowing : SHADER_PERMUTATION_BOOL("APPLY_SKY_SHADOWING"); - class FClearCoat : SHADER_PERMUTATION_BOOL("ENABLE_CLEAR_COAT"); + class FRayTracedReflections : SHADER_PERMUTATION_BOOL("RAY_TRACED_REFLECTIONS"); using FPermutationDomain = TShaderPermutationDomain< FHasBoxCaptures, @@ -529,7 +529,7 @@ class FReflectionEnvironmentSkyLightingPS : public FGlobalShader FSkyLight, FDynamicSkyLight, FSkyShadowing, - FClearCoat>; + FRayTracedReflections>; static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector) { @@ -557,7 +557,7 @@ class FReflectionEnvironmentSkyLightingPS : public FGlobalShader return PermutationVector; } - static FPermutationDomain BuildPermutationVector(const FViewInfo& View, bool bBoxCapturesOnly, bool bSphereCapturesOnly, bool bSupportDFAOIndirectOcclusion, bool bSpecularBounce, bool bEnableSkyLight, bool bEnableDynamicSkyLight, bool bApplySkyShadowing, bool bEnableClearCoat) + static FPermutationDomain BuildPermutationVector(const FViewInfo& View, bool bBoxCapturesOnly, bool bSphereCapturesOnly, bool bSupportDFAOIndirectOcclusion, bool bSpecularBounce, bool bEnableSkyLight, bool bEnableDynamicSkyLight, bool bApplySkyShadowing, bool bRayTracedReflections) { FPermutationDomain PermutationVector; @@ -568,7 +568,7 @@ class FReflectionEnvironmentSkyLightingPS : public FGlobalShader PermutationVector.Set(bEnableSkyLight); PermutationVector.Set(bEnableDynamicSkyLight); PermutationVector.Set(bApplySkyShadowing); - PermutationVector.Set(bEnableClearCoat); + PermutationVector.Set(bRayTracedReflections); return RemapPermutation(PermutationVector); } @@ -726,10 +726,10 @@ void FDeferredShadingSceneRenderer::RenderDeferredReflectionsAndSkyLighting(FRHI const bool bRayTracedReflections = IsRayTracingEnabled() && (GRayTracingReflections < 0 ? bAnyViewWithRaytracingReflections : GRayTracingReflections); + // The specular sky light contribution is also needed by RT Reflections as a fallback. const bool bSkyLight = Scene->SkyLight && Scene->SkyLight->ProcessedTexture - && !Scene->SkyLight->bHasStaticLighting - && !ShouldRenderRayTracingSkyLight(Scene->SkyLight); + && !Scene->SkyLight->bHasStaticLighting; bool bDynamicSkyLight = ShouldRenderDeferredDynamicSkyLight(Scene, ViewFamily); bool bApplySkyShadowing = false; @@ -791,7 +791,7 @@ void FDeferredShadingSceneRenderer::RenderDeferredReflectionsAndSkyLighting(FRHI IScreenSpaceDenoiser::FReflectionsInputs DenoiserInputs; RenderRayTracingReflections( GraphBuilder, - View, &DenoiserInputs.Color, &DenoiserInputs.RayHitDistance, + View, &DenoiserInputs.Color, &DenoiserInputs.RayHitDistance, &DenoiserInputs.RayImaginaryDepth, RayTracingReflectionsSPP, GRayTracingReflectionsHeightFog, RayTracingConfig.ResolutionFraction); @@ -855,7 +855,7 @@ void FDeferredShadingSceneRenderer::RenderDeferredReflectionsAndSkyLighting(FRHI TShaderMapRef VertexShader(View.ShaderMap); - auto PermutationVector = FReflectionEnvironmentSkyLightingPS::BuildPermutationVector(View, bHasBoxCaptures, bHasSphereCaptures, DynamicBentNormalAO != NULL, bReflectionCapture, bSkyLight, bDynamicSkyLight, bApplySkyShadowing, !bRayTracedReflections); + auto PermutationVector = FReflectionEnvironmentSkyLightingPS::BuildPermutationVector(View, bHasBoxCaptures, bHasSphereCaptures, DynamicBentNormalAO != NULL, bReflectionCapture, bSkyLight, bDynamicSkyLight, bApplySkyShadowing, bRayTracedReflections); TShaderMapRef PixelShader(View.ShaderMap, PermutationVector); diff --git a/Engine/Source/Runtime/Renderer/Private/RendererScene.cpp b/Engine/Source/Runtime/Renderer/Private/RendererScene.cpp index 5a948b07aae1..c348cd93c1c6 100644 --- a/Engine/Source/Runtime/Renderer/Private/RendererScene.cpp +++ b/Engine/Source/Runtime/Renderer/Private/RendererScene.cpp @@ -3160,16 +3160,36 @@ void FScene::ApplyWorldOffset(FVector InOffset) }); } -void FScene::ApplyWorldOffset_RenderThread(FVector InOffset) +void FScene::ApplyWorldOffset_RenderThread(const FVector& InOffset) { QUICK_SCOPE_CYCLE_COUNTER(STAT_SceneApplyWorldOffset); + GPUScene.bUpdateAllPrimitives = true; + // Primitives - for (auto It = Primitives.CreateIterator(); It; ++It) + for (int32 Idx = 0; Idx < Primitives.Num(); ++Idx) { - (*It)->ApplyWorldOffset(InOffset); + Primitives[Idx]->ApplyWorldOffset(InOffset); + } + + // Primitive transforms + for (int32 Idx = 0; Idx < PrimitiveTransforms.Num(); ++Idx) + { + PrimitiveTransforms[Idx].SetOrigin(PrimitiveTransforms[Idx].GetOrigin() + InOffset); } + // Primitive bounds + for (int32 Idx = 0; Idx < PrimitiveBounds.Num(); ++Idx) + { + PrimitiveBounds[Idx].BoxSphereBounds.Origin+= InOffset; + } + + // Primitive occlusion bounds + for (int32 Idx = 0; Idx < PrimitiveOcclusionBounds.Num(); ++Idx) + { + PrimitiveOcclusionBounds[Idx].Origin+= InOffset; + } + // Precomputed light volumes for (const FPrecomputedLightVolume* It : PrecomputedLightVolumes) { @@ -3188,18 +3208,6 @@ void FScene::ApplyWorldOffset_RenderThread(FVector InOffset) // Primitives octree PrimitiveOctree.ApplyOffset(InOffset, /*bGlobalOctee*/ true); - // Primitive bounds - for (auto It = PrimitiveBounds.CreateIterator(); It; ++It) - { - (*It).BoxSphereBounds.Origin+= InOffset; - } - - // Primitive occlusion bounds - for (auto It = PrimitiveOcclusionBounds.CreateIterator(); It; ++It) - { - (*It).Origin+= InOffset; - } - // Lights VectorRegister OffsetReg = VectorLoadFloat3_W0(&InOffset); for (auto It = Lights.CreateIterator(); It; ++It) diff --git a/Engine/Source/Runtime/Renderer/Private/ScenePrivate.h b/Engine/Source/Runtime/Renderer/Private/ScenePrivate.h index c3d1ac5430d8..70c1abf97f0b 100644 --- a/Engine/Source/Runtime/Renderer/Private/ScenePrivate.h +++ b/Engine/Source/Runtime/Renderer/Private/ScenePrivate.h @@ -2498,6 +2498,7 @@ public: TArray PrimitiveComponentIds; TSet PrimitivesNeedingStaticMeshUpdate; + TSet PrimitivesNeedingStaticMeshUpdateWithoutVisibilityCheck; struct FTypeOffsetTableEntry { @@ -2973,7 +2974,7 @@ private: * * @param InOffset Delta to shift scene by */ - void ApplyWorldOffset_RenderThread(FVector InOffset); + void ApplyWorldOffset_RenderThread(const FVector& InOffset); /** * Notification from game thread that level was added to a world diff --git a/Engine/Source/Runtime/Renderer/Private/SceneRendering.h b/Engine/Source/Runtime/Renderer/Private/SceneRendering.h index 67cba8b0b8f9..c8cb2394c2c4 100644 --- a/Engine/Source/Runtime/Renderer/Private/SceneRendering.h +++ b/Engine/Source/Runtime/Renderer/Private/SceneRendering.h @@ -641,6 +641,7 @@ struct FPreviousViewInfo TRefCountPtr DepthBuffer; TRefCountPtr GBufferA; TRefCountPtr GBufferB; + TRefCountPtr GBufferC; // Temporal AA result of last frame FTemporalAAHistory TemporalAAHistory; @@ -675,6 +676,7 @@ struct FPreviousViewInfo DepthBuffer.SafeRelease(); GBufferA.SafeRelease(); GBufferB.SafeRelease(); + GBufferC.SafeRelease(); TemporalAAHistory.SafeRelease(); DOFPreGatherHistory.SafeRelease(); DOFPostGatherForegroundHistory.SafeRelease(); diff --git a/Engine/Source/Runtime/Renderer/Private/SceneVisibility.cpp b/Engine/Source/Runtime/Renderer/Private/SceneVisibility.cpp index 3860e923fb0e..2657903c8641 100644 --- a/Engine/Source/Runtime/Renderer/Private/SceneVisibility.cpp +++ b/Engine/Source/Runtime/Renderer/Private/SceneVisibility.cpp @@ -3415,6 +3415,19 @@ void FSceneRenderer::ComputeViewVisibility(FRHICommandListImmediate& RHICmdList, const bool bIsInstancedStereo = (Views.Num() > 0) ? (Views[0].IsInstancedStereoPass() || Views[0].bIsMobileMultiViewEnabled) : false; UpdateReflectionSceneData(Scene); + { + QUICK_SCOPE_CYCLE_COUNTER(STAT_ViewVisibilityTime_ConditionalUpdateStaticMeshesWithoutVisibilityCheck); + + Scene->ConditionalMarkStaticMeshElementsForUpdate(); + + for (TSet::TIterator It(Scene->PrimitivesNeedingStaticMeshUpdateWithoutVisibilityCheck); It; ++It) + { + FPrimitiveSceneInfo* Primitive = *It; + Primitive->ConditionalUpdateStaticMeshes(RHICmdList); + } + Scene->PrimitivesNeedingStaticMeshUpdateWithoutVisibilityCheck.Reset(); + } + uint8 ViewBit = 0x1; for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex, ViewBit <<= 1) { @@ -3632,7 +3645,6 @@ void FSceneRenderer::ComputeViewVisibility(FRHICommandListImmediate& RHICmdList, { QUICK_SCOPE_CYCLE_COUNTER(STAT_ViewVisibilityTime_ConditionalUpdateStaticMeshes); - Scene->ConditionalMarkStaticMeshElementsForUpdate(); for (TSet::TIterator It(Scene->PrimitivesNeedingStaticMeshUpdate); It; ++It) { diff --git a/Engine/Source/Runtime/Renderer/Private/ScreenSpaceDenoise.cpp b/Engine/Source/Runtime/Renderer/Private/ScreenSpaceDenoise.cpp index 6c13ea197188..7451b9b048af 100644 --- a/Engine/Source/Runtime/Renderer/Private/ScreenSpaceDenoise.cpp +++ b/Engine/Source/Runtime/Renderer/Private/ScreenSpaceDenoise.cpp @@ -24,10 +24,15 @@ static TAutoConsoleVariable CVarShadowUse1SPPCodePath( ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarShadowReconstructionSampleCount( - TEXT("r.Shadow.Denoiser.ReconstructionSamples"), 16, + TEXT("r.Shadow.Denoiser.ReconstructionSamples"), 8, TEXT("Maximum number of samples for the reconstruction pass (default = 16)."), ECVF_RenderThreadSafe); +static TAutoConsoleVariable CVarShadowPreConvolutionCount( + TEXT("r.Shadow.Denoiser.PreConvolution"), 1, + TEXT("Number of pre-convolution passes (default = 1)."), + ECVF_RenderThreadSafe); + static TAutoConsoleVariable CVarShadowTemporalAccumulation( TEXT("r.Shadow.Denoiser.TemporalAccumulation"), 1, TEXT(""), @@ -150,6 +155,12 @@ static bool SignalUsesInjestion(ESignalProcessing SignalProcessing) return SignalProcessing == ESignalProcessing::MonochromaticPenumbra; } +/** Returns whether a signal processing uses an additional pre convolution pass. */ +static bool SignalUsesPreConvolution(ESignalProcessing SignalProcessing) +{ + return SignalProcessing == ESignalProcessing::MonochromaticPenumbra; +} + /** Returns whether a signal processing uses a history rejection pre convolution pass. */ static bool SignalUsesRejectionPreConvolution(ESignalProcessing SignalProcessing) { @@ -253,6 +264,32 @@ const TCHAR* const kReconstructionResourceNames[] = { TEXT("GIReconstruction3"), }; +const TCHAR* const kPreConvolutionResourceNames[] = { + // Penumbra + TEXT("ShadowPreConvolution0"), + TEXT("ShadowPreConvolution1"), + TEXT("ShadowPreConvolution2"), + TEXT("ShadowPreConvolution3"), + + // Reflections + nullptr, + nullptr, + nullptr, + nullptr, + + // AmbientOcclusion + nullptr, + nullptr, + nullptr, + nullptr, + + // GlobalIllumination + nullptr, + nullptr, + nullptr, + nullptr, +}; + const TCHAR* const kRejectionPreConvolutionResourceNames[] = { // Penumbra TEXT("ShadowRejectionPreConvolution0"), @@ -489,6 +526,9 @@ class FSSDSpatialAccumulationCS : public FScreenSpaceDenoisingShader // Spatial kernel used to process raw input for the temporal accumulation. ReConstruction, + // Spatial kernel to pre filter. + PreConvolution, + // Spatial kernel used to pre convolve history rejection. RejectionPreConvolution, @@ -530,6 +570,13 @@ class FSSDSpatialAccumulationCS : public FScreenSpaceDenoisingShader return false; } + // Only compile pre convolution for signal that uses it. + if (!SignalUsesPreConvolution(SignalProcessing) && + PermutationVector.Get() == EStage::PreConvolution) + { + return false; + } + // Only compile rejection pre convolution for signal that uses it. if (!SignalUsesRejectionPreConvolution(SignalProcessing) && PermutationVector.Get() == EStage::RejectionPreConvolution) @@ -638,6 +685,7 @@ struct FSSDConstantPixelDensitySettings int32 MaxInputSPP = 1; float InputResolutionFraction = 1.0f; int32 ReconstructionSamples = 1; + int32 PreConvolutionCount = 0; bool bUseTemporalAccumulation = false; int32 HistoryConvolutionSampleCount = 1; float HistoryConvolutionKernelSpreadFactor = 1.0f; @@ -875,6 +923,41 @@ static void DenoiseSignalAtConstantPixelDensity( SignalHistory = NewSignalOutput; } + // Spatial pre convolutions + for (int32 PreConvolutionId = 0; PreConvolutionId < Settings.PreConvolutionCount; PreConvolutionId++) + { + check(SignalUsesPreConvolution(Settings.SignalProcessing)); + + FSSDSignalTextures NewSignalOutput = CreateMultiplexedTextures( + GraphBuilder, + ReconstructionTextureCount, ReconstructionDescs, + GetResourceNames(kPreConvolutionResourceNames)); + + FSSDSpatialAccumulationCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); + PassParameters->CommonParameters = CommonParameters; + PassParameters->ConvolutionMetaData = ConvolutionMetaData; + PassParameters->SignalInput = SignalHistory; + PassParameters->SignalOutput = CreateMultiplexedUAVs(GraphBuilder, NewSignalOutput); + + PassParameters->DebugOutput = GraphBuilder.CreateUAV(GraphBuilder.CreateTexture(DebugDesc, TEXT("DebugDenoiserPreConvolution"))); + + FSSDSpatialAccumulationCS::FPermutationDomain PermutationVector; + PermutationVector.Set(Settings.SignalProcessing); + PermutationVector.Set(Settings.SignalBatchSize); + PermutationVector.Set(FSSDSpatialAccumulationCS::EStage::PreConvolution); + PermutationVector.Set(true); + + TShaderMapRef ComputeShader(View.ShaderMap, PermutationVector); + FComputeShaderUtils::AddPass( + GraphBuilder, + RDG_EVENT_NAME("SSD PreConvolution(MaxSamples=7)"), + *ComputeShader, + PassParameters, + FComputeShaderUtils::GetGroupCount(DenoiseResolution, FSSDSpatialAccumulationCS::kGroupSize)); + + SignalHistory = NewSignalOutput; + } + // Temporal pass. // // Note: always done even if there is no ViewState, because it is already not an idea case for the denoiser quality, therefore not really @@ -1177,6 +1260,7 @@ public: Settings.SignalProcessing = ESignalProcessing::MonochromaticPenumbra; Settings.InputResolutionFraction = 1.0f; Settings.ReconstructionSamples = CVarShadowReconstructionSampleCount.GetValueOnRenderThread(); + Settings.PreConvolutionCount = CVarShadowPreConvolutionCount.GetValueOnRenderThread(); Settings.bUseTemporalAccumulation = CVarShadowTemporalAccumulation.GetValueOnRenderThread() != 0; Settings.HistoryConvolutionSampleCount = CVarShadowHistoryConvolutionSampleCount.GetValueOnRenderThread(); Settings.SignalBatchSize = InputParameterCount; diff --git a/Engine/Source/Runtime/Renderer/Private/ScreenSpaceDenoise.h b/Engine/Source/Runtime/Renderer/Private/ScreenSpaceDenoise.h index 3fa8fb403b3e..4434e080c3c7 100644 --- a/Engine/Source/Runtime/Renderer/Private/ScreenSpaceDenoise.h +++ b/Engine/Source/Runtime/Renderer/Private/ScreenSpaceDenoise.h @@ -114,6 +114,7 @@ public: BEGIN_SHADER_PARAMETER_STRUCT(FReflectionsInputs, ) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, Color) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, RayHitDistance) + SHADER_PARAMETER_RDG_TEXTURE(Texture2D, RayImaginaryDepth) END_SHADER_PARAMETER_STRUCT() /** All the outputs of the reflection denoiser may generate. */ diff --git a/Engine/Source/Runtime/Renderer/Private/ShadowRendering.cpp b/Engine/Source/Runtime/Renderer/Private/ShadowRendering.cpp index 5be5c55f706b..1b6e0e80b0c8 100644 --- a/Engine/Source/Runtime/Renderer/Private/ShadowRendering.cpp +++ b/Engine/Source/Runtime/Renderer/Private/ShadowRendering.cpp @@ -565,7 +565,7 @@ void FProjectedShadowInfo::SetupFrustumForProjection(const FViewInfo* View, TArr const FPlane Top(BackTopRight, BackTopLeft, FrontTopLeft); const float TopDistance = Top.PlaneDot(ShadowViewOrigin); - const FPlane Bottom(FrontBottomRight, FrontBottomLeft, BackBottomLeft); + const FPlane Bottom(BackBottomLeft, BackBottomRight, FrontBottomLeft); const float BottomDistance = Bottom.PlaneDot(ShadowViewOrigin); // Use a distance threshold to treat the case where the near plane is intersecting the frustum as the camera being inside @@ -1188,7 +1188,7 @@ void FProjectedShadowInfo::UpdateShaderDepthBias() float FProjectedShadowInfo::ComputeTransitionSize() const { - float TransitionSize = 1; + float TransitionSize = 1.0f; if (IsWholeScenePointLightShadow()) { @@ -1215,7 +1215,7 @@ float FProjectedShadowInfo::ComputeTransitionSize() const else if (bPreShadow) { // Preshadows don't have self shadowing, so make sure the shadow starts as close to the caster as possible - TransitionSize = 0.00001f; + TransitionSize = 0.0f; } else { @@ -1225,7 +1225,9 @@ float FProjectedShadowInfo::ComputeTransitionSize() const TransitionSize *= 2.0f * LightSceneInfo->Proxy->GetUserShadowBias(); } - return TransitionSize; + // Make sure that shadow soft transition size is greater than zero so 1/TransitionSize shader parameter won't be INF. + const float MinTransitionSize = 0.00001f; + return FMath::Max(TransitionSize, MinTransitionSize); } /*----------------------------------------------------------------------------- diff --git a/Engine/Source/Runtime/Renderer/Private/ShadowSetup.cpp b/Engine/Source/Runtime/Renderer/Private/ShadowSetup.cpp index 0c246857ffa1..00b068e36ca6 100644 --- a/Engine/Source/Runtime/Renderer/Private/ShadowSetup.cpp +++ b/Engine/Source/Runtime/Renderer/Private/ShadowSetup.cpp @@ -917,7 +917,7 @@ bool FProjectedShadowInfo::ShouldDrawStaticMeshes(FViewInfo& InCurrentView, bool // Don't cache if it requires per view per mesh state for distance cull fade. const bool bIsPrimitiveDistanceCullFading = InCurrentView.PotentiallyFadingPrimitiveMap[InPrimitiveSceneInfo->GetIndex()]; - const bool bCanCache = !bIsPrimitiveDistanceCullFading; + const bool bCanCache = !bIsPrimitiveDistanceCullFading && !InPrimitiveSceneInfo->NeedsUpdateStaticMeshes(); for (int32 MeshIndex = 0; MeshIndex < InPrimitiveSceneInfo->StaticMeshRelevances.Num(); MeshIndex++) { @@ -1079,17 +1079,24 @@ void FProjectedShadowInfo::AddSubjectPrimitive(FPrimitiveSceneInfo* PrimitiveSce // Update the primitive component's last render time. Allows the component to update when using bCastWhenHidden. const float CurrentWorldTime = Views[0]->Family->CurrentWorldTime; *(PrimitiveSceneInfo->ComponentLastRenderTime) = CurrentWorldTime; - if (PrimitiveSceneInfo->NeedsUniformBufferUpdate() || PrimitiveSceneInfo->NeedsUpdateStaticMeshes()) + + if (PrimitiveSceneInfo->NeedsUniformBufferUpdate()) { for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { - // Main view visible primitives are processed on parallel tasks, updating them here will cause a race condition. + // Main view visible primitives are processed on parallel tasks, updating uniform buffer them here will cause a race condition. check(!Views[ViewIndex]->PrimitiveVisibilityMap[PrimitiveSceneInfo->GetIndex()]); } - PrimitiveSceneInfo->ConditionalUpdateStaticMeshes(FRHICommandListExecutor::GetImmediateCommandList()); PrimitiveSceneInfo->ConditionalUpdateUniformBuffer(FRHICommandListExecutor::GetImmediateCommandList()); } + + if (PrimitiveSceneInfo->NeedsUpdateStaticMeshes()) + { + // Need to defer to next InitViews, as main view visible primitives are processed on parallel tasks and calling + // CacheMeshDrawCommands may resize CachedDrawLists/CachedMeshDrawCommandStateBuckets causing a crash. + PrimitiveSceneInfo->BeginDeferredUpdateStaticMeshesWithoutVisibilityCheck(); + } } if (bOpaqueRelevance && bShadowRelevance) @@ -2144,11 +2151,19 @@ void FSceneRenderer::CreatePerObjectProjectedShadow( } } +static bool CanFallbackToOldShadowMapCache(const FShadowMapRenderTargetsRefCounted& CachedShadowMap, const FIntPoint& MaxShadowResolution) +{ + return CachedShadowMap.IsValid() + && CachedShadowMap.GetSize().X <= MaxShadowResolution.X + && CachedShadowMap.GetSize().Y <= MaxShadowResolution.Y; +} + void ComputeWholeSceneShadowCacheModes( const FLightSceneInfo* LightSceneInfo, bool bCubeShadowMap, float RealTime, float ActualDesiredResolution, + const FIntPoint& MaxShadowResolution, FScene* Scene, FWholeSceneProjectedShadowInitializer& InOutProjectedShadowInitializer, FIntPoint& InOutShadowMapSize, @@ -2207,7 +2222,7 @@ void ComputeWholeSceneShadowCacheModes( ++*NumCachesUpdatedThisFrame; // Check if update is caused by resolution change - if (CachedShadowMapData->ShadowMap.IsValid()) + if (CanFallbackToOldShadowMapCache(CachedShadowMapData->ShadowMap, MaxShadowResolution)) { FIntPoint ExistingShadowMapSize = CachedShadowMapData->ShadowMap.GetSize(); bool bOverBudget = *NumCachesUpdatedThisFrame > MaxCacheUpdatesAllowed; @@ -2549,6 +2564,7 @@ void FSceneRenderer::CreateWholeSceneProjectedShadow( ProjectedShadowInitializer.bOnePassPointLightShadow, ViewFamily.CurrentRealTime, MaxDesiredResolution, + FIntPoint(MaxShadowResolution, MaxShadowResolutionY), Scene, // Below are in-out or out parameters. They can change ProjectedShadowInitializer, diff --git a/Engine/Source/Runtime/Renderer/Private/SystemTextures.h b/Engine/Source/Runtime/Renderer/Private/SystemTextures.h index b54d9b918869..cd3dcacf41e9 100644 --- a/Engine/Source/Runtime/Renderer/Private/SystemTextures.h +++ b/Engine/Source/Runtime/Renderer/Private/SystemTextures.h @@ -96,4 +96,4 @@ protected: }; /** The global system textures used for scene rendering. */ -extern TGlobalResource GSystemTextures; +RENDERER_API extern TGlobalResource GSystemTextures; diff --git a/Engine/Source/Runtime/Renderer/Public/PrimitiveSceneInfo.h b/Engine/Source/Runtime/Renderer/Public/PrimitiveSceneInfo.h index 983ba022d3ea..b11a7c524508 100644 --- a/Engine/Source/Runtime/Renderer/Public/PrimitiveSceneInfo.h +++ b/Engine/Source/Runtime/Renderer/Public/PrimitiveSceneInfo.h @@ -356,6 +356,9 @@ public: /** Sets a flag to update the primitive's static meshes before it is next rendered. */ void BeginDeferredUpdateStaticMeshes(); + /** Will update static meshes during next InitViews, even if it's not visible. */ + void BeginDeferredUpdateStaticMeshesWithoutVisibilityCheck(); + /** Adds the primitive's static meshes to the scene. */ void AddStaticMeshes(FRHICommandListImmediate& RHICmdList, bool bUpdateStaticDrawLists = true); @@ -461,13 +464,16 @@ private: const UPrimitiveComponent* ComponentForDebuggingOnly; /** If this is TRUE, this primitive's static meshes needs to be updated before it can be rendered. */ - bool bNeedsStaticMeshUpdate; + bool bNeedsStaticMeshUpdate : 1; + + /** If this is TRUE, this primitive's static meshes will be update even if it's not visible. */ + bool bNeedsStaticMeshUpdateWithoutVisibilityCheck : 1; /** If this is TRUE, this primitive's uniform buffer needs to be updated before it can be rendered. */ - bool bNeedsUniformBufferUpdate; + bool bNeedsUniformBufferUpdate : 1; /** If this is TRUE, this primitive's indirect lighting cache buffer needs to be updated before it can be rendered. */ - bool bIndirectLightingCacheBufferDirty; + bool bIndirectLightingCacheBufferDirty : 1; /** Offset into the scene's lightmap data buffer, when GPUScene is enabled. */ int32 LightmapDataOffset; diff --git a/Engine/Source/Runtime/Slate/Public/Widgets/Layout/SScrollBox.h b/Engine/Source/Runtime/Slate/Public/Widgets/Layout/SScrollBox.h index 19e1f1c0cbbc..ffe4c8cb0393 100644 --- a/Engine/Source/Runtime/Slate/Public/Widgets/Layout/SScrollBox.h +++ b/Engine/Source/Runtime/Slate/Public/Widgets/Layout/SScrollBox.h @@ -174,6 +174,8 @@ public: void ScrollToEnd(); + void EndInertialScrolling(); + /** * Attempt to scroll a widget into view, will safely handle non-descendant widgets * @@ -285,8 +287,6 @@ private: void BeginInertialScrolling(); - void EndInertialScrolling(); - TSharedPtr GetKeyboardFocusableWidget(TSharedPtr InWidget); protected: diff --git a/Engine/Source/Runtime/SlateCore/Private/Types/ReflectionMetadata.cpp b/Engine/Source/Runtime/SlateCore/Private/Types/ReflectionMetadata.cpp index 5dc5342a8b61..bc16d4a59cde 100644 --- a/Engine/Source/Runtime/SlateCore/Private/Types/ReflectionMetadata.cpp +++ b/Engine/Source/Runtime/SlateCore/Private/Types/ReflectionMetadata.cpp @@ -9,8 +9,13 @@ FString FReflectionMetaData::GetWidgetDebugInfo(const SWidget* InWidget) return TEXT("None"); } + return GetWidgetDebugInfo(*InWidget); +} + +FString FReflectionMetaData::GetWidgetDebugInfo(const SWidget& InWidget) +{ // UMG widgets have meta-data to help track them - TSharedPtr MetaData = InWidget->GetMetaData(); + TSharedPtr MetaData = InWidget.GetMetaData(); if (MetaData.IsValid()) { if (const UObject* AssetPtr = MetaData->Asset.Get()) @@ -22,7 +27,7 @@ FString FReflectionMetaData::GetWidgetDebugInfo(const SWidget* InWidget) } } - TSharedPtr ParentMetadata = GetWidgetOrParentMetaData(InWidget); + TSharedPtr ParentMetadata = GetWidgetOrParentMetaData(&InWidget); if (ParentMetadata.IsValid()) { if (const UObject* AssetPtr = ParentMetadata->Asset.Get()) @@ -30,11 +35,11 @@ FString FReflectionMetaData::GetWidgetDebugInfo(const SWidget* InWidget) const FName AssetName = AssetPtr->GetFName(); const FName WidgetName = ParentMetadata->Name; - return FString::Printf(TEXT("%s [%s(%s)]"), *AssetName.ToString(), *WidgetName.ToString(), *InWidget->GetReadableLocation()); + return FString::Printf(TEXT("%s [%s(%s)]"), *AssetName.ToString(), *WidgetName.ToString(), *InWidget.GetReadableLocation()); } } - return InWidget->ToString(); + return InWidget.ToString(); } TSharedPtr FReflectionMetaData::GetWidgetOrParentMetaData(const SWidget* InWidget) diff --git a/Engine/Source/Runtime/SlateCore/Public/Types/ReflectionMetadata.h b/Engine/Source/Runtime/SlateCore/Public/Types/ReflectionMetadata.h index 0111c9d973ee..6b6c4f501e83 100644 --- a/Engine/Source/Runtime/SlateCore/Public/Types/ReflectionMetadata.h +++ b/Engine/Source/Runtime/SlateCore/Public/Types/ReflectionMetadata.h @@ -41,6 +41,7 @@ public: public: static FString GetWidgetDebugInfo(const SWidget* InWidget); + static FString GetWidgetDebugInfo(const SWidget& InWidget); static TSharedPtr GetWidgetOrParentMetaData(const SWidget* InWidget); }; diff --git a/Engine/Source/Runtime/UMG/Private/Blueprint/UserWidgetPool.cpp b/Engine/Source/Runtime/UMG/Private/Blueprint/UserWidgetPool.cpp index cd827da0773c..e0266d83d9cb 100644 --- a/Engine/Source/Runtime/UMG/Private/Blueprint/UserWidgetPool.cpp +++ b/Engine/Source/Runtime/UMG/Private/Blueprint/UserWidgetPool.cpp @@ -6,20 +6,6 @@ FUserWidgetPool::FUserWidgetPool(UWidget& InOwningWidget) : OwningWidget(&InOwningWidget) {} -FUserWidgetPool& FUserWidgetPool::operator=(FUserWidgetPool&& Other) -{ - OwningWidget = Other.OwningWidget; - - ActiveWidgets = MoveTemp(Other.ActiveWidgets); - InactiveWidgets = MoveTemp(Other.InactiveWidgets); - CachedSlateByWidgetObject = MoveTemp(Other.CachedSlateByWidgetObject); - - Other.OwningWidget.Reset(); - Other.ResetPool(); - - return *this; -} - FUserWidgetPool::~FUserWidgetPool() { ResetPool(); @@ -68,3 +54,8 @@ void FUserWidgetPool::ResetPool() ActiveWidgets.Reset(); CachedSlateByWidgetObject.Reset(); } + +void FUserWidgetPool::ReleaseSlateResources() +{ + CachedSlateByWidgetObject.Reset(); +} diff --git a/Engine/Source/Runtime/UMG/Private/Components/Button.cpp b/Engine/Source/Runtime/UMG/Private/Components/Button.cpp index 2acb59a0132a..e0abb8d2e180 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/Button.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/Button.cpp @@ -14,8 +14,9 @@ UButton::UButton(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { - SButton::FArguments ButtonDefaults; - WidgetStyle = *ButtonDefaults._ButtonStyle; + // HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BY DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS + static const FButtonStyle StaticButtonStyle = FCoreStyle::Get().GetWidgetStyle< FButtonStyle >("Button"); + WidgetStyle = StaticButtonStyle; ColorAndOpacity = FLinearColor::White; BackgroundColor = FLinearColor::White; diff --git a/Engine/Source/Runtime/UMG/Private/Components/CanvasPanel.cpp b/Engine/Source/Runtime/UMG/Private/Components/CanvasPanel.cpp index 719b60416ff9..a835ea88c8b5 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/CanvasPanel.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/CanvasPanel.cpp @@ -13,9 +13,7 @@ UCanvasPanel::UCanvasPanel(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { bIsVariable = false; - - SConstraintCanvas::FArguments Defaults; - Visibility = UWidget::ConvertRuntimeToSerializedVisibility(Defaults._Visibility.Get()); + Visibility = ESlateVisibility::SelfHitTestInvisible; } void UCanvasPanel::ReleaseSlateResources(bool bReleaseChildren) diff --git a/Engine/Source/Runtime/UMG/Private/Components/CheckBox.cpp b/Engine/Source/Runtime/UMG/Private/Components/CheckBox.cpp index ace3df1f7f38..f2c5d6d28604 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/CheckBox.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/CheckBox.cpp @@ -14,18 +14,18 @@ UCheckBox::UCheckBox(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { - SCheckBox::FArguments SlateDefaults; - WidgetStyle = *SlateDefaults._Style; + // HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BY DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS + static const FCheckBoxStyle StaticCheckboxStyle = FCoreStyle::Get().GetWidgetStyle< FCheckBoxStyle >("Checkbox"); + WidgetStyle = StaticCheckboxStyle; CheckedState = ECheckBoxState::Unchecked; - HorizontalAlignment = SlateDefaults._HAlign; - Padding_DEPRECATED = SlateDefaults._Padding.Get(); + HorizontalAlignment = HAlign_Fill; + Padding_DEPRECATED = FMargin(0, 0, 0, 0); BorderBackgroundColor_DEPRECATED = FLinearColor::White; IsFocusable = true; - } void UCheckBox::ReleaseSlateResources(bool bReleaseChildren) diff --git a/Engine/Source/Runtime/UMG/Private/Components/CircularThrobber.cpp b/Engine/Source/Runtime/UMG/Private/Components/CircularThrobber.cpp index e642bbf5c021..6763f2bd8ceb 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/CircularThrobber.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/CircularThrobber.cpp @@ -12,15 +12,16 @@ // UCircularThrobber UCircularThrobber::UCircularThrobber(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer), - bEnableRadius(true) + : Super(ObjectInitializer) + , bEnableRadius(true) { - SCircularThrobber::FArguments DefaultArgs; - Image = *DefaultArgs._PieceImage; + // HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BY DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS + static const FSlateBrush StaticBrushStyle = *FCoreStyle::Get().GetBrush("Throbber.CircleChunk"); + Image = StaticBrushStyle; - NumberOfPieces = DefaultArgs._NumPieces; - Period = DefaultArgs._Period; - Radius = DefaultArgs._Radius; + NumberOfPieces = 6; + Period = 0.75f; + Radius = 16.f; } void UCircularThrobber::ReleaseSlateResources(bool bReleaseChildren) @@ -32,8 +33,6 @@ void UCircularThrobber::ReleaseSlateResources(bool bReleaseChildren) TSharedRef UCircularThrobber::RebuildWidget() { - SCircularThrobber::FArguments DefaultArgs; - MyCircularThrobber = SNew(SCircularThrobber) .PieceImage(&Image) .NumPieces(FMath::Clamp(NumberOfPieces, 1, 25)) diff --git a/Engine/Source/Runtime/UMG/Private/Components/ComboBoxString.cpp b/Engine/Source/Runtime/UMG/Private/Components/ComboBoxString.cpp index c33ae36226dd..3c18b8b42ecc 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/ComboBoxString.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/ComboBoxString.cpp @@ -13,9 +13,12 @@ UComboBoxString::UComboBoxString(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { - SComboBox< TSharedPtr >::FArguments SlateDefaults; - WidgetStyle = *SlateDefaults._ComboBoxStyle; - ItemStyle = *SlateDefaults._ItemStyle; + // HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BY DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS + static const FComboBoxStyle StaticComboboxStyle = FCoreStyle::Get().GetWidgetStyle< FComboBoxStyle >("ComboBox"); + static const FTableRowStyle StaticRowStyle = FCoreStyle::Get().GetWidgetStyle< FTableRowStyle >("TableView.Row"); + + WidgetStyle = StaticComboboxStyle; + ItemStyle = StaticRowStyle; ItemStyle.SelectorFocusedBrush.TintColor = ItemStyle.SelectorFocusedBrush.TintColor.GetSpecifiedColor(); ItemStyle.ActiveHoveredBrush.TintColor = ItemStyle.ActiveHoveredBrush.TintColor.GetSpecifiedColor(); ItemStyle.ActiveBrush.TintColor = ItemStyle.ActiveBrush.TintColor.GetSpecifiedColor(); diff --git a/Engine/Source/Runtime/UMG/Private/Components/DynamicEntryBox.cpp b/Engine/Source/Runtime/UMG/Private/Components/DynamicEntryBox.cpp index 2bd6ca36bcae..07350f3f159a 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/DynamicEntryBox.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/DynamicEntryBox.cpp @@ -12,183 +12,21 @@ // UDynamicEntryBox ////////////////////////////////////////////////////////////////////////// -UDynamicEntryBox::UDynamicEntryBox(const FObjectInitializer& Initializer) - : Super(Initializer) - , EntryWidgetPool(*this) -{ - bIsVariable = true; - - Visibility = ESlateVisibility::SelfHitTestInvisible; - EntrySizeRule.SizeRule = ESlateSizeRule::Automatic; -} - -void UDynamicEntryBox::ReleaseSlateResources(bool bReleaseChildren) -{ - Super::ReleaseSlateResources(bReleaseChildren); - - EntryWidgetPool.ResetPool(); - MyPanelWidget.Reset(); -} - void UDynamicEntryBox::Reset(bool bDeleteWidgets) { - EntryWidgetPool.ReleaseAll(bDeleteWidgets); - - if (MyPanelWidget.IsValid()) - { - switch (EntryBoxType) - { - case EDynamicBoxType::Horizontal: - case EDynamicBoxType::Vertical: - StaticCastSharedPtr(MyPanelWidget)->ClearChildren(); - break; - case EDynamicBoxType::Wrap: - StaticCastSharedPtr(MyPanelWidget)->ClearChildren(); - break; - case EDynamicBoxType::Overlay: - StaticCastSharedPtr(MyPanelWidget)->ClearChildren(); - break; - } - } -} - -const TArray& UDynamicEntryBox::GetAllEntries() const -{ - return EntryWidgetPool.GetActiveWidgets(); -} - -int32 UDynamicEntryBox::GetNumEntries() const -{ - return MyPanelWidget.IsValid() ? MyPanelWidget->GetChildren()->Num() : 0; + ResetInternal(bDeleteWidgets); } void UDynamicEntryBox::RemoveEntry(UUserWidget* EntryWidget) { - if (EntryWidget) - { - if (MyPanelWidget.IsValid()) - { - TSharedPtr CachedEntryWidget = EntryWidget->GetCachedWidget(); - if (CachedEntryWidget.IsValid()) - { - switch (EntryBoxType) - { - case EDynamicBoxType::Horizontal: - case EDynamicBoxType::Vertical: - StaticCastSharedPtr(MyPanelWidget)->RemoveSlot(CachedEntryWidget.ToSharedRef()); - break; - case EDynamicBoxType::Wrap: - StaticCastSharedPtr(MyPanelWidget)->RemoveSlot(CachedEntryWidget.ToSharedRef()); - break; - case EDynamicBoxType::Overlay: - StaticCastSharedPtr(MyPanelWidget)->RemoveSlot(CachedEntryWidget.ToSharedRef()); - break; - } - } - } - EntryWidgetPool.Release(EntryWidget); - } -} - -void UDynamicEntryBox::SetEntrySpacing(const FVector2D& InEntrySpacing) -{ - EntrySpacing = InEntrySpacing; - - if (MyPanelWidget.IsValid()) - { - if (EntryBoxType == EDynamicBoxType::Wrap) - { - // Wrap boxes can change their widget spacing on the fly - StaticCastSharedPtr(MyPanelWidget)->SetInnerSlotPadding(EntrySpacing); - } - else if (EntryBoxType == EDynamicBoxType::Overlay) - { - TPanelChildren* OverlayChildren = static_cast*>(MyPanelWidget->GetChildren()); - for (int32 ChildIdx = 0; ChildIdx < OverlayChildren->Num(); ++ChildIdx) - { - FMargin Padding; - if (SpacingPattern.Num() > 0) - { - FVector2D Spacing(0.f, 0.f); - - // First establish the starting location - for (int32 CountIdx = 0; CountIdx < ChildIdx; ++CountIdx) - { - int32 PatternIdx = CountIdx % SpacingPattern.Num(); - Spacing += SpacingPattern[PatternIdx]; - } - - // Negative padding is no good, so negative spacing is expressed as positive spacing on the opposite side - if (Spacing.X >= 0.f) - { - Padding.Left = Spacing.X; - } - else - { - Padding.Right = -Spacing.X; - } - if (Spacing.Y >= 0.f) - { - Padding.Top = Spacing.Y; - } - else - { - Padding.Bottom = -Spacing.Y; - } - } - else - { - if (EntrySpacing.X >= 0.f) - { - Padding.Left = ChildIdx * EntrySpacing.X; - } - else - { - Padding.Right = ChildIdx * -EntrySpacing.X; - } - - if (EntrySpacing.Y >= 0.f) - { - Padding.Top = ChildIdx * EntrySpacing.Y; - } - else - { - Padding.Bottom = ChildIdx * -EntrySpacing.Y; - } - } - SOverlay::FOverlaySlot& OverlaySlot = (*OverlayChildren)[ChildIdx]; - OverlaySlot.SlotPadding = Padding; - } - } - else - { - // Vertical & Horizontal have to manually update the padding on each slot - const bool bIsHBox = EntryBoxType == EDynamicBoxType::Horizontal; - TPanelChildren* BoxChildren = static_cast*>(MyPanelWidget->GetChildren()); - for (int32 ChildIdx = 0; ChildIdx < BoxChildren->Num(); ++ChildIdx) - { - const bool bIsFirstChild = ChildIdx == 0; - - FMargin Padding; - Padding.Top = bIsHBox || bIsFirstChild ? 0.f : EntrySpacing.Y; - Padding.Left = bIsHBox && !bIsFirstChild ? EntrySpacing.X : 0.f; - - SBoxPanel::FSlot& BoxSlot = (*BoxChildren)[ChildIdx]; - BoxSlot.SlotPadding = Padding; - } - } - } + RemoveEntryInternal(EntryWidget); } #if WITH_EDITOR - -const FText UDynamicEntryBox::GetPaletteCategory() -{ - return LOCTEXT("Advanced", "Advanced"); -} - void UDynamicEntryBox::ValidateCompiledDefaults(IWidgetCompilerLog& CompileLog) const { + Super::ValidateCompiledDefaults(CompileLog); + if (!EntryWidgetClass) { CompileLog.Error(FText::Format(LOCTEXT("Error_DynamicEntryBox_MissingEntryClass", "{0} has no EntryWidgetClass specified - required for any Dynamic Entry Box to function."), FText::FromString(GetName()))); @@ -196,43 +34,6 @@ void UDynamicEntryBox::ValidateCompiledDefaults(IWidgetCompilerLog& CompileLog) } #endif -TSharedRef UDynamicEntryBox::RebuildWidget() -{ - TSharedPtr EntryBoxWidget; - switch (EntryBoxType) - { - case EDynamicBoxType::Horizontal: - EntryBoxWidget = SAssignNew(MyPanelWidget, SHorizontalBox); - break; - case EDynamicBoxType::Vertical: - EntryBoxWidget = SAssignNew(MyPanelWidget, SVerticalBox); - break; - case EDynamicBoxType::Wrap: - EntryBoxWidget = SAssignNew(MyPanelWidget, SWrapBox) - .UseAllottedWidth(true) - .InnerSlotPadding(EntrySpacing); - break; - case EDynamicBoxType::Overlay: - EntryBoxWidget = SAssignNew(MyPanelWidget, SOverlay) - .Clipping(EWidgetClipping::ClipToBounds); - break; - } - - return EntryBoxWidget.ToSharedRef(); -} - -#if WITH_EDITOR -void UDynamicEntryBox::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) -{ - if (MyPanelWidget.IsValid() && PropertyChangedEvent.GetPropertyName() == TEXT("EntryBoxType")) - { - MyPanelWidget.Reset(); - } - - Super::PostEditChangeProperty(PropertyChangedEvent); -} -#endif - void UDynamicEntryBox::SynchronizeProperties() { Super::SynchronizeProperties(); @@ -261,13 +62,6 @@ void UDynamicEntryBox::SynchronizeProperties() StartingNumber++; } } - else - { - // If we don't need to rebuild, update existing entries - SetEntrySpacing(EntrySpacing); - - //@todo DanH: update alignment, spacing pattern, clipping, size rule, max element size - } } #endif } @@ -283,109 +77,7 @@ UUserWidget* UDynamicEntryBox::BP_CreateEntryOfClass(TSubclassOf En { return CreateEntryInternal(EntryClass); } - return nullptr; } -UUserWidget* UDynamicEntryBox::CreateEntryInternal(TSubclassOf InEntryClass) -{ - if (MyPanelWidget.IsValid()) - { - UUserWidget* NewEntryWidget = EntryWidgetPool.GetOrCreateInstance(InEntryClass); - AddEntryChild(*NewEntryWidget); - return NewEntryWidget; - } - - UE_LOG(LogUMG, Warning, TEXT("UDynamicEntryBox::CreateEntryInternal(): Failed to create an entry.")); - return nullptr; -} - -FMargin UDynamicEntryBox::BuildEntryPadding(const FVector2D& DesiredSpacing) -{ - FMargin EntryPadding; - if (DesiredSpacing.X >= 0.f) - { - EntryPadding.Left = DesiredSpacing.X; - } - else - { - EntryPadding.Right = -DesiredSpacing.X; - } - - if (DesiredSpacing.Y >= 0.f) - { - EntryPadding.Top = DesiredSpacing.Y; - } - else - { - EntryPadding.Bottom = -DesiredSpacing.Y; - } - - return EntryPadding; -} - -void UDynamicEntryBox::AddEntryChild(UUserWidget& ChildWidget) -{ - FSlotBase* NewSlot = nullptr; - if (EntryBoxType == EDynamicBoxType::Wrap) - { - NewSlot = &StaticCastSharedPtr(MyPanelWidget)->AddSlot() - .FillEmptySpace(false) - .HAlign(EntryHorizontalAlignment) - .VAlign(EntryVerticalAlignment); - } - else if (EntryBoxType == EDynamicBoxType::Overlay) - { - const int32 ChildIdx = MyPanelWidget->GetChildren()->Num(); - SOverlay::FOverlaySlot& OverlaySlot = (SOverlay::FOverlaySlot&)StaticCastSharedPtr(MyPanelWidget)->AddSlot(); - - EHorizontalAlignment HAlign = EntryHorizontalAlignment; - EVerticalAlignment VAlign = EntryVerticalAlignment; - - FVector2D TargetSpacing = FVector2D::ZeroVector; - if (SpacingPattern.Num() > 0) - { - for (int32 CountIdx = 0; CountIdx < ChildIdx; ++CountIdx) - { - const int32 PatternIdx = CountIdx % SpacingPattern.Num(); - TargetSpacing += SpacingPattern[PatternIdx]; - } - } - else - { - TargetSpacing = EntrySpacing * ChildIdx; - HAlign = EntrySpacing.X >= 0.f ? EHorizontalAlignment::HAlign_Left : EHorizontalAlignment::HAlign_Right; - VAlign = EntrySpacing.Y >= 0.f ? EVerticalAlignment::VAlign_Top : EVerticalAlignment::VAlign_Bottom; - } - - OverlaySlot.HAlignment = HAlign; - OverlaySlot.VAlignment = VAlign; - OverlaySlot.SlotPadding = BuildEntryPadding(TargetSpacing); - - NewSlot = &OverlaySlot; - } - else - { - const bool bIsHBox = EntryBoxType == EDynamicBoxType::Horizontal; - const bool bIsFirstChild = MyPanelWidget->GetChildren()->Num() == 0; - - SBoxPanel::FSlot& BoxPanelSlot = bIsHBox ? (SBoxPanel::FSlot&)StaticCastSharedPtr(MyPanelWidget)->AddSlot().MaxWidth(MaxElementSize) : (SBoxPanel::FSlot&)StaticCastSharedPtr(MyPanelWidget)->AddSlot().MaxHeight(MaxElementSize); - BoxPanelSlot.HAlignment = EntryHorizontalAlignment; - BoxPanelSlot.VAlignment = EntryVerticalAlignment; - BoxPanelSlot.SizeParam = UWidget::ConvertSerializedSizeParamToRuntime(EntrySizeRule); - - FMargin Padding; - Padding.Top = bIsHBox || bIsFirstChild ? 0.f : EntrySpacing.Y; - Padding.Left = bIsHBox && !bIsFirstChild ? EntrySpacing.X : 0.f; - BoxPanelSlot.SlotPadding = Padding; - - NewSlot = &BoxPanelSlot; - } - - if (ensure(NewSlot)) - { - NewSlot->AttachWidget(ChildWidget.TakeWidget()); - } -} - #undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Engine/Source/Runtime/UMG/Private/Components/DynamicEntryBoxBase.cpp b/Engine/Source/Runtime/UMG/Private/Components/DynamicEntryBoxBase.cpp new file mode 100644 index 000000000000..e7f59610f472 --- /dev/null +++ b/Engine/Source/Runtime/UMG/Private/Components/DynamicEntryBoxBase.cpp @@ -0,0 +1,342 @@ +// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. + +#include "Components/DynamicEntryBoxBase.h" +#include "UMGPrivate.h" +#include "Widgets/Layout/SWrapBox.h" +#include "Widgets/SBoxPanel.h" +#include "Editor/WidgetCompilerLog.h" + +#define LOCTEXT_NAMESPACE "UMG" + +UDynamicEntryBoxBase::UDynamicEntryBoxBase(const FObjectInitializer& Initializer) + : Super(Initializer) + , EntryWidgetPool(*this) +{ + Visibility = ESlateVisibility::SelfHitTestInvisible; + EntrySizeRule.SizeRule = ESlateSizeRule::Automatic; +} + +void UDynamicEntryBoxBase::ReleaseSlateResources(bool bReleaseChildren) +{ + Super::ReleaseSlateResources(bReleaseChildren); + + EntryWidgetPool.ReleaseSlateResources(); + MyPanelWidget.Reset(); +} + +void UDynamicEntryBoxBase::ResetInternal(bool bDeleteWidgets) +{ + EntryWidgetPool.ReleaseAll(bDeleteWidgets); + + if (MyPanelWidget.IsValid()) + { + switch (EntryBoxType) + { + case EDynamicBoxType::Horizontal: + case EDynamicBoxType::Vertical: + StaticCastSharedPtr(MyPanelWidget)->ClearChildren(); + break; + case EDynamicBoxType::Wrap: + StaticCastSharedPtr(MyPanelWidget)->ClearChildren(); + break; + case EDynamicBoxType::Overlay: + StaticCastSharedPtr(MyPanelWidget)->ClearChildren(); + break; + } + } +} + +const TArray& UDynamicEntryBoxBase::GetAllEntries() const +{ + return EntryWidgetPool.GetActiveWidgets(); +} + +int32 UDynamicEntryBoxBase::GetNumEntries() const +{ + return EntryWidgetPool.GetActiveWidgets().Num(); +} + +void UDynamicEntryBoxBase::RemoveEntryInternal(UUserWidget* EntryWidget) +{ + if (EntryWidget) + { + if (MyPanelWidget.IsValid()) + { + TSharedPtr CachedEntryWidget = EntryWidget->GetCachedWidget(); + if (CachedEntryWidget.IsValid()) + { + switch (EntryBoxType) + { + case EDynamicBoxType::Horizontal: + case EDynamicBoxType::Vertical: + StaticCastSharedPtr(MyPanelWidget)->RemoveSlot(CachedEntryWidget.ToSharedRef()); + break; + case EDynamicBoxType::Wrap: + StaticCastSharedPtr(MyPanelWidget)->RemoveSlot(CachedEntryWidget.ToSharedRef()); + break; + case EDynamicBoxType::Overlay: + StaticCastSharedPtr(MyPanelWidget)->RemoveSlot(CachedEntryWidget.ToSharedRef()); + break; + } + } + } + EntryWidgetPool.Release(EntryWidget); + } +} + +void UDynamicEntryBoxBase::SetEntrySpacing(const FVector2D& InEntrySpacing) +{ + EntrySpacing = InEntrySpacing; + + if (MyPanelWidget.IsValid()) + { + if (EntryBoxType == EDynamicBoxType::Wrap) + { + // Wrap boxes can change their widget spacing on the fly + StaticCastSharedPtr(MyPanelWidget)->SetInnerSlotPadding(EntrySpacing); + } + else if (EntryBoxType == EDynamicBoxType::Overlay) + { + TPanelChildren* OverlayChildren = static_cast*>(MyPanelWidget->GetChildren()); + for (int32 ChildIdx = 0; ChildIdx < OverlayChildren->Num(); ++ChildIdx) + { + FMargin Padding; + if (SpacingPattern.Num() > 0) + { + FVector2D Spacing(0.f, 0.f); + + // First establish the starting location + for (int32 CountIdx = 0; CountIdx < ChildIdx; ++CountIdx) + { + int32 PatternIdx = CountIdx % SpacingPattern.Num(); + Spacing += SpacingPattern[PatternIdx]; + } + + // Negative padding is no good, so negative spacing is expressed as positive spacing on the opposite side + if (Spacing.X >= 0.f) + { + Padding.Left = Spacing.X; + } + else + { + Padding.Right = -Spacing.X; + } + if (Spacing.Y >= 0.f) + { + Padding.Top = Spacing.Y; + } + else + { + Padding.Bottom = -Spacing.Y; + } + } + else + { + if (EntrySpacing.X >= 0.f) + { + Padding.Left = ChildIdx * EntrySpacing.X; + } + else + { + Padding.Right = ChildIdx * -EntrySpacing.X; + } + + if (EntrySpacing.Y >= 0.f) + { + Padding.Top = ChildIdx * EntrySpacing.Y; + } + else + { + Padding.Bottom = ChildIdx * -EntrySpacing.Y; + } + } + SOverlay::FOverlaySlot& OverlaySlot = (*OverlayChildren)[ChildIdx]; + OverlaySlot.SlotPadding = Padding; + } + } + else + { + // Vertical & Horizontal have to manually update the padding on each slot + const bool bIsHBox = EntryBoxType == EDynamicBoxType::Horizontal; + TPanelChildren* BoxChildren = static_cast*>(MyPanelWidget->GetChildren()); + for (int32 ChildIdx = 0; ChildIdx < BoxChildren->Num(); ++ChildIdx) + { + const bool bIsFirstChild = ChildIdx == 0; + + FMargin Padding; + Padding.Top = bIsHBox || bIsFirstChild ? 0.f : EntrySpacing.Y; + Padding.Left = bIsHBox && !bIsFirstChild ? EntrySpacing.X : 0.f; + + SBoxPanel::FSlot& BoxSlot = (*BoxChildren)[ChildIdx]; + BoxSlot.SlotPadding = Padding; + } + } + } +} + +#if WITH_EDITOR + +const FText UDynamicEntryBoxBase::GetPaletteCategory() +{ + return LOCTEXT("Advanced", "Advanced"); +} +#endif + +TSharedRef UDynamicEntryBoxBase::RebuildWidget() +{ + TSharedPtr EntryBoxWidget; + switch (EntryBoxType) + { + case EDynamicBoxType::Horizontal: + EntryBoxWidget = SAssignNew(MyPanelWidget, SHorizontalBox); + break; + case EDynamicBoxType::Vertical: + EntryBoxWidget = SAssignNew(MyPanelWidget, SVerticalBox); + break; + case EDynamicBoxType::Wrap: + EntryBoxWidget = SAssignNew(MyPanelWidget, SWrapBox) + .UseAllottedWidth(true) + .InnerSlotPadding(EntrySpacing); + break; + case EDynamicBoxType::Overlay: + EntryBoxWidget = SAssignNew(MyPanelWidget, SOverlay) + .Clipping(EWidgetClipping::ClipToBounds); + break; + } + + if (!IsDesignTime()) + { + // Populate now with all the entries that have been created so far + for (UUserWidget* ActiveWidget : EntryWidgetPool.GetActiveWidgets()) + { + AddEntryChild(*ActiveWidget); + } + } + + return EntryBoxWidget.ToSharedRef(); +} + +#if WITH_EDITOR +void UDynamicEntryBoxBase::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + if (MyPanelWidget.IsValid() && PropertyChangedEvent.GetPropertyName() == TEXT("EntryBoxType")) + { + MyPanelWidget.Reset(); + } + + Super::PostEditChangeProperty(PropertyChangedEvent); +} +#endif + +void UDynamicEntryBoxBase::SynchronizeProperties() +{ + Super::SynchronizeProperties(); + +#if WITH_EDITORONLY_DATA + if (IsDesignTime()) + { + SetEntrySpacing(EntrySpacing); + } +#endif +} + +UUserWidget* UDynamicEntryBoxBase::CreateEntryInternal(TSubclassOf InEntryClass) +{ + UUserWidget* NewEntryWidget = EntryWidgetPool.GetOrCreateInstance(InEntryClass); + if (MyPanelWidget.IsValid()) + { + // If we've already been constructed, immediately add the child to our panel widget + AddEntryChild(*NewEntryWidget); + } + return NewEntryWidget; +} + +FMargin UDynamicEntryBoxBase::BuildEntryPadding(const FVector2D& DesiredSpacing) +{ + FMargin EntryPadding; + if (DesiredSpacing.X >= 0.f) + { + EntryPadding.Left = DesiredSpacing.X; + } + else + { + EntryPadding.Right = -DesiredSpacing.X; + } + + if (DesiredSpacing.Y >= 0.f) + { + EntryPadding.Top = DesiredSpacing.Y; + } + else + { + EntryPadding.Bottom = -DesiredSpacing.Y; + } + + return EntryPadding; +} + +void UDynamicEntryBoxBase::AddEntryChild(UUserWidget& ChildWidget) +{ + FSlotBase* NewSlot = nullptr; + if (EntryBoxType == EDynamicBoxType::Wrap) + { + NewSlot = &StaticCastSharedPtr(MyPanelWidget)->AddSlot() + .FillEmptySpace(false) + .HAlign(EntryHorizontalAlignment) + .VAlign(EntryVerticalAlignment); + } + else if (EntryBoxType == EDynamicBoxType::Overlay) + { + const int32 ChildIdx = MyPanelWidget->GetChildren()->Num(); + SOverlay::FOverlaySlot& OverlaySlot = (SOverlay::FOverlaySlot&)StaticCastSharedPtr(MyPanelWidget)->AddSlot(); + + EHorizontalAlignment HAlign = EntryHorizontalAlignment; + EVerticalAlignment VAlign = EntryVerticalAlignment; + + FVector2D TargetSpacing = FVector2D::ZeroVector; + if (SpacingPattern.Num() > 0) + { + for (int32 CountIdx = 0; CountIdx < ChildIdx; ++CountIdx) + { + const int32 PatternIdx = CountIdx % SpacingPattern.Num(); + TargetSpacing += SpacingPattern[PatternIdx]; + } + } + else + { + TargetSpacing = EntrySpacing * ChildIdx; + HAlign = EntrySpacing.X >= 0.f ? EHorizontalAlignment::HAlign_Left : EHorizontalAlignment::HAlign_Right; + VAlign = EntrySpacing.Y >= 0.f ? EVerticalAlignment::VAlign_Top : EVerticalAlignment::VAlign_Bottom; + } + + OverlaySlot.HAlignment = HAlign; + OverlaySlot.VAlignment = VAlign; + OverlaySlot.SlotPadding = BuildEntryPadding(TargetSpacing); + + NewSlot = &OverlaySlot; + } + else + { + const bool bIsHBox = EntryBoxType == EDynamicBoxType::Horizontal; + const bool bIsFirstChild = MyPanelWidget->GetChildren()->Num() == 0; + + SBoxPanel::FSlot& BoxPanelSlot = bIsHBox ? (SBoxPanel::FSlot&)StaticCastSharedPtr(MyPanelWidget)->AddSlot().MaxWidth(MaxElementSize) : (SBoxPanel::FSlot&)StaticCastSharedPtr(MyPanelWidget)->AddSlot().MaxHeight(MaxElementSize); + BoxPanelSlot.HAlignment = EntryHorizontalAlignment; + BoxPanelSlot.VAlignment = EntryVerticalAlignment; + BoxPanelSlot.SizeParam = UWidget::ConvertSerializedSizeParamToRuntime(EntrySizeRule); + + FMargin Padding; + Padding.Top = bIsHBox || bIsFirstChild ? 0.f : EntrySpacing.Y; + Padding.Left = bIsHBox && !bIsFirstChild ? EntrySpacing.X : 0.f; + BoxPanelSlot.SlotPadding = Padding; + + NewSlot = &BoxPanelSlot; + } + + if (ensure(NewSlot)) + { + NewSlot->AttachWidget(ChildWidget.TakeWidget()); + } +} + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Engine/Source/Runtime/UMG/Private/Components/EditableText.cpp b/Engine/Source/Runtime/UMG/Private/Components/EditableText.cpp index 7b759043afc9..06d9b0236033 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/EditableText.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/EditableText.cpp @@ -15,8 +15,9 @@ UEditableText::UEditableText(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { - SEditableText::FArguments Defaults; - WidgetStyle = *Defaults._Style; + // HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BY DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS + static const FEditableTextStyle StaticEditableTextStyle = FCoreStyle::Get().GetWidgetStyle< FEditableTextStyle >("NormalEditableText"); + WidgetStyle = StaticEditableTextStyle; ColorAndOpacity_DEPRECATED = FLinearColor::Black; @@ -26,18 +27,17 @@ UEditableText::UEditableText(const FObjectInitializer& ObjectInitializer) Font_DEPRECATED = FSlateFontInfo(RobotoFontObj.Object, 12, FName("Bold")); } - // Grab other defaults from slate arguments. - IsReadOnly = Defaults._IsReadOnly.Get(); - IsPassword = Defaults._IsPassword.Get(); - MinimumDesiredWidth = Defaults._MinDesiredWidth.Get(); - IsCaretMovedWhenGainFocus = Defaults._IsCaretMovedWhenGainFocus.Get(); - SelectAllTextWhenFocused = Defaults._SelectAllTextWhenFocused.Get(); - RevertTextOnEscape = Defaults._RevertTextOnEscape.Get(); - ClearKeyboardFocusOnCommit = Defaults._ClearKeyboardFocusOnCommit.Get(); - SelectAllTextOnCommit = Defaults._SelectAllTextOnCommit.Get(); - AllowContextMenu = Defaults._AllowContextMenu.Get(); - VirtualKeyboardDismissAction = Defaults._VirtualKeyboardDismissAction.Get(); - Clipping = Defaults._Clipping; + IsReadOnly = false; + IsPassword = false; + MinimumDesiredWidth = 0.0f; + IsCaretMovedWhenGainFocus = true; + SelectAllTextWhenFocused = false; + RevertTextOnEscape = false; + ClearKeyboardFocusOnCommit = true; + SelectAllTextOnCommit = false; + AllowContextMenu = true; + VirtualKeyboardDismissAction = EVirtualKeyboardDismissAction::TextChangeOnDismiss; + Clipping = EWidgetClipping::ClipToBounds; } void UEditableText::ReleaseSlateResources(bool bReleaseChildren) diff --git a/Engine/Source/Runtime/UMG/Private/Components/EditableTextBox.cpp b/Engine/Source/Runtime/UMG/Private/Components/EditableTextBox.cpp index 354dfa9a8772..d39b6032d906 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/EditableTextBox.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/EditableTextBox.cpp @@ -24,21 +24,21 @@ UEditableTextBox::UEditableTextBox(const FObjectInitializer& ObjectInitializer) Font_DEPRECATED = FSlateFontInfo(RobotoFontObj.Object, 12, FName("Bold")); } - // Grab other defaults from slate arguments. - SEditableTextBox::FArguments Defaults; - IsReadOnly = Defaults._IsReadOnly.Get(); - IsPassword = Defaults._IsPassword.Get(); - MinimumDesiredWidth = Defaults._MinDesiredWidth.Get(); - Padding_DEPRECATED = Defaults._Padding.Get(); - IsCaretMovedWhenGainFocus = Defaults._IsCaretMovedWhenGainFocus.Get(); - SelectAllTextWhenFocused = Defaults._SelectAllTextWhenFocused.Get(); - RevertTextOnEscape = Defaults._RevertTextOnEscape.Get(); - ClearKeyboardFocusOnCommit = Defaults._ClearKeyboardFocusOnCommit.Get(); - SelectAllTextOnCommit = Defaults._SelectAllTextOnCommit.Get(); - AllowContextMenu = Defaults._AllowContextMenu.Get(); - VirtualKeyboardDismissAction = Defaults._VirtualKeyboardDismissAction.Get(); + IsReadOnly = false; + IsPassword = false; + MinimumDesiredWidth = 0.0f; + Padding_DEPRECATED = FMargin(0, 0, 0, 0); + IsCaretMovedWhenGainFocus = true; + SelectAllTextWhenFocused = false; + RevertTextOnEscape = false; + ClearKeyboardFocusOnCommit = true; + SelectAllTextOnCommit = false; + AllowContextMenu = true; + VirtualKeyboardDismissAction = EVirtualKeyboardDismissAction::TextChangeOnDismiss; - WidgetStyle = *Defaults._Style; + // HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BY DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS + static const FEditableTextBoxStyle StaticNormalEditableTextBox = FCoreStyle::Get().GetWidgetStyle< FEditableTextBoxStyle >("NormalEditableTextBox"); + WidgetStyle = StaticNormalEditableTextBox; } void UEditableTextBox::ReleaseSlateResources(bool bReleaseChildren) diff --git a/Engine/Source/Runtime/UMG/Private/Components/ExpandableArea.cpp b/Engine/Source/Runtime/UMG/Private/Components/ExpandableArea.cpp index 70d6dd943179..c27a7fd465a5 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/ExpandableArea.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/ExpandableArea.cpp @@ -20,12 +20,16 @@ UExpandableArea::UExpandableArea(const FObjectInitializer& ObjectInitializer) { bIsVariable = true; - SExpandableArea::FArguments ExpandableDefaults; - Style = *ExpandableDefaults._Style; - BorderColor = ExpandableDefaults._BorderBackgroundColor.Get( FLinearColor::White ); - BorderBrush = *ExpandableDefaults._BorderImage.Get( FStyleDefaults::GetNoBrush() ); - AreaPadding = ExpandableDefaults._Padding.Get(); - HeaderPadding = ExpandableDefaults._HeaderPadding.Get(); + // HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BY DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS + static const FExpandableAreaStyle StaticExpandableArea = FCoreStyle::Get().GetWidgetStyle("ExpandableArea"); + static const FSlateBrush StaticBorderBrush = *FCoreStyle::Get().GetBrush("ExpandableArea.Border"); + + Style = StaticExpandableArea; + BorderBrush = StaticBorderBrush; + + BorderColor = FLinearColor::White; + AreaPadding = FMargin(1); + HeaderPadding = FMargin(4.0f, 2.0f); } bool UExpandableArea::GetIsExpanded() const diff --git a/Engine/Source/Runtime/UMG/Private/Components/GridPanel.cpp b/Engine/Source/Runtime/UMG/Private/Components/GridPanel.cpp index 825df454f189..cfc5cf54db8f 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/GridPanel.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/GridPanel.cpp @@ -14,9 +14,7 @@ UGridPanel::UGridPanel(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { bIsVariable = false; - - SGridPanel::FArguments Defaults; - Visibility = UWidget::ConvertRuntimeToSerializedVisibility(Defaults._Visibility.Get()); + Visibility = ESlateVisibility::SelfHitTestInvisible; } void UGridPanel::ReleaseSlateResources(bool bReleaseChildren) diff --git a/Engine/Source/Runtime/UMG/Private/Components/HorizontalBox.cpp b/Engine/Source/Runtime/UMG/Private/Components/HorizontalBox.cpp index f45bbbb3cba3..3a87d7d53810 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/HorizontalBox.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/HorizontalBox.cpp @@ -12,9 +12,7 @@ UHorizontalBox::UHorizontalBox(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { bIsVariable = false; - - SHorizontalBox::FArguments Defaults; - Visibility = UWidget::ConvertRuntimeToSerializedVisibility(Defaults._Visibility.Get()); + Visibility = ESlateVisibility::SelfHitTestInvisible; } void UHorizontalBox::ReleaseSlateResources(bool bReleaseChildren) diff --git a/Engine/Source/Runtime/UMG/Private/Components/InputKeySelector.cpp b/Engine/Source/Runtime/UMG/Private/Components/InputKeySelector.cpp index a87e1f4ecad4..f7a6f8c3951e 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/InputKeySelector.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/InputKeySelector.cpp @@ -13,14 +13,17 @@ UInputKeySelector::UInputKeySelector( const FObjectInitializer& ObjectInitializer ) : Super(ObjectInitializer) { - SInputKeySelector::FArguments InputKeySelectorDefaults; - WidgetStyle = *InputKeySelectorDefaults._ButtonStyle; - TextStyle = *InputKeySelectorDefaults._TextStyle; - KeySelectionText = InputKeySelectorDefaults._KeySelectionText; - NoKeySpecifiedText = InputKeySelectorDefaults._NoKeySpecifiedText; - SelectedKey = InputKeySelectorDefaults._SelectedKey.Get(); - bAllowModifierKeys = InputKeySelectorDefaults._AllowModifierKeys; - bAllowGamepadKeys = InputKeySelectorDefaults._AllowGamepadKeys; + // HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BY DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS + static const FButtonStyle StaticButtonStyle = FCoreStyle::Get().GetWidgetStyle("Button"); + static const FTextBlockStyle StaticNormalTextStyle = FCoreStyle::Get().GetWidgetStyle< FTextBlockStyle >("NormalText"); + WidgetStyle = StaticButtonStyle; + TextStyle = StaticNormalTextStyle; + + KeySelectionText = NSLOCTEXT("InputKeySelector", "DefaultKeySelectionText", "..."); + NoKeySpecifiedText = NSLOCTEXT("InputKeySelector", "DefaultEmptyText", "Empty"); + SelectedKey = FInputChord(EKeys::Invalid); + bAllowModifierKeys = true; + bAllowGamepadKeys = false; EscapeKeys.AddUnique(EKeys::Gamepad_Special_Right); // In most (if not all) cases this is going to be the menu button diff --git a/Engine/Source/Runtime/UMG/Private/Components/MultiLineEditableText.cpp b/Engine/Source/Runtime/UMG/Private/Components/MultiLineEditableText.cpp index af5d1dbb990e..7c76311ef6f7 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/MultiLineEditableText.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/MultiLineEditableText.cpp @@ -14,18 +14,20 @@ UMultiLineEditableText::UMultiLineEditableText(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { - SMultiLineEditableText::FArguments Defaults; - WidgetStyle = *Defaults._TextStyle; - bIsReadOnly = Defaults._IsReadOnly.Get(); - SelectAllTextWhenFocused = Defaults._SelectAllTextWhenFocused.Get(); - ClearTextSelectionOnFocusLoss = Defaults._ClearTextSelectionOnFocusLoss.Get(); - RevertTextOnEscape = Defaults._RevertTextOnEscape.Get(); - ClearKeyboardFocusOnCommit = Defaults._ClearKeyboardFocusOnCommit.Get(); - AllowContextMenu = Defaults._AllowContextMenu.Get(); - Clipping = Defaults._Clipping; - VirtualKeyboardDismissAction = Defaults._VirtualKeyboardDismissAction.Get(); - AutoWrapText = true; + // HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BY DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS + static const FTextBlockStyle StaticNormalText = FCoreStyle::Get().GetWidgetStyle("NormalText"); + WidgetStyle = StaticNormalText; + bIsReadOnly = false; + SelectAllTextWhenFocused = false; + ClearTextSelectionOnFocusLoss = true; + RevertTextOnEscape = false; + ClearKeyboardFocusOnCommit = true; + AllowContextMenu = true; + Clipping = EWidgetClipping::ClipToBounds; + VirtualKeyboardDismissAction = EVirtualKeyboardDismissAction::TextChangeOnDismiss; + AutoWrapText = true; + if (!IsRunningDedicatedServer()) { static ConstructorHelpers::FObjectFinder RobotoFontObj(*UWidget::GetDefaultFontName()); diff --git a/Engine/Source/Runtime/UMG/Private/Components/MultiLineEditableTextBox.cpp b/Engine/Source/Runtime/UMG/Private/Components/MultiLineEditableTextBox.cpp index 55100bab1d50..375101dd35b7 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/MultiLineEditableTextBox.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/MultiLineEditableTextBox.cpp @@ -18,12 +18,15 @@ UMultiLineEditableTextBox::UMultiLineEditableTextBox(const FObjectInitializer& O BackgroundColor_DEPRECATED = FLinearColor::White; ReadOnlyForegroundColor_DEPRECATED = FLinearColor::Black; - SMultiLineEditableTextBox::FArguments Defaults; - bIsReadOnly = Defaults._IsReadOnly.Get(); - WidgetStyle = *Defaults._Style; - TextStyle = *Defaults._TextStyle; - AllowContextMenu = Defaults._AllowContextMenu.Get(); - VirtualKeyboardDismissAction = Defaults._VirtualKeyboardDismissAction.Get(); + // HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BY DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS + static const FEditableTextBoxStyle StaticNormalEditableTextBox = FCoreStyle::Get().GetWidgetStyle("NormalEditableTextBox"); + static const FTextBlockStyle StaticNormalText = FCoreStyle::Get().GetWidgetStyle("NormalText"); + WidgetStyle = StaticNormalEditableTextBox; + TextStyle = StaticNormalText; + + bIsReadOnly = false; + AllowContextMenu = true; + VirtualKeyboardDismissAction = EVirtualKeyboardDismissAction::TextChangeOnDismiss; AutoWrapText = true; if (!IsRunningDedicatedServer()) diff --git a/Engine/Source/Runtime/UMG/Private/Components/Overlay.cpp b/Engine/Source/Runtime/UMG/Private/Components/Overlay.cpp index ab361eb678db..467347601225 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/Overlay.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/Overlay.cpp @@ -12,9 +12,7 @@ UOverlay::UOverlay(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { bIsVariable = false; - - SOverlay::FArguments Defaults; - Visibility = UWidget::ConvertRuntimeToSerializedVisibility(Defaults._Visibility.Get()); + Visibility = ESlateVisibility::SelfHitTestInvisible; } void UOverlay::ReleaseSlateResources(bool bReleaseChildren) diff --git a/Engine/Source/Runtime/UMG/Private/Components/ProgressBar.cpp b/Engine/Source/Runtime/UMG/Private/Components/ProgressBar.cpp index 275097135a3a..2880deaddd37 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/ProgressBar.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/ProgressBar.cpp @@ -11,8 +11,10 @@ UProgressBar::UProgressBar(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { - SProgressBar::FArguments SlateDefaults; - WidgetStyle = *SlateDefaults._Style; + // HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BY DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS + static const FProgressBarStyle StaticProgressBar = FCoreStyle::Get().GetWidgetStyle("ProgressBar"); + + WidgetStyle = StaticProgressBar; WidgetStyle.FillImage.TintColor = FLinearColor::White; BarFillType = EProgressBarFillType::LeftToRight; diff --git a/Engine/Source/Runtime/UMG/Private/Components/ScrollBar.cpp b/Engine/Source/Runtime/UMG/Private/Components/ScrollBar.cpp index a89910d6d44f..679a4240ed51 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/ScrollBar.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/ScrollBar.cpp @@ -17,8 +17,9 @@ UScrollBar::UScrollBar(const FObjectInitializer& ObjectInitializer) Orientation = Orient_Vertical; Thickness = FVector2D(12.0f, 12.0f); - SScrollBar::FArguments Defaults; - WidgetStyle = *Defaults._Style; + // HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BY DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS + static const FScrollBarStyle StaticScrollbar = FCoreStyle::Get().GetWidgetStyle("Scrollbar"); + WidgetStyle = StaticScrollbar; } void UScrollBar::ReleaseSlateResources(bool bReleaseChildren) diff --git a/Engine/Source/Runtime/UMG/Private/Components/ScrollBox.cpp b/Engine/Source/Runtime/UMG/Private/Components/ScrollBox.cpp index 02ca0019e21d..0108b6a942c7 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/ScrollBox.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/ScrollBox.cpp @@ -23,12 +23,18 @@ UScrollBox::UScrollBox(const FObjectInitializer& ObjectInitializer) { bIsVariable = false; - SScrollBox::FArguments Defaults; - Visibility = UWidget::ConvertRuntimeToSerializedVisibility(Defaults._Visibility.Get()); + Visibility = ESlateVisibility::Visible; Clipping = EWidgetClipping::ClipToBounds; - WidgetStyle = *Defaults._Style; - WidgetBarStyle = *Defaults._ScrollBarStyle; + { + // HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BY DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS + static const FScrollBoxStyle StaticScrollBoxStyle = FCoreStyle::Get().GetWidgetStyle("ScrollBox"); + static const FScrollBarStyle StaticScrollBarStyle = FCoreStyle::Get().GetWidgetStyle("ScrollBar"); + + WidgetStyle = StaticScrollBoxStyle; + WidgetBarStyle = StaticScrollBarStyle; + } + bAllowRightClickDragScrolling = true; } @@ -263,6 +269,14 @@ void UScrollBox::SetAllowOverscroll(bool NewAllowOverscroll) } } +void UScrollBox::EndInertialScrolling() +{ + if (MyScrollBox.IsValid()) + { + MyScrollBox->EndInertialScrolling(); + } +} + void UScrollBox::SlateHandleUserScrolled(float CurrentOffset) { OnUserScrolled.Broadcast(CurrentOffset); diff --git a/Engine/Source/Runtime/UMG/Private/Components/SizeBoxSlot.cpp b/Engine/Source/Runtime/UMG/Private/Components/SizeBoxSlot.cpp index 129b19f10a97..6a11f69a68d4 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/SizeBoxSlot.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/SizeBoxSlot.cpp @@ -15,8 +15,6 @@ USizeBoxSlot::USizeBoxSlot(const FObjectInitializer& ObjectInitializer) HorizontalAlignment = HAlign_Fill; VerticalAlignment = VAlign_Fill; - - SBox::FArguments SizeBoxDefaults; } void USizeBoxSlot::ReleaseSlateResources(bool bReleaseChildren) diff --git a/Engine/Source/Runtime/UMG/Private/Components/Slider.cpp b/Engine/Source/Runtime/UMG/Private/Components/Slider.cpp index ee38620fcd85..84fc87004901 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/Slider.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/Slider.cpp @@ -16,11 +16,13 @@ USlider::USlider(const FObjectInitializer& ObjectInitializer) SliderBarColor = FLinearColor::White; SliderHandleColor = FLinearColor::White; StepSize = 0.01f; - SSlider::FArguments Defaults; - WidgetStyle = *Defaults._Style; IsFocusable = true; MouseUsesStep = false; RequiresControllerLock = true; + + // HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BY DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS + static const FSliderStyle StaticSlider = FCoreStyle::Get().GetWidgetStyle("Slider"); + WidgetStyle = StaticSlider; } TSharedRef USlider::RebuildWidget() diff --git a/Engine/Source/Runtime/UMG/Private/Components/SpinBox.cpp b/Engine/Source/Runtime/UMG/Private/Components/SpinBox.cpp index a61a58c96bf8..7afd6168fa91 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/SpinBox.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/SpinBox.cpp @@ -18,22 +18,21 @@ USpinBox::USpinBox(const FObjectInitializer& ObjectInitializer) Font = FSlateFontInfo(RobotoFontObj.Object, 12, FName("Bold")); } - // Grab other defaults from slate arguments. - SSpinBox::FArguments Defaults; - - Value = Defaults._Value.Get(); - MinValue = Defaults._MinValue.Get().Get(0.0f); - MaxValue = Defaults._MaxValue.Get().Get(0.0f); - MinSliderValue = Defaults._MinSliderValue.Get().Get(0.0f); - MaxSliderValue = Defaults._MaxSliderValue.Get().Get(0.0f); - Delta = Defaults._Delta.Get(); - SliderExponent = Defaults._SliderExponent.Get(); - MinDesiredWidth = Defaults._MinDesiredWidth.Get(); - ClearKeyboardFocusOnCommit = Defaults._ClearKeyboardFocusOnCommit.Get(); - SelectAllTextOnCommit = Defaults._SelectAllTextOnCommit.Get(); - - WidgetStyle = *Defaults._Style; + Value = 0; + MinValue = 0; + MaxValue = 10; + MinSliderValue = 0; + MaxSliderValue = 0; + Delta = 0; + SliderExponent = 1; + MinDesiredWidth = 0; + ClearKeyboardFocusOnCommit = false; + SelectAllTextOnCommit = true; ForegroundColor = FSlateColor(FLinearColor::Black); + + // HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BY DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS + static const FSpinBoxStyle StaticSpinBox = FCoreStyle::Get().GetWidgetStyle("SpinBox"); + WidgetStyle = StaticSpinBox; } void USpinBox::ReleaseSlateResources(bool bReleaseChildren) diff --git a/Engine/Source/Runtime/UMG/Private/Components/Throbber.cpp b/Engine/Source/Runtime/UMG/Private/Components/Throbber.cpp index 356dd9fd32fd..ac81627df450 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/Throbber.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/Throbber.cpp @@ -12,14 +12,15 @@ UThrobber::UThrobber(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { - SThrobber::FArguments DefaultArgs; - Image = *DefaultArgs._PieceImage; + NumberOfPieces = 3; - NumberOfPieces = DefaultArgs._NumPieces; + bAnimateVertically = true; + bAnimateHorizontally = true; + bAnimateOpacity = true; - bAnimateVertically = (DefaultArgs._Animate & SThrobber::Vertical) != 0; - bAnimateHorizontally = (DefaultArgs._Animate & SThrobber::Horizontal) != 0; - bAnimateOpacity = (DefaultArgs._Animate & SThrobber::Opacity) != 0; + // HACK: THIS SHOULD NOT COME FROM CORESTYLE AND SHOULD INSTEAD BY DEFINED BY ENGINE TEXTURES/PROJECT SETTINGS + static const FSlateBrush StaticImage = *FCoreStyle::Get().GetBrush("Throbber.Chunk"); + Image = StaticImage; } void UThrobber::ReleaseSlateResources(bool bReleaseChildren) @@ -31,8 +32,6 @@ void UThrobber::ReleaseSlateResources(bool bReleaseChildren) TSharedRef UThrobber::RebuildWidget() { - SThrobber::FArguments DefaultArgs; - MyThrobber = SNew(SThrobber) .PieceImage(&Image) .NumPieces(FMath::Clamp(NumberOfPieces, 1, 25)) diff --git a/Engine/Source/Runtime/UMG/Private/Components/UniformGridPanel.cpp b/Engine/Source/Runtime/UMG/Private/Components/UniformGridPanel.cpp index 4003665ba711..fbef7ed702af 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/UniformGridPanel.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/UniformGridPanel.cpp @@ -14,9 +14,7 @@ UUniformGridPanel::UUniformGridPanel(const FObjectInitializer& ObjectInitializer : Super(ObjectInitializer) { bIsVariable = false; - - SUniformGridPanel::FArguments Defaults; - Visibility = UWidget::ConvertRuntimeToSerializedVisibility(Defaults._Visibility.Get()); + Visibility = ESlateVisibility::SelfHitTestInvisible; } void UUniformGridPanel::ReleaseSlateResources(bool bReleaseChildren) diff --git a/Engine/Source/Runtime/UMG/Private/Components/VerticalBox.cpp b/Engine/Source/Runtime/UMG/Private/Components/VerticalBox.cpp index 63dc4f27fe28..544e87dbe9d3 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/VerticalBox.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/VerticalBox.cpp @@ -12,9 +12,7 @@ UVerticalBox::UVerticalBox(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { bIsVariable = false; - - SVerticalBox::FArguments Defaults; - Visibility = UWidget::ConvertRuntimeToSerializedVisibility(Defaults._Visibility.Get()); + Visibility = ESlateVisibility::SelfHitTestInvisible; } void UVerticalBox::ReleaseSlateResources(bool bReleaseChildren) diff --git a/Engine/Source/Runtime/UMG/Private/Components/WidgetComponent.cpp b/Engine/Source/Runtime/UMG/Private/Components/WidgetComponent.cpp index 889f07f131e1..45ebd3f468ab 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/WidgetComponent.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/WidgetComponent.cpp @@ -1175,18 +1175,21 @@ void UWidgetComponent::RemoveWidgetFromScreen() { bAddedToScreen = false; - if ( UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport() ) + if (UWorld* World = GetWorld()) { - TSharedPtr LayerManager = ViewportClient->GetGameLayerManager(); - if ( LayerManager.IsValid() ) + if (UGameViewportClient* ViewportClient = World->GetGameViewport()) { if (ULocalPlayer* TargetPlayer = GetOwnerPlayer()) { - TSharedPtr Layer = LayerManager->FindLayerForPlayer(TargetPlayer, SharedLayerName); - if (Layer.IsValid()) + TSharedPtr LayerManager = ViewportClient->GetGameLayerManager(); + if (LayerManager.IsValid()) { - TSharedPtr ScreenLayer = StaticCastSharedPtr(Layer); - ScreenLayer->RemoveComponent(this); + TSharedPtr Layer = LayerManager->FindLayerForPlayer(TargetPlayer, SharedLayerName); + if (Layer.IsValid()) + { + TSharedPtr ScreenLayer = StaticCastSharedPtr(Layer); + ScreenLayer->RemoveComponent(this); + } } } } diff --git a/Engine/Source/Runtime/UMG/Private/Components/WidgetSwitcher.cpp b/Engine/Source/Runtime/UMG/Private/Components/WidgetSwitcher.cpp index 171b2d9d004f..ce49b060f062 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/WidgetSwitcher.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/WidgetSwitcher.cpp @@ -13,9 +13,7 @@ UWidgetSwitcher::UWidgetSwitcher(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { bIsVariable = true; - - SWidgetSwitcher::FArguments Defaults; - Visibility = UWidget::ConvertRuntimeToSerializedVisibility(Defaults._Visibility.Get()); + Visibility = ESlateVisibility::SelfHitTestInvisible; } void UWidgetSwitcher::ReleaseSlateResources(bool bReleaseChildren) diff --git a/Engine/Source/Runtime/UMG/Private/Components/WindowTitleBarArea.cpp b/Engine/Source/Runtime/UMG/Private/Components/WindowTitleBarArea.cpp index c9adaf549da0..f5c1f132d3b7 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/WindowTitleBarArea.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/WindowTitleBarArea.cpp @@ -19,9 +19,7 @@ UWindowTitleBarArea::UWindowTitleBarArea(const FObjectInitializer& ObjectInitial : Super(ObjectInitializer) { bIsVariable = false; - - SWindowTitleBarArea::FArguments Defaults; - Visibility = UWidget::ConvertRuntimeToSerializedVisibility(Defaults._Visibility.Get()); + Visibility = ESlateVisibility::Visible; bDoubleClickTogglesFullscreen = false; } diff --git a/Engine/Source/Runtime/UMG/Private/Components/WrapBox.cpp b/Engine/Source/Runtime/UMG/Private/Components/WrapBox.cpp index dea290d290d5..78838c065f79 100644 --- a/Engine/Source/Runtime/UMG/Private/Components/WrapBox.cpp +++ b/Engine/Source/Runtime/UMG/Private/Components/WrapBox.cpp @@ -12,10 +12,7 @@ UWrapBox::UWrapBox(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { bIsVariable = false; - - SWrapBox::FArguments Defaults; - Visibility = UWidget::ConvertRuntimeToSerializedVisibility(Defaults._Visibility.Get()); - + Visibility = ESlateVisibility::SelfHitTestInvisible; WrapWidth = 500; bExplicitWrapWidth = false; } diff --git a/Engine/Source/Runtime/UMG/Private/UserWidget.cpp b/Engine/Source/Runtime/UMG/Private/UserWidget.cpp index 85d8f762679f..95445dbe05da 100644 --- a/Engine/Source/Runtime/UMG/Private/UserWidget.cpp +++ b/Engine/Source/Runtime/UMG/Private/UserWidget.cpp @@ -90,36 +90,13 @@ UUserWidget::UUserWidget(const FObjectInitializer& ObjectInitializer) UWidgetBlueprintGeneratedClass* UUserWidget::GetWidgetTreeOwningClass() { - UWidgetBlueprintGeneratedClass* RootBGClass = Cast(GetClass()); - UWidgetBlueprintGeneratedClass* BGClass = RootBGClass; - - while ( BGClass ) + UWidgetBlueprintGeneratedClass* WidgetClass = Cast(GetClass()); + if (WidgetClass != nullptr) { - //TODO NickD: This conditional post load shouldn't be needed any more once the Fast Widget creation path is the only path! - // Force post load on the generated class so all subobjects are done (specifically the widget tree). - BGClass->ConditionalPostLoad(); - - const bool bNoRootWidget = ( nullptr == BGClass->WidgetTree ) || ( nullptr == BGClass->WidgetTree->RootWidget ); - - if ( bNoRootWidget ) - { - UWidgetBlueprintGeneratedClass* SuperBGClass = Cast(BGClass->GetSuperClass()); - if ( SuperBGClass ) - { - BGClass = SuperBGClass; - continue; - } - else - { - // If we reach a super class that isn't a UWidgetBlueprintGeneratedClass, return the root class. - return RootBGClass; - } - } - - return BGClass; + WidgetClass = WidgetClass->FindWidgetTreeOwningClass(); } - return nullptr; + return WidgetClass; } void UUserWidget::TemplateInit() @@ -2170,6 +2147,15 @@ UUserWidget* UUserWidget::CreateInstanceInternal(UObject* Outer, TSubclassOfbIsTearingDown, TEXT("Widget Class %s - Attempting to be created while tearing down the world."), *UserWidgetClass->GetName()); + } +#endif + if (!Outer) { FMessageLog("PIE").Error(FText::Format(LOCTEXT("OuterNull", "Unable to create the widget {0}, no outer provided."), FText::FromName(UserWidgetClass->GetFName()))); diff --git a/Engine/Source/Runtime/UMG/Private/WidgetBlueprintGeneratedClass.cpp b/Engine/Source/Runtime/UMG/Private/WidgetBlueprintGeneratedClass.cpp index 460554af2df5..ccb36ea20460 100644 --- a/Engine/Source/Runtime/UMG/Private/WidgetBlueprintGeneratedClass.cpp +++ b/Engine/Source/Runtime/UMG/Private/WidgetBlueprintGeneratedClass.cpp @@ -133,53 +133,51 @@ void UWidgetBlueprintGeneratedClass::InitializeBindingsStatic(UUserWidget* UserW { ensure(!UserWidget->HasAnyFlags(RF_ArchetypeObject)); - // Iterate all generated classes in the widget's class hierarchy and initialize bindings found on each one. - UClass* CurrentClass = UserWidget->GetClass(); - while (UWidgetBlueprintGeneratedClass* WBPGC = Cast(CurrentClass)) + // Note: It's not safe to assume here that the UserWidget class type is a UWidgetBlueprintGeneratedClass! + // - @see InitializeWidgetStatic() + + // For each property binding that we're given, find the corresponding field, and setup the delegate binding on the widget. + for (const FDelegateRuntimeBinding& Binding : InBindings) { - // For each property binding that we're given, find the corresponding field, and setup the delegate binding on the widget. - for (const FDelegateRuntimeBinding& Binding : WBPGC->Bindings) + // If the binding came from a parent class, this will still find it - FindField() searches the super class hierarchy by default. + UObjectProperty* WidgetProperty = FindField(UserWidget->GetClass(), *Binding.ObjectName); + if (WidgetProperty == nullptr) { - UObjectProperty* WidgetProperty = FindField(WBPGC, *Binding.ObjectName); - if (WidgetProperty == nullptr) + continue; + } + + UWidget* Widget = Cast(WidgetProperty->GetObjectPropertyValue_InContainer(UserWidget)); + + if (Widget) + { + UDelegateProperty* DelegateProperty = FindField(Widget->GetClass(), FName(*(Binding.PropertyName.ToString() + TEXT("Delegate")))); + if (!DelegateProperty) { - continue; + DelegateProperty = FindField(Widget->GetClass(), Binding.PropertyName); } - UWidget* Widget = Cast(WidgetProperty->GetObjectPropertyValue_InContainer(UserWidget)); - - if (Widget) + if (DelegateProperty) { - UDelegateProperty* DelegateProperty = FindField(Widget->GetClass(), FName(*(Binding.PropertyName.ToString() + TEXT("Delegate")))); - if (!DelegateProperty) + bool bSourcePathBound = false; + + if (Binding.SourcePath.IsValid()) { - DelegateProperty = FindField(Widget->GetClass(), Binding.PropertyName); + bSourcePathBound = Widget->AddBinding(DelegateProperty, UserWidget, Binding.SourcePath); } - if (DelegateProperty) + // If no native binder is found then the only possibility is that the binding is for + // a delegate that doesn't match the known native binders available and so we + // fallback to just attempting to bind to the function directly. + if (bSourcePathBound == false) { - bool bSourcePathBound = false; - - if (Binding.SourcePath.IsValid()) + FScriptDelegate* ScriptDelegate = DelegateProperty->GetPropertyValuePtr_InContainer(Widget); + if (ScriptDelegate) { - bSourcePathBound = Widget->AddBinding(DelegateProperty, UserWidget, Binding.SourcePath); - } - - // If no native binder is found then the only possibility is that the binding is for - // a delegate that doesn't match the known native binders available and so we - // fallback to just attempting to bind to the function directly. - if (bSourcePathBound == false) - { - FScriptDelegate* ScriptDelegate = DelegateProperty->GetPropertyValuePtr_InContainer(Widget); - if (ScriptDelegate) - { - ScriptDelegate->BindUFunction(UserWidget, Binding.FunctionName); - } + ScriptDelegate->BindUFunction(UserWidget, Binding.FunctionName); } } } } - CurrentClass = CurrentClass->GetSuperClass(); } } @@ -193,6 +191,10 @@ void UWidgetBlueprintGeneratedClass::InitializeWidgetStatic(UUserWidget* UserWid { check(InClass); + // Note: It's not safe to assume here that the UserWidget class type is a UWidgetBlueprintGeneratedClass! In the case of a nativized widget + // blueprint class, it will be a UDynamicClass instead, and this API will be invoked by the blueprint's C++ code that's generated at cook time. + // - @see FBackendHelperUMG::EmitWidgetInitializationFunctions() + if ( UserWidget->HasAllFlags(RF_ArchetypeObject) ) { UE_LOG(LogUMG, Error, TEXT("Widget Class %s - Running Initialize On Archetype, %s."), *InClass->GetName(), *UserWidget->GetName()); @@ -316,25 +318,20 @@ void UWidgetBlueprintGeneratedClass::InitializeWidgetStatic(UUserWidget* UserWid void UWidgetBlueprintGeneratedClass::BindAnimations(UUserWidget* Instance, const TArray< UWidgetAnimation* >& InAnimations) { - // Iterate all generated classes in the widget's class hierarchy and initialize each animation property with the animation in the class. + // Note: It's not safe to assume here that the UserWidget class type is a UWidgetBlueprintGeneratedClass! + // - @see InitializeWidgetStatic() - // @todo: this will not work when nativizing widget blueprints if support for them is ever added - a more complete solution will be needed in that case - UClass* CurrentClass = Instance->GetClass(); - while (UWidgetBlueprintGeneratedClass* WBPGC = Cast(CurrentClass)) + for (UWidgetAnimation* Animation : InAnimations) { - for (UWidgetAnimation* Animation : WBPGC->Animations) + if (Animation->GetMovieScene()) { - if (Animation->GetMovieScene()) + // Find property with the same name as the animation and assign the animation to it. + UObjectPropertyBase* Prop = FindField(Instance->GetClass(), Animation->GetMovieScene()->GetFName()); + if (Prop) { - // Find property with the same name as the animation and assign the animation to it. - UObjectPropertyBase* Prop = FindField(WBPGC, Animation->GetMovieScene()->GetFName()); - if (Prop) - { - Prop->SetObjectPropertyValue_InContainer(Instance, Animation); - } + Prop->SetObjectPropertyValue_InContainer(Instance, Animation); } } - CurrentClass = CurrentClass->GetSuperClass(); } } @@ -347,7 +344,26 @@ void UWidgetBlueprintGeneratedClass::SetClassRequiresNativeTick(bool InClassRequ void UWidgetBlueprintGeneratedClass::InitializeWidget(UUserWidget* UserWidget) const { - InitializeWidgetStatic(UserWidget, this, HasTemplate(), bAllowDynamicCreation, WidgetTree, Animations, Bindings); + TArray AllAnims; + TArray AllBindings; + + // Include current class animations. + AllAnims.Append(Animations); + + // Include current class bindings. + AllBindings.Append(Bindings); + + // Iterate all generated classes in the widget's parent class hierarchy and include animations and bindings found on each one. + UClass* SuperClass = GetSuperClass(); + while (UWidgetBlueprintGeneratedClass* WBPGC = Cast(SuperClass)) + { + AllAnims.Append(WBPGC->Animations); + AllBindings.Append(WBPGC->Bindings); + + SuperClass = SuperClass->GetSuperClass(); + } + + InitializeWidgetStatic(UserWidget, this, HasTemplate(), bAllowDynamicCreation, WidgetTree, AllAnims, AllBindings); } void UWidgetBlueprintGeneratedClass::PostLoad() @@ -626,4 +642,38 @@ void UWidgetBlueprintGeneratedClass::InitializeTemplate(const ITargetPlatform* T #endif } +UWidgetBlueprintGeneratedClass* UWidgetBlueprintGeneratedClass::FindWidgetTreeOwningClass() +{ + UWidgetBlueprintGeneratedClass* RootBGClass = this; + UWidgetBlueprintGeneratedClass* BGClass = RootBGClass; + + while (BGClass) + { + //TODO NickD: This conditional post load shouldn't be needed any more once the Fast Widget creation path is the only path! + // Force post load on the generated class so all subobjects are done (specifically the widget tree). + BGClass->ConditionalPostLoad(); + + const bool bNoRootWidget = (nullptr == BGClass->WidgetTree) || (nullptr == BGClass->WidgetTree->RootWidget); + + if (bNoRootWidget) + { + UWidgetBlueprintGeneratedClass* SuperBGClass = Cast(BGClass->GetSuperClass()); + if (SuperBGClass) + { + BGClass = SuperBGClass; + continue; + } + else + { + // If we reach a super class that isn't a UWidgetBlueprintGeneratedClass, return the root class. + return RootBGClass; + } + } + + return BGClass; + } + + return nullptr; +} + #undef LOCTEXT_NAMESPACE diff --git a/Engine/Source/Runtime/UMG/Public/Blueprint/UserWidgetPool.h b/Engine/Source/Runtime/UMG/Public/Blueprint/UserWidgetPool.h index 9b620aaa34ca..a0495af2e98c 100644 --- a/Engine/Source/Runtime/UMG/Public/Blueprint/UserWidgetPool.h +++ b/Engine/Source/Runtime/UMG/Public/Blueprint/UserWidgetPool.h @@ -6,29 +6,35 @@ #include "WidgetTree.h" #include "Slate/SObjectWidget.h" -/** - * Pools UUserWidget instances to minimize UObject allocations for UMG elements with dynamic entries. Optionally retains the underlying slate instances of each UUserWidget as well. - * - * Note that if underlying Slate instances are released when a UserWidget instance becomes inactive, NativeConstruct & NativeDestruct will be called when UUserWidget +#include "UserWidgetPool.generated.h" + +/** + * Pools UUserWidget instances to minimize UObject and SWidget allocations for UMG elements with dynamic entries. + * + * Note that if underlying Slate instances are released when a UserWidget instance becomes inactive, NativeConstruct & NativeDestruct will be called when UUserWidget * instances are made active or inactive, respectively, provided the widget isn't actively referenced in the Slate hierarchy (i.e. if the shared reference count on the widget goes from/to 0). * - * WARNING: Be sure to fully reset the pool within the owning widget's ReleaseSlateResources call to prevent leaking due to circular references (since the pool caches hard references to both the UUserWidget and SObjectWidget instances) + * WARNING: Be sure to release the pool's Slate widgets within the owning widget's ReleaseSlateResources call to prevent leaking due to circular references + * Otherwise the cached references to SObjectWidgets will keep the UUserWidgets - and all that they reference - alive * * @see UListView * @see UDynamicEntryBox */ -class UMG_API FUserWidgetPool : public FGCObject +USTRUCT() +struct UMG_API FUserWidgetPool { + GENERATED_BODY(); + public: FUserWidgetPool() = default; FUserWidgetPool(UWidget& InOwningWidget); - FUserWidgetPool& operator=(FUserWidgetPool&& Other); ~FUserWidgetPool(); /** In the case that you don't have an owner widget, you should set a world to your pool, or it won't be able to construct widgets. */ void SetWorld(UWorld* OwningWorld); - virtual void AddReferencedObjects(FReferenceCollector& Collector) override; + /** Report any references to UObjects to the reference collector (only necessary if this is not already a UPROPERTY) */ + void AddReferencedObjects(FReferenceCollector& Collector); bool IsInitialized() const { return OwningWidget.IsValid() || OwningWorld.IsValid(); } const TArray& GetActiveWidgets() const { return ActiveWidgets; } @@ -66,6 +72,9 @@ public: /** Full reset of all created widget objects (and any cached underlying slate) */ void ResetPool(); + /** Reset of all cached underlying Slate widgets, but not the active UUserWidget objects */ + void ReleaseSlateResources(); + private: template UserWidgetT* AddActiveWidgetInternal(TSubclassOf WidgetClass, WidgetConstructFunc ConstructWidgetFunc) @@ -111,10 +120,13 @@ private: return Cast(WidgetInstance); } + UPROPERTY(Transient) + TArray ActiveWidgets; + + UPROPERTY(Transient) + TArray InactiveWidgets; + TWeakObjectPtr OwningWidget; TWeakObjectPtr OwningWorld; - - TArray ActiveWidgets; - TArray InactiveWidgets; TMap> CachedSlateByWidgetObject; }; \ No newline at end of file diff --git a/Engine/Source/Runtime/UMG/Public/Blueprint/WidgetBlueprintGeneratedClass.h b/Engine/Source/Runtime/UMG/Public/Blueprint/WidgetBlueprintGeneratedClass.h index 2961b5aa680d..392e61a2b07f 100644 --- a/Engine/Source/Runtime/UMG/Public/Blueprint/WidgetBlueprintGeneratedClass.h +++ b/Engine/Source/Runtime/UMG/Public/Blueprint/WidgetBlueprintGeneratedClass.h @@ -120,6 +120,9 @@ public: void SetTemplate(UUserWidget* InTemplate); UUserWidget* GetTemplate(); + // Walks up the hierarchy looking for a valid widget tree. + UWidgetBlueprintGeneratedClass* FindWidgetTreeOwningClass(); + // UObject interface virtual void PreSave(const class ITargetPlatform* TargetPlatform) override; virtual void Serialize(FArchive& Ar) override; diff --git a/Engine/Source/Runtime/UMG/Public/Components/DynamicEntryBox.h b/Engine/Source/Runtime/UMG/Public/Components/DynamicEntryBox.h index 8a3b3593d653..c6f209a770d4 100644 --- a/Engine/Source/Runtime/UMG/Public/Components/DynamicEntryBox.h +++ b/Engine/Source/Runtime/UMG/Public/Components/DynamicEntryBox.h @@ -2,22 +2,10 @@ #pragma once -#include "Components/Widget.h" -#include "Blueprint/UserWidgetPool.h" +#include "Components/DynamicEntryBoxBase.h" #include "DynamicEntryBox.generated.h" -class UUserWidget; - -UENUM(BlueprintType) -enum class EDynamicBoxType : uint8 -{ - Horizontal, - Vertical, - Wrap, - Overlay -}; - /** * A special box panel that auto-generates its entries at both design-time and runtime. * Useful for cases where you can have a varying number of entries, but it isn't worth the effort or conceptual overhead to set up a list/tile view. @@ -26,28 +14,20 @@ enum class EDynamicBoxType : uint8 * No children can be manually added in the designer - all are auto-generated based on the given entry class. */ UCLASS() -class UMG_API UDynamicEntryBox : public UWidget +class UMG_API UDynamicEntryBox : public UDynamicEntryBoxBase { GENERATED_BODY() public: - UDynamicEntryBox(const FObjectInitializer& Initializer); - virtual void ReleaseSlateResources(bool bReleaseChildren) override; - - EDynamicBoxType GetBoxType() const { return EntryBoxType; } TSubclassOf GetEntryWidgetClass() const { return EntryWidgetClass; } - const FVector2D& GetEntrySpacing() const { return EntrySpacing; } template WidgetT* CreateEntry(const TSubclassOf& ExplicitEntryClass = nullptr) { - if (ExplicitEntryClass && ExplicitEntryClass->IsChildOf(WidgetT::StaticClass())) + TSubclassOf EntryClass = ExplicitEntryClass ? ExplicitEntryClass : EntryWidgetClass; + if (EntryClass && EntryClass->IsChildOf(WidgetT::StaticClass())) { - return Cast(CreateEntryInternal(ExplicitEntryClass)); - } - if (EntryWidgetClass && EntryWidgetClass->IsChildOf(WidgetT::StaticClass())) - { - return Cast(CreateEntryInternal(EntryWidgetClass)); + return Cast(CreateEntryInternal(EntryClass)); } return nullptr; } @@ -55,7 +35,7 @@ public: template void Reset(TFunctionRef ResetEntryFunc, bool bDeleteWidgets = false) { - for (UUserWidget* EntryWidget : EntryWidgetPool.GetActiveWidgets()) + for (UUserWidget* EntryWidget : GetAllEntries()) { if (WidgetT* TypedEntry = Cast(EntryWidget)) { @@ -70,36 +50,11 @@ public: UFUNCTION(BlueprintCallable, Category = DynamicEntryBox) void Reset(bool bDeleteWidgets = false); - UFUNCTION(BlueprintCallable, Category = DynamicEntryBox) - const TArray& GetAllEntries() const; - - template - TArray GetTypedEntries() const - { - TArray TypedEntries; - for (UUserWidget* Entry : GetAllEntries()) - { - if (EntryWidgetT* TypedEntry = Cast(Entry)) - { - TypedEntries.Add(TypedEntry); - } - } - return TypedEntries; - } - - UFUNCTION(BlueprintCallable, Category = DynamicEntryBox) - int32 GetNumEntries() const; - UFUNCTION(BlueprintCallable, Category = DynamicEntryBox) void RemoveEntry(UUserWidget* EntryWidget); - UFUNCTION(BlueprintCallable, Category = DynamicEntryBox) - void SetEntrySpacing(const FVector2D& InEntrySpacing); - //~ Begin UWidget Interface #if WITH_EDITOR - virtual const FText GetPaletteCategory() override; - virtual void ValidateCompiledDefaults(class IWidgetCompilerLog& CompileLog) const override; #endif //~ End UWidget Interface @@ -116,53 +71,8 @@ public: #endif protected: - virtual TSharedRef RebuildWidget() override; -#if WITH_EDITOR - virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; -#endif virtual void SynchronizeProperties() override; - virtual void AddEntryChild(UUserWidget& ChildWidget); - -protected: - UUserWidget* CreateEntryInternal(TSubclassOf InEntryClass); - - /** The type of box panel into which created entries are added. Some differences in functionality exist between each type. */ - UPROPERTY(EditAnywhere, Category = DynamicEntryBox, meta = (DesignerRebuild)) - EDynamicBoxType EntryBoxType; - - /** - * The padding to apply between entries in the box. - * Note horizontal boxes only use the X and vertical boxes only use Y. Value is also ignored for the first entry in the box. - * Wrap and Overlay types use both X and Y for spacing. - */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = EntryLayout) - FVector2D EntrySpacing; - - //@todo DanH EntryBox: Consider giving a callback option as well/instead. Then this thing could actually create circular or pinwheel layouts... - /** The looping sequence of entry paddings to apply as entries are created. Overlay boxes only. Ignores EntrySpacing if not empty. */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = EntryLayout) - TArray SpacingPattern; - - /** Sizing rule to apply to generated entries. Horizontal/Vertical boxes only. */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = EntryLayout) - FSlateChildSize EntrySizeRule; - - /** Horizontal alignment of generated entries. Horizontal/Vertical/Wrap boxes only. */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = EntryLayout) - TEnumAsByte EntryHorizontalAlignment; - - /** Vertical alignment of generated entries. Horizontal/Vertical/Wrap boxes only. */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = EntryLayout) - TEnumAsByte EntryVerticalAlignment; - - /** The maximum size of each entry in the dominant axis of the box. Vertical/Horizontal boxes only. */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = EntryLayout) - int32 MaxElementSize = 0; - - // Can be a horizontal, vertical, wrap box, or overlay - TSharedPtr MyPanelWidget; - private: /** Creates and establishes a new dynamic entry in the box */ UFUNCTION(BlueprintCallable, Category = DynamicEntryBox, meta = (DisplayName = "Create Entry", AllowPrivateAccess = true)) @@ -172,16 +82,13 @@ private: UFUNCTION(BlueprintCallable, Category = DynamicEntryBox, meta = (DisplayName = "Create Entry of Class", AllowPrivateAccess = true)) UUserWidget* BP_CreateEntryOfClass(TSubclassOf EntryClass); - FMargin BuildEntryPadding(const FVector2D& DesiredSpacing); - /** * The class of widget to create entries of. * If natively binding this widget, use the EntryClass UPROPERTY metadata to specify the desired entry widget base class. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = EntryLayout, meta = (AllowPrivateAccess = true)) TSubclassOf EntryWidgetClass; - FUserWidgetPool EntryWidgetPool; - + // Let the details customization manipulate us directly friend class FDynamicEntryBoxDetails; }; \ No newline at end of file diff --git a/Engine/Source/Runtime/UMG/Public/Components/DynamicEntryBoxBase.h b/Engine/Source/Runtime/UMG/Public/Components/DynamicEntryBoxBase.h new file mode 100644 index 000000000000..380a75d85e73 --- /dev/null +++ b/Engine/Source/Runtime/UMG/Public/Components/DynamicEntryBoxBase.h @@ -0,0 +1,138 @@ +// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "Components/Widget.h" +#include "Blueprint/UserWidgetPool.h" + +#include "DynamicEntryBoxBase.generated.h" + +class UUserWidget; + +UENUM(BlueprintType) +enum class EDynamicBoxType : uint8 +{ + Horizontal, + Vertical, + Wrap, + Overlay +}; + +/** + * Base for widgets that support a dynamic number of auto-generated entries at both design- and run-time. + * Contains all functionality needed to create, construct, and cache an arbitrary number of entry widgets, but exposes no means of entry creation or removal + * It's up to child classes to decide how they want to perform the population (some may do so entirely on their own without exposing a thing) + * + * @see UDynamicEntryBox for a ready-to-use version + */ +UCLASS(Abstract) +class UMG_API UDynamicEntryBoxBase : public UWidget +{ + GENERATED_BODY() + +public: + UDynamicEntryBoxBase(const FObjectInitializer& Initializer); + virtual void ReleaseSlateResources(bool bReleaseChildren) override; + + EDynamicBoxType GetBoxType() const { return EntryBoxType; } + const FVector2D& GetEntrySpacing() const { return EntrySpacing; } + + UFUNCTION(BlueprintCallable, Category = DynamicEntryBox) + const TArray& GetAllEntries() const; + + template + TArray GetTypedEntries() const + { + TArray TypedEntries; + for (UUserWidget* Entry : GetAllEntries()) + { + if (EntryWidgetT* TypedEntry = Cast(Entry)) + { + TypedEntries.Add(TypedEntry); + } + } + return TypedEntries; + } + + UFUNCTION(BlueprintCallable, Category = DynamicEntryBox) + int32 GetNumEntries() const; + + UFUNCTION(BlueprintCallable, Category = DynamicEntryBox) + void SetEntrySpacing(const FVector2D& InEntrySpacing); + +protected: + virtual TSharedRef RebuildWidget() override; + virtual void SynchronizeProperties() override; + virtual void AddEntryChild(UUserWidget& ChildWidget); + + UUserWidget* CreateEntryInternal(TSubclassOf InEntryClass); + void RemoveEntryInternal(UUserWidget* EntryWidget); + FMargin BuildEntryPadding(const FVector2D& DesiredSpacing); + + /** Clear out the box entries, optionally deleting the underlying Slate widgets entirely as well. */ + void ResetInternal(bool bDeleteWidgets = false); + + /** Clear out the box entries, executing the provided reset function for each and optionally deleting the underlying Slate widgets entirely as well. */ + template + void ResetInternal(TFunctionRef ResetEntryFunc, bool bDeleteWidgets = false) + { + for (UUserWidget* EntryWidget : EntryWidgetPool.GetActiveWidgets()) + { + if (WidgetT* TypedEntry = Cast(EntryWidget)) + { + ResetEntryFunc(*TypedEntry); + } + } + + ResetInternal(bDeleteWidgets); + } + +#if WITH_EDITOR + virtual const FText GetPaletteCategory() override; + virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; +#endif + +protected: + /** The type of box panel into which created entries are added. Some differences in functionality exist between each type. */ + UPROPERTY(EditAnywhere, Category = DynamicEntryBox, meta = (DesignerRebuild)) + EDynamicBoxType EntryBoxType; + + /** + * The padding to apply between entries in the box. + * Note horizontal boxes only use the X and vertical boxes only use Y. Value is also ignored for the first entry in the box. + * Wrap and Overlay types use both X and Y for spacing. + */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = EntryLayout) + FVector2D EntrySpacing; + + //@todo DanH EntryBox: Consider giving a callback option as well/instead. Then this thing could actually create circular or pinwheel layouts... + /** The looping sequence of entry paddings to apply as entries are created. Overlay boxes only. Ignores EntrySpacing if not empty. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = EntryLayout) + TArray SpacingPattern; + + /** Sizing rule to apply to generated entries. Horizontal/Vertical boxes only. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = EntryLayout) + FSlateChildSize EntrySizeRule; + + /** Horizontal alignment of generated entries. Horizontal/Vertical/Wrap boxes only. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = EntryLayout) + TEnumAsByte EntryHorizontalAlignment; + + /** Vertical alignment of generated entries. Horizontal/Vertical/Wrap boxes only. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = EntryLayout) + TEnumAsByte EntryVerticalAlignment; + + /** The maximum size of each entry in the dominant axis of the box. Vertical/Horizontal boxes only. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = EntryLayout) + int32 MaxElementSize = 0; + + // Can be a horizontal, vertical, wrap box, or overlay + TSharedPtr MyPanelWidget; + +private: + UPROPERTY(Transient) + FUserWidgetPool EntryWidgetPool; + + // Let the details customization manipulate us directly + friend class FDynamicEntryBoxBaseDetails; +}; \ No newline at end of file diff --git a/Engine/Source/Runtime/UMG/Public/Components/ScrollBox.h b/Engine/Source/Runtime/UMG/Public/Components/ScrollBox.h index 34ac1657e737..85d98692f4b6 100644 --- a/Engine/Source/Runtime/UMG/Public/Components/ScrollBox.h +++ b/Engine/Source/Runtime/UMG/Public/Components/ScrollBox.h @@ -97,6 +97,11 @@ public: UFUNCTION(BlueprintCallable, Category = "Scroll") void SetAllowOverscroll(bool NewAllowOverscroll); + + /** Instantly stops any inertial scrolling that is currently in progress */ + UFUNCTION(BlueprintCallable, Category = "Scroll") + void EndInertialScrolling(); + public: /** Called when the scroll has changed */ diff --git a/Engine/Source/Runtime/VulkanRHI/Private/VulkanCommandBuffer.h b/Engine/Source/Runtime/VulkanRHI/Private/VulkanCommandBuffer.h index 1e34877b6762..6744b7482ec8 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/VulkanCommandBuffer.h +++ b/Engine/Source/Runtime/VulkanRHI/Private/VulkanCommandBuffer.h @@ -63,6 +63,11 @@ public: return State == EState::Submitted; } + inline bool IsAllocated() const + { + return State != EState::NotAllocated; + } + inline VkCommandBuffer GetHandle() { return CommandBufferHandle; @@ -169,14 +174,8 @@ public: bool AcquirePoolSetAndDescriptorsIfNeeded(const class FVulkanDescriptorSetsLayout& Layout, bool bNeedDescriptors, VkDescriptorSet* OutDescriptors); - /*TRefCountPtr<*/FVulkanTimestampQueryPool* /*>*/ PrepareTimestampQueryPool() - { - return TimestampQueryPool; - } - private: FVulkanDevice* Device; - /*TRefCountPtr<*/FVulkanTimestampQueryPool* /*>*/ TimestampQueryPool = nullptr; VkCommandBuffer CommandBufferHandle; double SubmittedTime = 0.0f; diff --git a/Engine/Source/Runtime/VulkanRHI/Private/VulkanCommands.cpp b/Engine/Source/Runtime/VulkanRHI/Private/VulkanCommands.cpp index 8a3f603645c0..bf7df8463ab2 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/VulkanCommands.cpp +++ b/Engine/Source/Runtime/VulkanRHI/Private/VulkanCommands.cpp @@ -631,7 +631,8 @@ inline void FVulkanCommandListContext::SetShaderUniformBuffer(ShaderStage::EStag } else { - PendingGfxState->SetUniformBufferConstantData(Stage, BufferIndex, UniformBuffer->ConstantData); + const FVulkanEmulatedUniformBuffer* EmulatedUniformBuffer = static_cast(UniformBuffer); + PendingGfxState->SetUniformBufferConstantData(Stage, BufferIndex, EmulatedUniformBuffer->ConstantData); } } @@ -734,7 +735,8 @@ void FVulkanCommandListContext::RHISetShaderUniformBuffer(FComputeShaderRHIParam } else { - State.SetUniformBufferConstantData(BufferIndex, UniformBuffer->ConstantData); + const FVulkanEmulatedUniformBuffer* EmulatedUniformBuffer = static_cast(UniformBuffer); + State.SetUniformBufferConstantData(BufferIndex, EmulatedUniformBuffer->ConstantData); } } diff --git a/Engine/Source/Runtime/VulkanRHI/Private/VulkanDescriptorSets.h b/Engine/Source/Runtime/VulkanRHI/Private/VulkanDescriptorSets.h index 85d32aac097b..990d2984b82a 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/VulkanDescriptorSets.h +++ b/Engine/Source/Runtime/VulkanRHI/Private/VulkanDescriptorSets.h @@ -969,12 +969,11 @@ public: bool bChanged = false; if (UseVulkanDescriptorCache()) { - - FVulkanHashableDescriptorInfo& HeshableInfo = HashableDescriptorInfos[DescriptorIndex]; + FVulkanHashableDescriptorInfo& HashableInfo = HashableDescriptorInfos[DescriptorIndex]; check(Sampler.SamplerId > 0); - if (HeshableInfo.Image.SamplerId != Sampler.SamplerId) + if (HashableInfo.Image.SamplerId != Sampler.SamplerId) { - HeshableInfo.Image.SamplerId = Sampler.SamplerId; + HashableInfo.Image.SamplerId = Sampler.SamplerId; ImageInfo->sampler = Sampler.Sampler; bChanged = true; } @@ -1050,23 +1049,23 @@ protected: bool bChanged = false; if (UseVulkanDescriptorCache()) { - FVulkanHashableDescriptorInfo& HeshableInfo = HashableDescriptorInfos[DescriptorIndex]; + FVulkanHashableDescriptorInfo& HashableInfo = HashableDescriptorInfos[DescriptorIndex]; check(BufferAllocation.GetHandleId() > 0); - if (HeshableInfo.Buffer.Id != BufferAllocation.GetHandleId()) + if (HashableInfo.Buffer.Id != BufferAllocation.GetHandleId()) { - HeshableInfo.Buffer.Id = BufferAllocation.GetHandleId(); + HashableInfo.Buffer.Id = BufferAllocation.GetHandleId(); BufferInfo->buffer = BufferAllocation.GetHandle(); bChanged = true; } - if (HeshableInfo.Buffer.Offset != static_cast(Offset)) + if (HashableInfo.Buffer.Offset != static_cast(Offset)) { - HeshableInfo.Buffer.Offset = static_cast(Offset); + HashableInfo.Buffer.Offset = static_cast(Offset); BufferInfo->offset = Offset; bChanged = true; } - if (HeshableInfo.Buffer.Range != static_cast(Range)) + if (HashableInfo.Buffer.Range != static_cast(Range)) { - HeshableInfo.Buffer.Range = static_cast(Range); + HashableInfo.Buffer.Range = static_cast(Range); BufferInfo->range = Range; bChanged = true; } @@ -1105,17 +1104,17 @@ protected: bool bChanged = false; if (UseVulkanDescriptorCache()) { - FVulkanHashableDescriptorInfo& HeshableInfo = HashableDescriptorInfos[DescriptorIndex]; + FVulkanHashableDescriptorInfo& HashableInfo = HashableDescriptorInfos[DescriptorIndex]; check(TextureView.ViewId > 0); - if (HeshableInfo.Image.ImageViewId != TextureView.ViewId) + if (HashableInfo.Image.ImageViewId != TextureView.ViewId) { - HeshableInfo.Image.ImageViewId = TextureView.ViewId; + HashableInfo.Image.ImageViewId = TextureView.ViewId; ImageInfo->imageView = TextureView.View; bChanged = true; } - if (HeshableInfo.Image.ImageLayout != static_cast(Layout)) + if (HashableInfo.Image.ImageLayout != static_cast(Layout)) { - HeshableInfo.Image.ImageLayout = static_cast(Layout); + HashableInfo.Image.ImageLayout = static_cast(Layout); ImageInfo->imageLayout = Layout; bChanged = true; } @@ -1141,11 +1140,11 @@ protected: if (UseVulkanDescriptorCache()) { bool bChanged = false; - FVulkanHashableDescriptorInfo& HeshableInfo = HashableDescriptorInfos[DescriptorIndex]; + FVulkanHashableDescriptorInfo& HashableInfo = HashableDescriptorInfos[DescriptorIndex]; check(View->ViewId > 0); - if (HeshableInfo.BufferView.Id != View->ViewId) + if (HashableInfo.BufferView.Id != View->ViewId) { - HeshableInfo.BufferView.Id = View->ViewId; + HashableInfo.BufferView.Id = View->ViewId; bChanged = true; } bIsKeyDirty |= bChanged; diff --git a/Engine/Source/Runtime/VulkanRHI/Private/VulkanDevice.cpp b/Engine/Source/Runtime/VulkanRHI/Private/VulkanDevice.cpp index 215d7cf2da87..9fefb3dc3b98 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/VulkanDevice.cpp +++ b/Engine/Source/Runtime/VulkanRHI/Private/VulkanDevice.cpp @@ -968,15 +968,11 @@ void FVulkanDevice::Destroy() delete Pool; } FreeOcclusionQueryPools.SetNum(0, false); -/* - delete TimestampQueryPool; -*/ delete PipelineStateCache; PipelineStateCache = nullptr; StagingManager.Deinit(); - if (GGPUCrashDebuggingEnabled) { #if VULKAN_SUPPORTS_AMD_BUFFER_MARKER @@ -998,11 +994,11 @@ void FVulkanDevice::Destroy() #endif } - ResourceHeapManager.Deinit(); - FRHIResource::FlushPendingDeletes(); DeferredDeletionQueue.Clear(); + ResourceHeapManager.Deinit(); + delete TransferQueue; delete ComputeQueue; delete GfxQueue; diff --git a/Engine/Source/Runtime/VulkanRHI/Private/VulkanDevice.h b/Engine/Source/Runtime/VulkanRHI/Private/VulkanDevice.h index a0855bebd499..2b62e9336188 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/VulkanDevice.h +++ b/Engine/Source/Runtime/VulkanRHI/Private/VulkanDevice.h @@ -173,6 +173,11 @@ public: return MemoryManager; } + inline const VkPhysicalDeviceMemoryProperties& GetDeviceMemoryProperties() const + { + return MemoryManager.GetMemoryProperties(); + } + inline VulkanRHI::FResourceHeapManager& GetResourceHeapManager() { return ResourceHeapManager; @@ -299,6 +304,8 @@ public: VkSamplerYcbcrConversion CreateSamplerColorConversion(const VkSamplerYcbcrConversionCreateInfo& CreateInfo); #endif + void* Hotfix; + private: void MapFormatSupport(EPixelFormat UEFormat, VkFormat VulkanFormat); void MapFormatSupportWithFallback(EPixelFormat UEFormat, VkFormat VulkanFormat, TArrayView FallbackTextureFormats); diff --git a/Engine/Source/Runtime/VulkanRHI/Private/VulkanGenericPlatform.h b/Engine/Source/Runtime/VulkanRHI/Private/VulkanGenericPlatform.h index 8200e6b3f37e..5b0095e416bc 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/VulkanGenericPlatform.h +++ b/Engine/Source/Runtime/VulkanRHI/Private/VulkanGenericPlatform.h @@ -13,7 +13,7 @@ class FVulkanGenericPlatform { public: static bool IsSupported() { return true; } - static void CheckDeviceDriver(uint32 DeviceIndex) {} + static void CheckDeviceDriver(uint32 DeviceIndex, const VkPhysicalDeviceProperties& Props) {} static bool LoadVulkanLibrary() { return true; } static bool LoadVulkanInstanceFunctions(VkInstance inInstance) { return true; } diff --git a/Engine/Source/Runtime/VulkanRHI/Private/VulkanLayers.cpp b/Engine/Source/Runtime/VulkanRHI/Private/VulkanLayers.cpp index 9290d93ce3b2..c975db891b7e 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/VulkanLayers.cpp +++ b/Engine/Source/Runtime/VulkanRHI/Private/VulkanLayers.cpp @@ -310,7 +310,12 @@ void FVulkanDynamicRHI::GetInstanceLayersAndExtensions(TArray& #endif // VULKAN_ENABLE_API_DUMP int32 VulkanValidationOption = GValidationCvar.GetValueOnAnyThread(); - if (FParse::Value(FCommandLine::Get(), TEXT("vulkanvalidation="), VulkanValidationOption)) + if (FParse::Param(FCommandLine::Get(), TEXT("vulkandebug"))) + { + // Match D3D and GL + GValidationCvar->Set(2, ECVF_SetByCommandline); + } + else if (FParse::Value(FCommandLine::Get(), TEXT("vulkanvalidation="), VulkanValidationOption)) { GValidationCvar->Set(VulkanValidationOption, ECVF_SetByCommandline); } diff --git a/Engine/Source/Runtime/VulkanRHI/Private/VulkanMemory.cpp b/Engine/Source/Runtime/VulkanRHI/Private/VulkanMemory.cpp index f0f6630cb331..b8c20773f0cf 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/VulkanMemory.cpp +++ b/Engine/Source/Runtime/VulkanRHI/Private/VulkanMemory.cpp @@ -15,6 +15,10 @@ uint32 GVulkanRHIDeletionFrameNumber = 0; const uint32 NUM_FRAMES_TO_WAIT_FOR_RESOURCE_DELETE = 2; +#if UE_BUILD_DEBUG +RENDERCORE_API void DumpRenderTargetPoolMemory(FOutputDevice& OutputDevice); +#endif + #if VULKAN_MEMORY_TRACK_CALLSTACK static FCriticalSection GStackTraceMutex; static char GStackTrace[65536]; @@ -209,8 +213,59 @@ namespace VulkanRHI check(!DedicatedAllocateInfo); #endif + VkDeviceMemory Handle; + VkResult Result = VulkanRHI::vkAllocateMemory(DeviceHandle, &Info, VULKAN_CPU_ALLOCATOR, &Handle); + + //FPlatformMisc::LowLevelOutputDebugStringf(TEXT("(%u)(%x) VulkanRHI::vkAllocateMemory size=%u(%6.3fmb) Type=%u\n"), + // GFrameNumberRenderThread, this, AllocationSize, AllocationSize / (1024.f * 1024.f), MemoryTypeIndex); + + if (Result == VK_ERROR_OUT_OF_DEVICE_MEMORY) + { +#if UE_BUILD_DEBUG + DumpMemory(); + GLog->PanicFlushThreadedLogs(); + DumpRenderTargetPoolMemory(*GLog); + GLog->PanicFlushThreadedLogs(); +#endif + if (bCanFail) + { + UE_LOG(LogVulkanRHI, Warning, TEXT("Failed to allocate Device Memory, Requested=%.2fKb MemTypeIndex=%d"), (float)Info.allocationSize / 1024.0f, Info.memoryTypeIndex); + return nullptr; + } + auto Callback = [=]() + { +#if UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT + DumpMemory(); + GLog->PanicFlushThreadedLogs(); +#endif + return FString::Printf(TEXT("Out of Device Memory, Requested=%.2fKb MemTypeIndex=%d"), (float)Info.allocationSize / 1024.0f, Info.memoryTypeIndex); + }; + UE_LOG(LogVulkanRHI, Fatal, TEXT("%s"), *(Callback())); + } + else if (Result == VK_ERROR_OUT_OF_HOST_MEMORY) + { + if (bCanFail) + { + UE_LOG(LogVulkanRHI, Warning, TEXT("Failed to allocate Host Memory, Requested=%.2fKb MemTypeIndex=%d"), (float)Info.allocationSize / 1024.0f, Info.memoryTypeIndex); + return nullptr; + } + auto Callback = [=]() + { +#if UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT + DumpMemory(); + GLog->PanicFlushThreadedLogs(); +#endif + return FString::Printf(TEXT("Out of Host Memory, Requested=%.2fKb MemTypeIndex=%d"), (float)Info.allocationSize / 1024.0f, Info.memoryTypeIndex); + }; + UE_LOG(LogVulkanRHI, Error, TEXT("%s"), *(Callback())); + } + else + { + VERIFYVULKANRESULT(Result); + } FDeviceMemoryAllocation* NewAllocation = new FDeviceMemoryAllocation; NewAllocation->DeviceHandle = DeviceHandle; + NewAllocation->Handle = Handle; NewAllocation->Size = AllocationSize; NewAllocation->MemoryTypeIndex = MemoryTypeIndex; NewAllocation->bCanBeMapped = ((MemoryProperties.memoryTypes[MemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); @@ -225,49 +280,8 @@ namespace VulkanRHI #if VULKAN_MEMORY_TRACK_CALLSTACK CaptureCallStack(NewAllocation->Callstack); #endif - VkResult Result = VulkanRHI::vkAllocateMemory(DeviceHandle, &Info, VULKAN_CPU_ALLOCATOR, &NewAllocation->Handle); - - //FPlatformMisc::LowLevelOutputDebugStringf(TEXT("(%u)(%x) VulkanRHI::vkAllocateMemory size=%u Type=%u"), - // GFrameNumberRenderThread, this, AllocationSize, MemoryTypeIndex); - if (Result == VK_ERROR_OUT_OF_DEVICE_MEMORY) - { - if (bCanFail) - { - UE_LOG(LogVulkanRHI, Warning, TEXT("Failed to allocate Device Memory, Requested=%fKb MemTypeIndex=%d"), (float)Info.allocationSize / 1024.0f, Info.memoryTypeIndex); - return nullptr; - } - auto Callback = [=]() - { -#if UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT - DumpMemory(); - GLog->PanicFlushThreadedLogs(); -#endif - return FString::Printf(TEXT("Out of Device Memory, Requested=%fKb MemTypeIndex=%d"), (float)Info.allocationSize / 1024.0f, Info.memoryTypeIndex); - }; - UE_LOG(LogVulkanRHI, Fatal, TEXT("%s"), *(Callback())); - } - else if (Result == VK_ERROR_OUT_OF_HOST_MEMORY) - { - if (bCanFail) - { - UE_LOG(LogVulkanRHI, Warning, TEXT("Failed to allocate Host Memory, Requested=%fKb MemTypeIndex=%d"), (float)Info.allocationSize / 1024.0f, Info.memoryTypeIndex); - return nullptr; - } - auto Callback = [=]() - { -#if UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT - DumpMemory(); - GLog->PanicFlushThreadedLogs(); -#endif - return FString::Printf(TEXT("Out of Host Memory, Requested=%fKb MemTypeIndex=%d"), (float)Info.allocationSize / 1024.0f, Info.memoryTypeIndex); - }; - UE_LOG(LogVulkanRHI, Error, TEXT("%s"), *(Callback())); - } - else - { - VERIFYVULKANRESULT(Result); - } + ++NumAllocations; PeakNumAllocations = FMath::Max(NumAllocations, PeakNumAllocations); @@ -333,14 +347,16 @@ namespace VulkanRHI { FDeviceMemoryAllocation* Allocation = HeapInfo.Allocations[SubIndex]; #if VULKAN_MEMORY_TRACK_FILE_LINE - UE_LOG(LogVulkanRHI, Display, TEXT("\t\t%d Size %d Handle %p ID %d %s(%d)"), SubIndex, Allocation->Size, (void*)Allocation->Handle, Allocation->UID, ANSI_TO_TCHAR(Allocation->File), Allocation->Line); + UE_LOG(LogVulkanRHI, Display, TEXT("\t\t%d Size %.2f MB %2.f Handle %p ID %d %s(%d)"), SubIndex, Allocation->Size / 1024.f / 1024.f, TotalSize / 1024.0f / 1024.0f, (void*)Allocation->Handle, Allocation->UID, ANSI_TO_TCHAR(Allocation->File), Allocation->Line); #else - UE_LOG(LogVulkanRHI, Display, TEXT("\t\t%d Size %d Handle %p"), SubIndex, Allocation->Size, (void*)Allocation->Handle); + UE_LOG(LogVulkanRHI, Display, TEXT("\t\t%d Size %.2f MB %2.f Handle %p"), SubIndex, Allocation->Size / 1024.f / 1024.f, TotalSize / 1024.0f / 1024.0f, (void*)Allocation->Handle); #endif TotalSize += Allocation->Size; } UE_LOG(LogVulkanRHI, Display, TEXT("\t\tTotal Allocated %.2f MB, Peak %.2f MB"), TotalSize / 1024.0f / 1024.0f, HeapInfo.PeakSize / 1024.0f / 1024.0f); } + Device->GetResourceHeapManager().DumpMemory(); + GLog->PanicFlushThreadedLogs(); } #endif @@ -811,10 +827,15 @@ namespace VulkanRHI SubAllocAllocatedMemory += UsedPages[Index]->MaxSize; NumSuballocations += UsedPages[Index]->ResourceAllocations.Num(); - UE_LOG(LogVulkanRHI, Display, TEXT("\t\t%d: ID %4d %4d suballocs, %4d free chunks (%d used/%d free/%d max) DeviceMemory %p"), Index, UsedPages[Index]->GetID(), UsedPages[Index]->ResourceAllocations.Num(), UsedPages[Index]->FreeList.Num(), UsedPages[Index]->UsedSize, UsedPages[Index]->MaxSize - UsedPages[Index]->UsedSize, UsedPages[Index]->MaxSize, (void*)UsedPages[Index]->DeviceMemoryAllocation->GetHandle()); + UE_LOG(LogVulkanRHI, Display, TEXT("\t\t%d: ID %4d %4d suballocs, %4d free chunks (%6.2fmb used/%6.2fmb free/%6.2fmb max) DeviceMemory %p"), Index, UsedPages[Index]->GetID(), UsedPages[Index]->ResourceAllocations.Num(), + UsedPages[Index]->FreeList.Num(), + UsedPages[Index]->UsedSize / (1024.f*1024.f), + (UsedPages[Index]->MaxSize - UsedPages[Index]->UsedSize) / (1024.f*1024.f), + UsedPages[Index]->MaxSize / (1024.f*1024.f), + (void*)UsedPages[Index]->DeviceMemoryAllocation->GetHandle()); } - UE_LOG(LogVulkanRHI, Display, TEXT("%d Suballocations for Used/Total: %d/%d = %.2f%%"), NumSuballocations, SubAllocUsedMemory, SubAllocAllocatedMemory, SubAllocAllocatedMemory > 0 ? 100.0f * (float)SubAllocUsedMemory / (float)SubAllocAllocatedMemory : 0.0f); + UE_LOG(LogVulkanRHI, Display, TEXT("%d Suballocations for Used/Total: %.2fmb/%.2fmb = %.2f%%"), NumSuballocations, SubAllocUsedMemory / (1024.f*1024.f), SubAllocAllocatedMemory / (1024.f*1024.f), SubAllocAllocatedMemory > 0 ? 100.0f * (float)SubAllocUsedMemory / (float)SubAllocAllocatedMemory : 0.0f); }; DumpPages(UsedBufferPages, TEXT("Buffer")); @@ -1361,11 +1382,11 @@ namespace VulkanRHI if (PoolSizeIndex == (int32)EPoolSizes::SizesCount) { - UE_LOG(LogVulkanRHI, Display, TEXT(" Large Alloc Used/Max %d/%d %.2f%%"), _UsedLargeTotal, _AllocLargeTotal, 100.0f * (float)_UsedLargeTotal / (float)_AllocLargeTotal); + UE_LOG(LogVulkanRHI, Display, TEXT(" Large Alloc Used/Max %d/%d %6.2f%%"), _UsedLargeTotal, _AllocLargeTotal, 100.0f * (float)_UsedLargeTotal / (float)_AllocLargeTotal); } else { - UE_LOG(LogVulkanRHI, Display, TEXT(" Binned [%d] Alloc Used/Max %d/%d %.2f%%"), PoolSizes[PoolSizeIndex], _UsedBinnedTotal, _AllocBinnedTotal, 100.0f * (float)_UsedBinnedTotal / (float)_AllocBinnedTotal); + UE_LOG(LogVulkanRHI, Display, TEXT(" Binned [%d] Alloc Used/Max %d/%d %6.2f%%"), PoolSizes[PoolSizeIndex], _UsedBinnedTotal, _AllocBinnedTotal, 100.0f * (float)_UsedBinnedTotal / (float)_AllocBinnedTotal); } } } @@ -1530,9 +1551,9 @@ namespace VulkanRHI for (int32 Index = 0; Index < FreeStagingBuffers.Num(); ++Index) { FFreeEntry& FreeBuffer = FreeStagingBuffers[Index]; - if (FreeBuffer.Buffer->GetSize() == Size && FreeBuffer.Buffer->bCPURead == bCPURead) + if (FreeBuffer.StagingBuffer->GetSize() == Size && FreeBuffer.StagingBuffer->bCPURead == bCPURead) { - FStagingBuffer* Buffer = FreeBuffer.Buffer; + FStagingBuffer* Buffer = FreeBuffer.StagingBuffer; FreeStagingBuffers.RemoveAtSwap(Index, 1, false); UsedStagingBuffers.Add(Buffer); return Buffer; @@ -1620,6 +1641,7 @@ namespace VulkanRHI { FPendingItemsPerCmdBuffer* ItemsForCmdBuffer = FindOrAdd(CmdBuffer); FPendingItemsPerCmdBuffer::FPendingItems* ItemsForFence = ItemsForCmdBuffer->FindOrAddItemsForFence(CmdBuffer->GetFenceSignaledCounterA()); + check(StagingBuffer); ItemsForFence->Resources.Add(StagingBuffer); } else @@ -1661,7 +1683,7 @@ namespace VulkanRHI for (int32 Index = 0; Index < FreeStagingBuffers.Num(); ++Index) { FFreeEntry& Entry = FreeStagingBuffers[Index]; - UE_LOG(LogVulkanRHI, Display, TEXT("%6d %p %p"), Index, (void*)Entry.Buffer->GetHandle(), (void*)Entry.Buffer->ResourceAllocation->GetHandle()); + UE_LOG(LogVulkanRHI, Display, TEXT("%6d %p %p"), Index, (void*)Entry.StagingBuffer->GetHandle(), (void*)Entry.StagingBuffer->ResourceAllocation->GetHandle()); } } #endif @@ -1679,6 +1701,7 @@ namespace VulkanRHI { for (int32 ResourceIndex = 0; ResourceIndex < PendingItems.Resources.Num(); ++ResourceIndex) { + check(PendingItems.Resources[ResourceIndex]); FreeStagingBuffers.Add({PendingItems.Resources[ResourceIndex], GFrameNumberRenderThread}); } @@ -1700,9 +1723,9 @@ namespace VulkanRHI FFreeEntry& Entry = FreeStagingBuffers[Index]; if (bImmediately || Entry.FrameNumber + NUM_FRAMES_TO_WAIT_BEFORE_RELEASING_TO_OS < GFrameNumberRenderThread) { - UsedMemory -= Entry.Buffer->GetSize(); - Entry.Buffer->Destroy(Device); - delete Entry.Buffer; + UsedMemory -= Entry.StagingBuffer->GetSize(); + Entry.StagingBuffer->Destroy(Device); + delete Entry.StagingBuffer; FreeStagingBuffers.RemoveAtSwap(Index, 1, false); } } @@ -1910,7 +1933,7 @@ namespace VulkanRHI { return InEntry.Handle == Entry.Handle; }); - checkf(ExistingEntry == nullptr, TEXT("Attempt to double-delete resource, Type: %d, Handle: %llu"), (int32)Type, Handle); + checkf(ExistingEntry == nullptr, TEXT("Attempt to double-delete resource, FDeferredDeletionQueue::EType: %d, Handle: %llu"), (int32)Type, Handle); #endif Entries.Add(Entry); diff --git a/Engine/Source/Runtime/VulkanRHI/Private/VulkanPipeline.cpp b/Engine/Source/Runtime/VulkanRHI/Private/VulkanPipeline.cpp index e0b6d42dc194..dcd8ec766c37 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/VulkanPipeline.cpp +++ b/Engine/Source/Runtime/VulkanRHI/Private/VulkanPipeline.cpp @@ -1634,7 +1634,9 @@ FVulkanRHIGraphicsPipelineState* FVulkanPipelineStateCacheManager::FindInRuntime for (uint32 Index = 0; Index < PSI.RenderTargetsEnabled; ++Index) { Ar << BlendState.RenderTargets[Index]; - TempEnumValue = PSI.RenderTargetFormats[Index]; + EPixelFormat PixelFormat = (EPixelFormat)PSI.RenderTargetFormats[Index]; + bool bSRGB = (PSI.RenderTargetFlags[Index] & TexCreate_SRGB) == TexCreate_SRGB; + TempEnumValue = UEToVkTextureFormat(PixelFormat, bSRGB); Ar << TempEnumValue; } Ar << BlendState.bUseIndependentRenderTargetBlendStates; diff --git a/Engine/Source/Runtime/VulkanRHI/Private/VulkanQuery.cpp b/Engine/Source/Runtime/VulkanRHI/Private/VulkanQuery.cpp index 00fc0d575849..5ee8890de144 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/VulkanQuery.cpp +++ b/Engine/Source/Runtime/VulkanRHI/Private/VulkanQuery.cpp @@ -32,94 +32,7 @@ constexpr int32 NUM_FRAMES_TO_WAIT_REUSE_POOL = 10; constexpr uint32 NUM_FRAMES_TO_WAIT_RELEASE_POOL = MAX_uint32; // never release #endif -/* -FCriticalSection GOcclusionQueryCS; -const uint32 GMaxLifetimeTimestampQueries = 1024; -FVulkanRenderQuery::FVulkanRenderQuery(FVulkanDevice* Device, ERenderQueryType InQueryType) - : QueryType(InQueryType) -{ - INC_DWORD_STAT(STAT_VulkanNumQueries); -} - -FVulkanRenderQuery::~FVulkanRenderQuery() -{ - DEC_DWORD_STAT(STAT_VulkanNumQueries); -} - -inline void FVulkanRenderQuery::Begin(FVulkanCmdBuffer* InCmdBuffer) -{ - BeginCmdBuffer = InCmdBuffer; - check(State == EState::Reset); - if (QueryType == RQT_Occlusion) - { - check(Pool); - VulkanRHI::vkCmdBeginQuery(InCmdBuffer->GetHandle(), Pool->GetHandle(), QueryIndex, VK_QUERY_CONTROL_PRECISE_BIT); - State = EState::InBegin; - LastPoolReset = Pool->NumResets; - } - else - { - ensureMsgf(0, TEXT("Timer queries do NOT support Begin()!")); - } -} - -inline void FVulkanRenderQuery::End(FVulkanCmdBuffer* InCmdBuffer) -{ - ensure(QueryType != RQT_Occlusion || BeginCmdBuffer == InCmdBuffer); - if (QueryType == RQT_Occlusion) - { - check(State == EState::InBegin); - check(Pool); - VulkanRHI::vkCmdEndQuery(InCmdBuffer->GetHandle(), Pool->GetHandle(), QueryIndex); - check(LastPoolReset == Pool->NumResets); - } - else - { - check(State == EState::Reset); - BeginCmdBuffer = InCmdBuffer; - VulkanRHI::vkCmdWriteTimestamp(InCmdBuffer->GetHandle(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, Pool->GetHandle(), QueryIndex); - LastPoolReset = Pool->NumResets; - } - State = EState::InEnd; -} - -bool FVulkanRenderQuery::GetResult(FVulkanDevice* Device, uint64& OutResult, bool bWait) -{ -FScopeLock ScopeLock(&GOcclusionQueryCS); - if (State == EState::HasResults) - { - OutResult = Result; - return true; - } - - check(Pool); - check(State == EState::InEnd); - if (QueryType == RQT_Occlusion) - { - check(IsInRenderingThread()); - if (((FVulkanOcclusionQueryPool*)Pool)->GetResults(QueryIndex, bWait, Result)) - { - check(LastPoolReset == Pool->NumResets); - State = EState::HasResults; - OutResult = Result; - return true; - } - } - else - { - check(IsInRenderingThread() || IsInRHIThread()); - if (((FVulkanTimestampQueryPool*)Pool)->GetResults(QueryIndex, bWait, Result)) - { - check(LastPoolReset == Pool->NumResets); - State = EState::HasResults; - OutResult = Result; - return true; - } - } - return false; -} -*/ FVulkanQueryPool::FVulkanQueryPool(FVulkanDevice* InDevice, uint32 InMaxQueries, VkQueryType InQueryType) : VulkanRHI::FDeviceChild(InDevice) , QueryPool(VK_NULL_HANDLE) @@ -152,22 +65,6 @@ FVulkanQueryPool::~FVulkanQueryPool() ResetEvent = VK_NULL_HANDLE; } -/* -inline VkResult FVulkanQueryPool::InternalGetQueryPoolResults(uint32 FirstQuery, uint32 NumQueries, VkQueryResultFlags ExtraFlags) -{ - VkResult Result = VulkanRHI::vkGetQueryPoolResults(Device->GetInstanceHandle(), QueryPool, FirstQuery, NumQueries, NumQueries * sizeof(uint64), QueryOutput.GetData() + FirstQuery, sizeof(uint64), VK_QUERY_RESULT_64_BIT | ExtraFlags); - return Result; -} - -inline int32 FVulkanQueryPool::AllocateQuery() -{ - FScopeLock ScopeLock(&GOcclusionQueryCS); - int32 UsedQuery = NumUsedQueries; - ++NumUsedQueries; - checkf((uint32)UsedQuery < MaxQueries, TEXT("Internal error allocating query! UsedQuery=%d NumUsedQueries=%d MaxQueries=%d"), UsedQuery, NumUsedQueries, MaxQueries); - return UsedQuery; -} -*/ bool FVulkanOcclusionQueryPool::CanBeReused() { @@ -275,104 +172,10 @@ bool FVulkanOcclusionQueryPool::InternalTryGetResults(bool bWait) { VERIFYVULKANRESULT(Result); } -/* - FScopeLock ScopeLock(&GOcclusionQueryCS); - check(!bHasResults); - VkResult Result = VK_NOT_READY; - bool bFencePassed = false; - if (CmdBuffer->GetFenceSignaledCounter() > FenceCounter) - { - bFencePassed = true; - Result = InternalGetQueryPoolResults(0); - if (Result == VK_SUCCESS) - { - bHasResults = true; - return true; - } - } - if (Result == VK_NOT_READY) - { - if (bWait) - { - uint32 IdleStart = FPlatformTime::Cycles(); - - SCOPE_CYCLE_COUNTER(STAT_VulkanWaitQuery); - - // We'll do manual wait - double StartTime = FPlatformTime::Seconds(); - - ENamedThreads::Type RenderThread_Local = ENamedThreads::GetRenderThread_Local(); - bool bSuccess = false; - int32 NumLoops = 0; - while (!bSuccess) - { - FPlatformProcess::SleepNoStats(0); - - // pump RHIThread to make sure these queries have actually been submitted to the GPU. - if (IsInActualRenderingThread()) - { - FTaskGraphInterface::Get().ProcessThreadUntilIdle(RenderThread_Local); - } - - Result = InternalGetQueryPoolResults(0); - if (Result == VK_SUCCESS) - { - bSuccess = true; - break; - } - else if (Result == VK_NOT_READY) - { - bSuccess = false; - } - else - { - bSuccess = false; - VERIFYVULKANRESULT(Result); - } - - // timer queries are used for Benchmarks which can stall a bit more - const double TimeoutValue = (QueryType == VK_QUERY_TYPE_TIMESTAMP) ? 2.0 : 0.5; - // look for gpu stuck/crashed - if ((FPlatformTime::Seconds() - StartTime) > TimeoutValue) - { - if (QueryType == VK_QUERY_TYPE_OCCLUSION) - { - UE_LOG(LogRHI, Log, TEXT("Timed out while waiting for GPU to catch up on occlusion results. (%.1f s)"), TimeoutValue); - } - else - { - UE_LOG(LogRHI, Log, TEXT("Timed out while waiting for GPU to catch up on occlusion/timer results. (%.1f s)"), TimeoutValue); - } - return false; - } - - ++NumLoops; - } - - GRenderThreadIdle[ERenderThreadIdleTypes::WaitingForGPUQuery] += FPlatformTime::Cycles() - IdleStart; - GRenderThreadNumIdle[ERenderThreadIdleTypes::WaitingForGPUQuery]++; - - bHasResults = true; - return true; - } - } - else - { - VERIFYVULKANRESULT(Result); - } - */ return false; } -/* -void FVulkanQueryPool::ResetAll(FVulkanCmdBuffer* InCmdBuffer) -{ - VulkanRHI::vkCmdResetQueryPool(InCmdBuffer->GetHandle(), QueryPool, 0, MaxQueries); - ++NumResets; -} -*/ - void FVulkanOcclusionQueryPool::SetFence(FVulkanCmdBuffer* InCmdBuffer) { check(InCmdBuffer); @@ -415,144 +218,6 @@ void FVulkanOcclusionQueryPool::Reset(FVulkanCmdBuffer* InCmdBuffer, uint32 InFr State = EState::RHIT_PostBeginBatch; } -bool FVulkanTimingQuery::InternalGetResult(uint64& OutTime, bool bWait) -{ - ensure(State == EState::Written); - if (Pool->TryGetResults(this, IndexInPool, bWait, Result)) - { - State = EState::HasResult; - OutTime = Result; - return true; - } - - return false; -} - -VkResult FVulkanTimestampQueryPool::InternalGetQueryPoolResults(FVulkanTimingQuery* Query, uint32 QueryIndex, uint64& OutResults) -{ - uint32 PendingQueries = (QueueTail <= QueueHead) ? (QueueHead - QueueTail) : (MaxQueries - QueueTail) + QueueHead; - uint32 FetchingQueries = (QueueTail <= QueryIndex) ? (QueryIndex - QueueTail) + 1 : (MaxQueries - QueueTail) + QueryIndex + 1; - - if (PendingQueries < FetchingQueries) //if we try to fetch more than are pending that we have the result already - { - OutResults = QueryOutput[QueryIndex]; - ensure(QueryAllocation[QueryIndex] == Query); - QueryAllocation[QueryIndex] = nullptr; - return VK_SUCCESS; - } - else if (QueryIndex >= QueueTail) - { - uint32 NumQueries = QueryIndex - QueueTail + 1; - VkResult Result = VulkanRHI::vkGetQueryPoolResults(Device->GetInstanceHandle(), QueryPool, QueueTail, NumQueries, sizeof(uint64) * NumQueries, &QueryOutput[QueueTail], sizeof(uint64), VK_QUERY_RESULT_64_BIT); - if (Result == VK_SUCCESS) - { - if (!IsInRenderingThread() || !IsRunningRHIInSeparateThread()) - { - FVulkanCmdBuffer* CmdBuffer = Device->GetImmediateContext().GetCommandBufferManager()->GetActiveCmdBuffer(); - VulkanRHI::vkCmdResetQueryPool(CmdBuffer->GetHandle(), QueryPool, QueueTail, NumQueries); - } - else - { - check(Query->Pool == this); - FRHICommandListExecutor::GetImmediateCommandList().EnqueueLambda([NumQueries, QueueTail = QueueTail, QueryPool = Query->Pool] - (FRHICommandListImmediate& RHICommandList) - { - FVulkanCmdBuffer* CmdBuffer = static_cast(RHICommandList.GetContext()).GetCommandBufferManager()->GetActiveCmdBuffer(); - VulkanRHI::vkCmdResetQueryPool(CmdBuffer->GetHandle(), QueryPool->QueryPool, QueueTail, NumQueries); - }); - } - OutResults = QueryOutput[QueryIndex]; - ensure(QueryAllocation[QueryIndex] == Query); - QueryAllocation[QueryIndex] = nullptr; - QueueTail = QueryIndex + 1; - } - return Result; - } - else - { - FVulkanTimingQuery* SavedQuery = QueryAllocation[MaxQueries - 1]; - VkResult FirstHalf = InternalGetQueryPoolResults(SavedQuery, MaxQueries - 1, OutResults); - QueryAllocation[MaxQueries - 1] = SavedQuery; - if (FirstHalf == VK_SUCCESS) - { - QueueTail = 0; - VkResult SecondHalf = InternalGetQueryPoolResults(Query, QueryIndex, OutResults); - if (SecondHalf == VK_SUCCESS) - { - QueueTail = QueryIndex + 1; - } - return SecondHalf; - } - return FirstHalf; - } -} - -bool FVulkanTimestampQueryPool::TryGetResults(FVulkanTimingQuery* Query, uint32 QueryIndex, bool bWait, uint64& OutResults) -{ - VkResult Result = InternalGetQueryPoolResults(Query, QueryIndex, OutResults); - if (Result == VK_SUCCESS) - { - return true; - } - else if (Result == VK_NOT_READY) - { - if (bWait) - { - SCOPE_CYCLE_COUNTER(STAT_VulkanWaitQuery); - - // We'll do manual wait - double StartTime = FPlatformTime::Seconds(); - - ENamedThreads::Type RenderThread_Local = ENamedThreads::GetRenderThread_Local(); - bool bSuccess = false; - while (!bSuccess) - { - FPlatformProcess::SleepNoStats(0); - - // pump RHIThread to make sure these queries have actually been submitted to the GPU. - if (IsInActualRenderingThread()) - { - FTaskGraphInterface::Get().ProcessThreadUntilIdle(RenderThread_Local); - } - - Result = InternalGetQueryPoolResults(Query, QueryIndex, OutResults); - if (Result == VK_SUCCESS) - { - bSuccess = true; - break; - } - else if (Result == VK_NOT_READY) - { - bSuccess = false; - } - else - { - bSuccess = false; - VERIFYVULKANRESULT(Result); - } - - // timer queries are used for Benchmarks which can stall a bit more - const double TimeoutValue = (QueryType == VK_QUERY_TYPE_TIMESTAMP) ? 2.0 : 0.5; - // look for gpu stuck/crashed - if ((FPlatformTime::Seconds() - StartTime) > TimeoutValue) - { - UE_LOG(LogRHI, Log, TEXT("Timed out while waiting for GPU to catch up on timer results. (%.1f s)"), TimeoutValue); - return false; - } - } - - return true; - } - } - else - { - VERIFYVULKANRESULT(Result); - } - - return false; -} - - void FVulkanCommandListContext::BeginOcclusionQueryBatch(FVulkanCmdBuffer* CmdBuffer, uint32 NumQueriesInBatch) { ensure(IsImmediate()); @@ -670,27 +335,6 @@ void FVulkanDevice::ReleaseUnusedOcclusionQueryPools() } } -/* -FVulkanTimestampQueryPool* FVulkanDevice::PrepareTimestampQueryPool(bool& bOutRequiresReset) -{ - bOutRequiresReset = false; - if (!TimestampQueryPool) - { - //#todo-rco: Create this earlier - TimestampQueryPool = new FVulkanTimestampQueryPool(this, GMaxLifetimeTimestampQueries); - bOutRequiresReset = true; - } - - // Wrap around the queries - if (TimestampQueryPool->GetNumAllocatedQueries() >= TimestampQueryPool->GetMaxQueries()) - { - //#todo-rco: Check overlap and grow if necessary - TimestampQueryPool->NumUsedQueries = 0; - } - - return TimestampQueryPool; -} -*/ void FVulkanCommandListContext::EndOcclusionQueryBatch(FVulkanCmdBuffer* CmdBuffer) { ensure(IsImmediate()); @@ -770,10 +414,8 @@ FRenderQueryRHIRef FVulkanDynamicRHI::RHICreateRenderQuery(ERenderQueryType Quer } else if (QueryType == RQT_AbsoluteTime) { -/* - FVulkanTimingQuery* Query = new FVulkanTimingQuery(); + FVulkanTimingQuery* Query = new FVulkanTimingQuery(Device); return Query; -*/ } else { @@ -786,6 +428,12 @@ FRenderQueryRHIRef FVulkanDynamicRHI::RHICreateRenderQuery(ERenderQueryType Quer bool FVulkanDynamicRHI::RHIGetRenderQueryResult(FRenderQueryRHIParamRef QueryRHI, uint64& OutNumPixels, bool bWait) { + auto ToMicroseconds = [](uint64 Timestamp) + { + const double Frequency = double(FVulkanGPUTiming::GetTimingFrequency()); + uint64 Microseconds = (uint64)((double(Timestamp) / Frequency) * 1000.0 * 1000.0); + return Microseconds; + }; check(IsInRenderingThread()); FVulkanRenderQuery* BaseQuery = ResourceCast(QueryRHI); if (BaseQuery->QueryType == RQT_Occlusion) @@ -815,20 +463,67 @@ bool FVulkanDynamicRHI::RHIGetRenderQueryResult(FRenderQueryRHIParamRef QueryRHI } else if (BaseQuery->QueryType == RQT_AbsoluteTime) { -/* FVulkanTimingQuery* Query = static_cast(BaseQuery); - uint64 TimingResult; - bool QueryResult = Query->GetResult(TimingResult, false); - OutNumPixels = TimingResult / 1000; //RHIGetRenderQueryResult assumes microseconds where vk provided ns - return QueryResult; -*/ - } - /* - FScopeLock ScopeLock(&GOcclusionQueryCS); - check(IsInRenderingThread()); + check(Query->Pool.CurrentTimestamp < Query->Pool.BufferSize); + int32 TimestampIndex = Query->Pool.CurrentTimestamp; + if (!bWait) + { + // Quickly check the most recent measurements to see if any of them has been resolved. Do not flush these queries. + for (uint32 IssueIndex = 1; IssueIndex < Query->Pool.NumIssuedTimestamps; ++IssueIndex) + { + const FVulkanTimingQueryPool::FCmdBufferFence& StartQuerySyncPoint = Query->Pool.TimestampListHandles[TimestampIndex]; + if (StartQuerySyncPoint.FenceCounter < StartQuerySyncPoint.CmdBuffer->GetFenceSignaledCounter()) + { + Query->Pool.ResultsBuffer->InvalidateMappedMemory(); + uint64* Data = (uint64*)Query->Pool.ResultsBuffer->GetMappedPointer(); + OutNumPixels = ToMicroseconds(Data[TimestampIndex]); + return true; + } + + TimestampIndex = (TimestampIndex + Query->Pool.BufferSize - 1) % Query->Pool.BufferSize; + } + } + + if (Query->Pool.NumIssuedTimestamps > 0 || bWait) + { + // None of the (NumIssuedTimestamps - 1) measurements were ready yet, + // so check the oldest measurement more thoroughly. + // This really only happens if occlusion and frame sync event queries are disabled, otherwise those will block until the GPU catches up to 1 frame behind + + const bool bBlocking = (Query->Pool.NumIssuedTimestamps == Query->Pool.BufferSize) || bWait; + const uint32 IdleStart = FPlatformTime::Cycles(); + + SCOPE_CYCLE_COUNTER(STAT_RenderQueryResultTime); + + if (bBlocking) + { + const FVulkanTimingQueryPool::FCmdBufferFence& StartQuerySyncPoint = Query->Pool.TimestampListHandles[TimestampIndex]; + bool bWaitForStart = StartQuerySyncPoint.FenceCounter == StartQuerySyncPoint.CmdBuffer->GetFenceSignaledCounter(); + if (bWaitForStart) + { + FRHICommandListExecutor::GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::FlushRHIThread); + + // Need to submit the open command lists. + Device->SubmitCommandsAndFlushGPU(); + } + + // CPU wait for query results to be ready. + if (bWaitForStart && StartQuerySyncPoint.FenceCounter == StartQuerySyncPoint.CmdBuffer->GetFenceSignaledCounter()) + { + Device->GetImmediateContext().GetCommandBufferManager()->WaitForCmdBuffer(StartQuerySyncPoint.CmdBuffer); + } + } + + Query->Pool.ResultsBuffer->InvalidateMappedMemory(); + GRenderThreadIdle[ERenderThreadIdleTypes::WaitingForGPUQuery] += FPlatformTime::Cycles() - IdleStart; + GRenderThreadNumIdle[ERenderThreadIdleTypes::WaitingForGPUQuery]++; + + uint64* Data = (uint64*)Query->Pool.ResultsBuffer->GetMappedPointer(); + OutNumPixels = ToMicroseconds(Data[TimestampIndex]); + return true; + } + } - return Query->GetResult(Device, OutResult, bWait); - */ return false; } @@ -888,37 +583,20 @@ void FVulkanCommandListContext::RHIEndRenderQuery(FRenderQueryRHIParamRef QueryR } else if (BaseQuery->QueryType == RQT_AbsoluteTime) { -/* FVulkanTimingQuery* Query = static_cast(BaseQuery); + Query->Pool.CurrentTimestamp = (Query->Pool.CurrentTimestamp + 1) % Query->Pool.BufferSize; + const uint32 QueryEndIndex = Query->Pool.CurrentTimestamp; FVulkanCmdBuffer* CmdBuffer = CommandBufferManager->GetActiveCmdBuffer(); - Query->Pool = Query->IsPooledAtHighLevel ? Query->Pool : CmdBuffer->PrepareTimestampQueryPool(); - Query->IndexInPool = Query->Pool->AcquireIndex(Query); - Query->State = FVulkanTimingQuery::Written; - - VulkanRHI::vkCmdWriteTimestamp(CmdBuffer->GetHandle(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, Query->Pool->GetHandle(), Query->IndexInPool); -#if VULKAN_QUERY_CALLSTACK - FPlatformStackWalk::CaptureStackBackTrace(Query->StackFrames, 16); -#endif -*/ + VulkanRHI::vkCmdWriteTimestamp(CmdBuffer->GetHandle(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, Query->Pool.GetHandle(), QueryEndIndex); + //check(CmdBuffer->IsOutsideRenderPass()); + VulkanRHI::vkCmdCopyQueryPoolResults(CmdBuffer->GetHandle(), Query->Pool.GetHandle(), QueryEndIndex, 1, Query->Pool.ResultsBuffer->GetHandle(), sizeof(uint64) * QueryEndIndex, sizeof(uint64), VK_QUERY_RESULT_64_BIT); + VulkanRHI::vkCmdResetQueryPool(CmdBuffer->GetHandle(), Query->Pool.GetHandle(), QueryEndIndex, 1); + Query->Pool.TimestampListHandles[QueryEndIndex].CmdBuffer = CmdBuffer; + Query->Pool.TimestampListHandles[QueryEndIndex].FenceCounter = CmdBuffer->GetFenceSignaledCounter(); + Query->Pool.NumIssuedTimestamps = FMath::Min(Query->Pool.NumIssuedTimestamps + 1, Query->Pool.BufferSize); } } -/* -FRenderQueryRHIRef FVulkanTimestampQueryPool::AllocateQuery() -{ - FVulkanTimingQuery* Query = new FVulkanTimingQuery(); - Query->State = FVulkanTimingQuery::Unused; - Query->IsPooledAtHighLevel = true; - Query->Pool = this; - return Query; -} - -void FVulkanTimestampQueryPool::ReleaseQuery(TRefCountPtr &Query) -{ - Query.SafeRelease(); -} -*/ - void FVulkanCommandListContext::WriteBeginTimestamp(FVulkanCmdBuffer* CmdBuffer) { FrameTiming->StartTiming(CmdBuffer); @@ -928,3 +606,16 @@ void FVulkanCommandListContext::WriteEndTimestamp(FVulkanCmdBuffer* CmdBuffer) { FrameTiming->EndTiming(CmdBuffer); } + +FVulkanTimingQuery::FVulkanTimingQuery(FVulkanDevice* InDevice) + : FVulkanRenderQuery(RQT_AbsoluteTime) + , Pool(InDevice, 4) +{ + Pool.ResultsBuffer = InDevice->GetStagingManager().AcquireBuffer(Pool.BufferSize * sizeof(uint64), VK_BUFFER_USAGE_TRANSFER_DST_BIT, true); +} + + +FVulkanTimingQuery::~FVulkanTimingQuery() +{ + Pool.GetParent()->GetStagingManager().ReleaseBuffer(nullptr, Pool.ResultsBuffer); +} diff --git a/Engine/Source/Runtime/VulkanRHI/Private/VulkanRHI.cpp b/Engine/Source/Runtime/VulkanRHI/Private/VulkanRHI.cpp index 05beaccc7b30..db552fbaf79f 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/VulkanRHI.cpp +++ b/Engine/Source/Runtime/VulkanRHI/Private/VulkanRHI.cpp @@ -570,7 +570,7 @@ void FVulkanDynamicRHI::SelectAndInitDevice() GRHIVendorId = Props.vendorID; GRHIAdapterName = ANSI_TO_TCHAR(Props.deviceName); - FVulkanPlatform::CheckDeviceDriver(DeviceIndex); + FVulkanPlatform::CheckDeviceDriver(DeviceIndex, Props); Device->InitGPU(DeviceIndex); @@ -1190,8 +1190,11 @@ void FVulkanDescriptorSetsLayoutInfo::GenerateHash(const TArrayView GTypesUsageHashMap; static uint32 GUniqueID = 1; diff --git a/Engine/Source/Runtime/VulkanRHI/Private/VulkanTexture.cpp b/Engine/Source/Runtime/VulkanRHI/Private/VulkanTexture.cpp index 13e3e948a11d..910d31e97532 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/VulkanTexture.cpp +++ b/Engine/Source/Runtime/VulkanRHI/Private/VulkanTexture.cpp @@ -1480,6 +1480,28 @@ VkImageView FVulkanTextureView::StaticCreate(FVulkanDevice& Device, VkImage InIm ViewInfo.subresourceRange.baseMipLevel = FirstMip; ensure(NumMips != 0xFFFFFFFF); ViewInfo.subresourceRange.levelCount = NumMips; + + auto CheckUseNvidiaWorkaround = []() -> bool + { + if (IsRHIDeviceNVIDIA()) + { + if(FParse::Param(FCommandLine::Get(), TEXT("rtx20xxmipworkaround"))) + { + // Workaround for 20xx family not copying last mips correctly, so instead the view is created without the last 1x1 and 2x2 mips + if (GRHIAdapterName.Contains(TEXT("RTX 20"))) + { + return true; + } + } + } + return false; + }; + static bool NvidiaWorkaround = CheckUseNvidiaWorkaround(); + if(NvidiaWorkaround && Format >= VK_FORMAT_BC1_RGB_UNORM_BLOCK && Format <= VK_FORMAT_BC7_SRGB_BLOCK && NumMips > 1) + { + ViewInfo.subresourceRange.levelCount = FMath::Max(1, int32(NumMips) - 2); + } + ensure(ArraySliceIndex != 0xFFFFFFFF); ViewInfo.subresourceRange.baseArrayLayer = ArraySliceIndex; ensure(NumArraySlices != 0xFFFFFFFF); diff --git a/Engine/Source/Runtime/VulkanRHI/Private/VulkanUAV.cpp b/Engine/Source/Runtime/VulkanRHI/Private/VulkanUAV.cpp index 1b9741c12a23..0ba87c87236c 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/VulkanUAV.cpp +++ b/Engine/Source/Runtime/VulkanRHI/Private/VulkanUAV.cpp @@ -410,20 +410,6 @@ void FVulkanGPUFence::Clear() CmdBuffer = nullptr; FenceSignaledCounter = MAX_uint64; } -/* -FRenderQueryPoolRHIRef FVulkanDynamicRHI::RHICreateRenderQueryPool(ERenderQueryType QueryType, uint32 NumQueries) -{ - if (QueryType == RQT_AbsoluteTime) - { - return new FVulkanTimestampQueryPool(Device, NumQueries); - } - else - { - return new FDefaultRHIRenderQueryPool(QueryType, this, NumQueries); - } -} -*/ - bool FVulkanGPUFence::Poll() const { diff --git a/Engine/Source/Runtime/VulkanRHI/Private/VulkanUniformBuffer.cpp b/Engine/Source/Runtime/VulkanRHI/Private/VulkanUniformBuffer.cpp index 0039650997c2..a2c995742d7a 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/VulkanUniformBuffer.cpp +++ b/Engine/Source/Runtime/VulkanRHI/Private/VulkanUniformBuffer.cpp @@ -17,30 +17,6 @@ enum #endif }; -#if 0 -const int NUM_SAFE_FRAMES = 5; -static TArray> GUBPool[NUM_SAFE_FRAMES]; - -static TRefCountPtr AllocateBufferFromPool(FVulkanDevice& Device, uint32 ConstantBufferSize, EUniformBufferUsage Usage) -{ - if (Usage == UniformBuffer_MultiFrame) - { - return new FVulkanBuffer(Device, ConstantBufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, false, __FILE__, __LINE__); - } - - int32 BufferIndex = GFrameNumberRenderThread % NUM_SAFE_FRAMES; - - GUBPool[BufferIndex].Add(new FVulkanBuffer(Device, ConstantBufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, false, __FILE__, __LINE__)); - return GUBPool[BufferIndex].Last(); -} - -void CleanupUniformBufferPool() -{ - int32 BufferIndex = (GFrameNumberRenderThread + 1) % NUM_SAFE_FRAMES; - GUBPool[BufferIndex].Reset(0); -} -#endif - /*----------------------------------------------------------------------------- Uniform buffer RHI object -----------------------------------------------------------------------------*/ @@ -63,7 +39,7 @@ static inline EBufferUsageFlags UniformBufferToBufferUsage(EUniformBufferUsage U } } -FVulkanUniformBuffer::FVulkanUniformBuffer(const FRHIUniformBufferLayout& InLayout, const void* Contents, EUniformBufferUsage InUsage, EUniformBufferValidation Validation, bool bCopyIntoConstantData) +FVulkanUniformBuffer::FVulkanUniformBuffer(const FRHIUniformBufferLayout& InLayout, const void* Contents, EUniformBufferUsage InUsage, EUniformBufferValidation Validation) : FRHIUniformBuffer(InLayout) { #if VULKAN_ENABLE_AGGRESSIVE_STATS @@ -75,18 +51,7 @@ FVulkanUniformBuffer::FVulkanUniformBuffer(const FRHIUniformBufferLayout& InLayo // - Meaning, there is always a uniform buffer with a size specified larged than 0 bytes check(InLayout.Resources.Num() > 0 || InLayout.ConstantBufferSize > 0); - // Contents might be null but size > 0 as the data doesn't need a CPU copy (it lives inside the 'real' VkBuffer) - if (bCopyIntoConstantData && InLayout.ConstantBufferSize) - { - // Create uniform buffer, which is stored on the CPU, the buffer is uploaded to a correct GPU buffer in UpdateDescriptorSets() - ConstantData.AddUninitialized(InLayout.ConstantBufferSize); - if (Contents) - { - FMemory::Memcpy(ConstantData.GetData(), Contents, InLayout.ConstantBufferSize); - } - } - - // Parse Sampler and Texture resources, if necessary + // Setup resource table const uint32 NumResources = InLayout.Resources.Num(); if (NumResources > 0) { @@ -134,19 +99,41 @@ void FVulkanUniformBuffer::UpdateResourceTable(FRHIResource** Resources, int32 R } } -void FVulkanUniformBuffer::Update(const void* Contents, int32 ContentsSize) -{ - check(ConstantData.Num() * sizeof(ConstantData[0]) == ContentsSize); +FVulkanEmulatedUniformBuffer::FVulkanEmulatedUniformBuffer(const FRHIUniformBufferLayout& InLayout, const void* Contents, EUniformBufferUsage InUsage, EUniformBufferValidation Validation) + : FVulkanUniformBuffer(InLayout, Contents, InUsage, Validation) +{ +#if VULKAN_ENABLE_AGGRESSIVE_STATS + SCOPE_CYCLE_COUNTER(STAT_VulkanUniformBufferCreateTime); +#endif + + // Contents might be null but size > 0 as the data doesn't need a CPU copy + if (InLayout.ConstantBufferSize) + { + // Create uniform buffer, which is stored on the CPU, the buffer is uploaded to a correct GPU buffer in UpdateDescriptorSets() + ConstantData.AddUninitialized(InLayout.ConstantBufferSize); + if (Contents) + { + FMemory::Memcpy(ConstantData.GetData(), Contents, InLayout.ConstantBufferSize); + } + } + + // Ancestor's constructor will set up the Resource table, so nothing else to do here +} + +void FVulkanEmulatedUniformBuffer::UpdateConstantData(const void* Contents, int32 ContentsSize) +{ + checkSlow(ConstantData.Num() * sizeof(ConstantData[0]) == ContentsSize); if (ContentsSize > 0) { FMemory::Memcpy(ConstantData.GetData(), Contents, ContentsSize); } } -FVulkanRealUniformBuffer::FVulkanRealUniformBuffer(FVulkanDevice& Device, const FRHIUniformBufferLayout& InLayout, const void* Contents, EUniformBufferUsage Usage, EUniformBufferValidation Validation) - : FVulkanUniformBuffer(InLayout, Contents, Usage, Validation, false) - , FVulkanResourceMultiBuffer(&Device, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, InLayout.ConstantBufferSize, UniformBufferToBufferUsage(Usage), GEmptyCreateInfo) + +FVulkanRealUniformBuffer::FVulkanRealUniformBuffer(FVulkanDevice& Device, const FRHIUniformBufferLayout& InLayout, const void* Contents, EUniformBufferUsage InUsage, EUniformBufferValidation Validation) + : FVulkanUniformBuffer(InLayout, Contents, InUsage, Validation) + , FVulkanResourceMultiBuffer(&Device, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, InLayout.ConstantBufferSize, UniformBufferToBufferUsage(InUsage), GEmptyCreateInfo) { #if VULKAN_ENABLE_AGGRESSIVE_STATS SCOPE_CYCLE_COUNTER(STAT_VulkanUniformBufferCreateTime); @@ -160,6 +147,8 @@ FVulkanRealUniformBuffer::FVulkanRealUniformBuffer(FVulkanDevice& Device, const FMemory::Memcpy(Data, Contents, InLayout.ConstantBufferSize); Unlock(bRT); } + + // Ancestor's constructor will set up the Resource table, so nothing else to do here } void FVulkanRealUniformBuffer::Update(const void* Contents, int32 ContentsSize) @@ -187,24 +176,33 @@ FUniformBufferRHIRef FVulkanDynamicRHI::RHICreateUniformBuffer(const void* Conte else { // Parts of the buffer are later on copied for each shader stage into the packed uniform buffer - return new FVulkanUniformBuffer(Layout, Contents, Usage, Validation, true); + return new FVulkanEmulatedUniformBuffer(Layout, Contents, Usage, Validation); } } -void FVulkanDynamicRHI::RHIUpdateUniformBuffer(FUniformBufferRHIParamRef UniformBufferRHI, const void* Contents) +template +inline void FVulkanDynamicRHI::UpdateUniformBuffer(FVulkanUniformBuffer* UniformBuffer, const void* Contents) { - FVulkanUniformBuffer* UniformBuffer = ResourceCast(UniformBufferRHI); - - const FRHIUniformBufferLayout& Layout = UniformBufferRHI->GetLayout(); + const FRHIUniformBufferLayout& Layout = UniformBuffer->GetLayout(); const int32 ConstantBufferSize = Layout.ConstantBufferSize; const int32 NumResources = Layout.Resources.Num(); FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList(); + FVulkanRealUniformBuffer* RealUniformBuffer = bRealUBs ? (FVulkanRealUniformBuffer*)UniformBuffer : nullptr; + FVulkanEmulatedUniformBuffer* EmulatedUniformBuffer = bRealUBs ? nullptr : (FVulkanEmulatedUniformBuffer*)UniformBuffer; + if (RHICmdList.Bypass()) { - UniformBuffer->Update(Contents, ConstantBufferSize); + if (bRealUBs) + { + RealUniformBuffer->Update(Contents, ConstantBufferSize); + } + else + { + EmulatedUniformBuffer->UpdateConstantData(Contents, ConstantBufferSize); + } UniformBuffer->UpdateResourceTable(Layout, Contents, NumResources); } else @@ -236,15 +234,39 @@ void FVulkanDynamicRHI::RHIUpdateUniformBuffer(FUniformBufferRHIParamRef Uniform FMemory::Memcpy(CmdListConstantBufferData, Contents, ConstantBufferSize); } - RHICmdList.EnqueueLambda([UniformBuffer, CmdListResources, NumResources, CmdListConstantBufferData, ConstantBufferSize](FRHICommandList&) + RHICmdList.EnqueueLambda([RealUniformBuffer, EmulatedUniformBuffer, CmdListResources, NumResources, CmdListConstantBufferData, ConstantBufferSize](FRHICommandList&) { - UniformBuffer->Update(CmdListConstantBufferData, ConstantBufferSize); - UniformBuffer->UpdateResourceTable(CmdListResources, NumResources); + if (bRealUBs) + { + RealUniformBuffer->Update(CmdListConstantBufferData, ConstantBufferSize); + RealUniformBuffer->UpdateResourceTable(CmdListResources, NumResources); + } + else + { + EmulatedUniformBuffer->UpdateConstantData(CmdListConstantBufferData, ConstantBufferSize); + EmulatedUniformBuffer->UpdateResourceTable(CmdListResources, NumResources); + } }); RHICmdList.RHIThreadFence(true); } } + +void FVulkanDynamicRHI::RHIUpdateUniformBuffer(FUniformBufferRHIParamRef UniformBufferRHI, const void* Contents) +{ + static TConsoleVariableData* CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Vulkan.UseRealUBs")); + const bool bHasRealUBs = FVulkanPlatform::UseRealUBsOptimization(CVar && CVar->GetValueOnAnyThread() > 0); + FVulkanUniformBuffer* UniformBuffer = ResourceCast(UniformBufferRHI); + if (bHasRealUBs) + { + UpdateUniformBuffer(UniformBuffer, Contents); + } + else + { + UpdateUniformBuffer(UniformBuffer, Contents); + } +} + FVulkanUniformBufferUploader::FVulkanUniformBufferUploader(FVulkanDevice* InDevice) : VulkanRHI::FDeviceChild(InDevice) , CPUBuffer(nullptr) diff --git a/Engine/Source/Runtime/VulkanRHI/Private/VulkanUtil.cpp b/Engine/Source/Runtime/VulkanRHI/Private/VulkanUtil.cpp index 087c554d21c5..7ecf3d9b3c53 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/VulkanUtil.cpp +++ b/Engine/Source/Runtime/VulkanRHI/Private/VulkanUtil.cpp @@ -14,34 +14,6 @@ extern CORE_API bool GIsGPUCrashed; -//#todo-rco: Horrible work-around to avoid breaking binary compatibility; move to header! -struct FVulkanTimingQueryPool : public FVulkanQueryPool -{ - FVulkanTimingQueryPool(FVulkanDevice* InDevice, uint32 InBufferSize) - : FVulkanQueryPool(InDevice, InBufferSize * 2, VK_QUERY_TYPE_TIMESTAMP) - , BufferSize(InBufferSize) - { - TimestampListHandles.AddZeroed(InBufferSize * 2); - } - - uint32 CurrentTimestamp = 0; - uint32 NumIssuedTimestamps = 0; - const uint32 BufferSize; - - struct FCmdBufferFence - { - FVulkanCmdBuffer* CmdBuffer; - uint64 FenceCounter; - }; - TArray TimestampListHandles; - - VulkanRHI::FStagingBuffer* ResultsBuffer = nullptr; -}; - - -//#todo-rco: Horrible work-around to avoid breaking binary compatibility -static TMap GTimingQueryPools; - static FString EventDeepString(TEXT("EventTooDeep")); static const uint32 EventDeepCRC = FCrc::StrCrc32(*EventDeepString); @@ -157,6 +129,11 @@ void FVulkanDynamicRHI::RHIUnlockStagingBuffer(FStagingBufferRHIParamRef Staging StagingBuffer->Unlock(); } +FVulkanGPUTiming::~FVulkanGPUTiming() +{ + check(!Pool); +} + /** * Initializes all Vulkan resources and if necessary, the static variables. */ @@ -168,9 +145,9 @@ void FVulkanGPUTiming::Initialize() if (FVulkanPlatform::SupportsTimestampRenderQueries() && GIsSupported) { - FVulkanTimingQueryPool* Pool = new FVulkanTimingQueryPool(Device, 8); + check(!Pool); + Pool = new FVulkanTimingQueryPool(Device, 8); Pool->ResultsBuffer = Device->GetStagingManager().AcquireBuffer(8 * sizeof(uint64), VK_BUFFER_USAGE_TRANSFER_DST_BIT, true); - GTimingQueryPools.Add(this, Pool); } } @@ -181,12 +158,10 @@ void FVulkanGPUTiming::Release() { if (FVulkanPlatform::SupportsTimestampRenderQueries()) { - FVulkanTimingQueryPool* FoundPool = nullptr; - if (GTimingQueryPools.RemoveAndCopyValue(this, FoundPool) && FoundPool) - { - Device->GetStagingManager().ReleaseBuffer(nullptr, FoundPool->ResultsBuffer); - delete FoundPool; - } + check(Pool); + Device->GetStagingManager().ReleaseBuffer(nullptr, Pool->ResultsBuffer); + delete Pool; + Pool = nullptr; } } @@ -202,7 +177,6 @@ void FVulkanGPUTiming::StartTiming(FVulkanCmdBuffer* CmdBuffer) { CmdBuffer = CmdContext->GetCommandBufferManager()->GetActiveCmdBuffer(); } - FVulkanTimingQueryPool* Pool = GTimingQueryPools.FindChecked(this); Pool->CurrentTimestamp = (Pool->CurrentTimestamp + 1) % Pool->BufferSize; const uint32 QueryStartIndex = Pool->CurrentTimestamp * 2; VulkanRHI::vkCmdWriteTimestamp(CmdBuffer->GetHandle(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, Pool->GetHandle(), QueryStartIndex); @@ -225,7 +199,6 @@ void FVulkanGPUTiming::EndTiming(FVulkanCmdBuffer* CmdBuffer) { CmdBuffer = CmdContext->GetCommandBufferManager()->GetActiveCmdBuffer(); } - FVulkanTimingQueryPool* Pool = GTimingQueryPools.FindChecked(this); check(Pool->CurrentTimestamp < Pool->BufferSize); const uint32 QueryStartIndex = Pool->CurrentTimestamp * 2; const uint32 QueryEndIndex = Pool->CurrentTimestamp * 2 + 1; @@ -253,7 +226,6 @@ uint64 FVulkanGPUTiming::GetTiming(bool bGetCurrentResultsAndBlock) { if (GIsSupported) { - FVulkanTimingQueryPool* Pool = GTimingQueryPools.FindChecked(this); check(Pool->CurrentTimestamp < Pool->BufferSize); uint64 StartTime, EndTime; int32 TimestampIndex = Pool->CurrentTimestamp; diff --git a/Engine/Source/Runtime/VulkanRHI/Private/Windows/VulkanWindowsPlatform.cpp b/Engine/Source/Runtime/VulkanRHI/Private/Windows/VulkanWindowsPlatform.cpp index b6046e86f7f3..46332f9cb958 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/Windows/VulkanWindowsPlatform.cpp +++ b/Engine/Source/Runtime/VulkanRHI/Private/Windows/VulkanWindowsPlatform.cpp @@ -185,7 +185,7 @@ void FVulkanWindowsPlatform::WriteCrashMarker(const FOptionalVulkanDeviceExtensi } } -void FVulkanWindowsPlatform::CheckDeviceDriver(uint32 DeviceIndex) +void FVulkanWindowsPlatform::CheckDeviceDriver(uint32 DeviceIndex, const VkPhysicalDeviceProperties& Props) { const bool bAllowVendorDevice = !FParse::Param(FCommandLine::Get(), TEXT("novendordevice")); if (IsRHIDeviceAMD() && bAllowVendorDevice) @@ -279,4 +279,16 @@ void FVulkanWindowsPlatform::CheckDeviceDriver(uint32 DeviceIndex) agsDeInit(AmdAgsContext); } } + else if (IsRHIDeviceNVIDIA()) + { + // Workaround a crash on 20xx family + UE_LOG(LogVulkanRHI, Warning, TEXT("Nvidia 20xx family of GPUs have a known crash. Compatibility mode (slow!) will now be enabled")); + if (GRHIAdapterName.Contains(TEXT("RTX 20"))) + { + extern TAutoConsoleVariable GRHIThreadCvar; + GRHIThreadCvar->SetWithCurrentPriority(0); + IConsoleVariable* BypassVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.RHICmdBypass")); + BypassVar->SetWithCurrentPriority(1); + } + } } diff --git a/Engine/Source/Runtime/VulkanRHI/Private/Windows/VulkanWindowsPlatform.h b/Engine/Source/Runtime/VulkanRHI/Private/Windows/VulkanWindowsPlatform.h index 22ed663645ca..0204d3b0af26 100644 --- a/Engine/Source/Runtime/VulkanRHI/Private/Windows/VulkanWindowsPlatform.h +++ b/Engine/Source/Runtime/VulkanRHI/Private/Windows/VulkanWindowsPlatform.h @@ -49,7 +49,7 @@ class FVulkanWindowsPlatform : public FVulkanGenericPlatform { public: - static void CheckDeviceDriver(uint32 DeviceIndex); + static void CheckDeviceDriver(uint32 DeviceIndex, const VkPhysicalDeviceProperties& Props); static bool LoadVulkanLibrary(); static bool LoadVulkanInstanceFunctions(VkInstance inInstance); diff --git a/Engine/Source/Runtime/VulkanRHI/Public/VulkanContext.h b/Engine/Source/Runtime/VulkanRHI/Public/VulkanContext.h index eeca66319800..2d63f7171f0b 100644 --- a/Engine/Source/Runtime/VulkanRHI/Public/VulkanContext.h +++ b/Engine/Source/Runtime/VulkanRHI/Public/VulkanContext.h @@ -369,6 +369,8 @@ public: void PrepareParallelFromBase(const FVulkanCommandListContext& BaseContext); + void* Hotfix; + protected: FVulkanDynamicRHI* RHI; FVulkanCommandListContext* Immediate; diff --git a/Engine/Source/Runtime/VulkanRHI/Public/VulkanDynamicRHI.h b/Engine/Source/Runtime/VulkanRHI/Public/VulkanDynamicRHI.h index 24c8d8f23fdc..17ec0e858f59 100644 --- a/Engine/Source/Runtime/VulkanRHI/Public/VulkanDynamicRHI.h +++ b/Engine/Source/Runtime/VulkanRHI/Public/VulkanDynamicRHI.h @@ -480,6 +480,9 @@ protected: void InternalUpdateTexture2D(bool bFromRenderingThread, FTexture2DRHIParamRef TextureRHI, uint32 MipIndex, const struct FUpdateTextureRegion2D& UpdateRegion, uint32 SourcePitch, const uint8* SourceData); void InternalUpdateTexture3D(bool bFromRenderingThread, FTexture3DRHIParamRef TextureRHI, uint32 MipIndex, const struct FUpdateTextureRegion3D& UpdateRegion, uint32 SourceRowPitch, uint32 SourceDepthPitch, const uint8* SourceData); + template + void UpdateUniformBuffer(FVulkanUniformBuffer* UniformBuffer, const void* Contents); + public: static TSharedPtr< IHeadMountedDisplayVulkanExtensions, ESPMode::ThreadSafe > HMDVulkanExtensions; }; diff --git a/Engine/Source/Runtime/VulkanRHI/Public/VulkanMemory.h b/Engine/Source/Runtime/VulkanRHI/Public/VulkanMemory.h index f4713867b8a5..42e9e703a4cf 100644 --- a/Engine/Source/Runtime/VulkanRHI/Public/VulkanMemory.h +++ b/Engine/Source/Runtime/VulkanRHI/Public/VulkanMemory.h @@ -834,6 +834,8 @@ namespace VulkanRHI void DumpMemory(); #endif + void* Hotfix; + protected: FDeviceMemoryManager* DeviceMemoryManager; TArray ResourceTypeHeaps; @@ -906,10 +908,7 @@ namespace VulkanRHI TArray UsedBufferAllocations[(int32)EPoolSizes::SizesCount + 1]; TArray FreeBufferAllocations[(int32)EPoolSizes::SizesCount + 1]; -#if 0 - TArray UsedImageAllocations; - TArray FreeImageAllocations; -#endif + void ReleaseFreedResources(bool bImmediately); void DestroyResourceAllocations(); }; @@ -935,11 +934,6 @@ namespace VulkanRHI return ResourceAllocation->GetMappedPointer(); } - inline uint32 GetAllocationOffset() const - { - return ResourceAllocation->GetOffset(); - } - inline uint32 GetSize() const { return BufferSize; @@ -1023,7 +1017,7 @@ namespace VulkanRHI TArray PendingFreeStagingBuffers; struct FFreeEntry { - FStagingBuffer* Buffer; + FStagingBuffer* StagingBuffer; uint32 FrameNumber; }; TArray FreeStagingBuffers; @@ -1587,7 +1581,6 @@ namespace VulkanRHI FVulkanDevice& Device; VkSemaphore SemaphoreHandle; }; - } diff --git a/Engine/Source/Runtime/VulkanRHI/Public/VulkanResources.h b/Engine/Source/Runtime/VulkanRHI/Public/VulkanResources.h index 80a56147a3b1..0b11fc2929d9 100644 --- a/Engine/Source/Runtime/VulkanRHI/Public/VulkanResources.h +++ b/Engine/Source/Runtime/VulkanRHI/Public/VulkanResources.h @@ -879,38 +879,28 @@ protected: uint32 FrameNumber = UINT32_MAX; }; - -class FVulkanTimestampQueryPool final : public FVulkanQueryPool +class FVulkanTimingQueryPool : public FVulkanQueryPool { -/* - FRenderQueryRHIRef AllocateQuery() override; - virtual void ReleaseQuery(TRefCountPtr &Query) override; -*/ - - uint32 QueueHead = 0; - uint32 QueueTail = 0; - TArray QueryAllocation; - public: - FVulkanTimestampQueryPool(FVulkanDevice* InDevice, uint32 InMaxQueries) - : FVulkanQueryPool(InDevice, InMaxQueries, VK_QUERY_TYPE_TIMESTAMP) + FVulkanTimingQueryPool(FVulkanDevice* InDevice, uint32 InBufferSize) + : FVulkanQueryPool(InDevice, InBufferSize * 2, VK_QUERY_TYPE_TIMESTAMP) + , BufferSize(InBufferSize) { - QueryAllocation.AddDefaulted(InMaxQueries); + TimestampListHandles.AddZeroed(InBufferSize * 2); } - uint32 AcquireIndex(class FVulkanTimingQuery* Query) + uint32 CurrentTimestamp = 0; + uint32 NumIssuedTimestamps = 0; + const uint32 BufferSize; + + struct FCmdBufferFence { - uint32 QueueIndex = QueueHead++; - QueueHead = (QueueHead < MaxQueries) ? QueueHead : 0; - ensure(QueueHead != QueueTail); - QueryAllocation[QueueIndex] = Query; - return QueueIndex; - } + FVulkanCmdBuffer* CmdBuffer; + uint64 FenceCounter; + }; + TArray TimestampListHandles; - bool TryGetResults(class FVulkanTimingQuery* Query, uint32 QueryIndex, bool bWait, uint64& OutResult); - -private: - VkResult InternalGetQueryPoolResults(class FVulkanTimingQuery* Query, uint32 QueryIndex, uint64& OutResults); + VulkanRHI::FStagingBuffer* ResultsBuffer = nullptr; }; class FVulkanRenderQuery : public FRHIRenderQuery @@ -954,70 +944,11 @@ public: class FVulkanTimingQuery : public FVulkanRenderQuery { public: - FVulkanTimingQuery() - : FVulkanRenderQuery(RQT_AbsoluteTime) - { - } + FVulkanTimingQuery(FVulkanDevice* InDevice); + virtual ~FVulkanTimingQuery(); - virtual ~FVulkanTimingQuery() - { - } - - enum EState : uint8 - { - Unused, - Written, - HasResult, - }; - - bool GetResult(uint64& OutTime, bool bWait) - { - if (State == EState::HasResult) - { - OutTime = Result; - return true; - } - else if (State != EState::Unused) - { - return InternalGetResult(OutTime, bWait); - } - else - { - return false; - } - } - -#if VULKAN_QUERY_CALLSTACK - uint64 StackFrames[16]; -#endif - -protected: - /*TRefCountPtr<*/FVulkanTimestampQueryPool* /*>*/ Pool; - EState State = EState::Unused; - bool IsPooledAtHighLevel = false; - - bool InternalGetResult(uint64& OutTime, bool bWait); - - friend class FVulkanTimestampQueryPool; - friend class FVulkanCommandListContext; + FVulkanTimingQueryPool Pool; }; -/* -class FVulkanRenderQuery : public FRHIRenderQuery -{ -public: - FVulkanRenderQuery(FVulkanDevice* Device, ERenderQueryType InQueryType); - virtual ~FVulkanRenderQuery(); - - inline bool HasQueryBeenEmitted() const - { - return State == EState::InEnd; - } - - uint32 LastPoolReset = 0; - -private: -}; -*/ struct FVulkanBufferView : public FRHIResource, public VulkanRHI::FDeviceChild { @@ -1273,28 +1204,33 @@ public: class FVulkanUniformBuffer : public FRHIUniformBuffer { public: - FVulkanUniformBuffer(const FRHIUniformBufferLayout& InLayout, const void* Contents, EUniformBufferUsage InUsage, EUniformBufferValidation Validation, bool bCopyIntoConstantData); - - TArray ConstantData; + FVulkanUniformBuffer(const FRHIUniformBufferLayout& InLayout, const void* Contents, EUniformBufferUsage InUsage, EUniformBufferValidation Validation); const TArray>& GetResourceTable() const { return ResourceTable; } void UpdateResourceTable(const FRHIUniformBufferLayout& InLayout, const void* Contents, int32 ResourceNum); void UpdateResourceTable(FRHIResource** Resources, int32 ResourceNum); - virtual void Update(const void* Contents, int32 ContentsSize); - - -private: +protected: TArray> ResourceTable; }; +class FVulkanEmulatedUniformBuffer : public FVulkanUniformBuffer +{ +public: + FVulkanEmulatedUniformBuffer(const FRHIUniformBufferLayout& InLayout, const void* Contents, EUniformBufferUsage InUsage, EUniformBufferValidation Validation); + + TArray ConstantData; + + void UpdateConstantData(const void* Contents, int32 ContentsSize); +}; + class FVulkanRealUniformBuffer : public FVulkanUniformBuffer, public FVulkanResourceMultiBuffer { public: - FVulkanRealUniformBuffer(FVulkanDevice& Device, const FRHIUniformBufferLayout& InLayout, const void* Contents, EUniformBufferUsage Usage, EUniformBufferValidation Validation); + FVulkanRealUniformBuffer(FVulkanDevice& Device, const FRHIUniformBufferLayout& InLayout, const void* Contents, EUniformBufferUsage InUsage, EUniformBufferValidation Validation); - virtual void Update(const void* Contents, int32 ContentsSize); + void Update(const void* Contents, int32 ContentsSize); private: TArray> ResourceTable; diff --git a/Engine/Source/Runtime/VulkanRHI/Public/VulkanUtil.h b/Engine/Source/Runtime/VulkanRHI/Public/VulkanUtil.h index 0cc0a4e936df..65fbdd9187b1 100644 --- a/Engine/Source/Runtime/VulkanRHI/Public/VulkanUtil.h +++ b/Engine/Source/Runtime/VulkanRHI/Public/VulkanUtil.h @@ -11,8 +11,8 @@ #include "Serialization/MemoryWriter.h" class FVulkanCmdBuffer; -class FVulkanTimingQuery; class FVulkanCommandListContext; +class FVulkanTimingQueryPool; class FVulkanGPUTiming : public FGPUTiming { @@ -25,6 +25,8 @@ public: { } + ~FVulkanGPUTiming(); + /** * Start a GPU timing measurement. */ @@ -57,7 +59,6 @@ public: bool IsComplete() const { check(bEndTimestampIssued); - //return *((uint64*)EndTimestamp.GetPointer()) != 0; return true; } @@ -76,6 +77,7 @@ private: bool bEndTimestampIssued; FVulkanCommandListContext* CmdContext; + FVulkanTimingQueryPool* Pool = nullptr; }; /** A single perf event node, which tracks information about a appBeginDrawEvent/appEndDrawEvent range. */ diff --git a/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSScripting.cpp b/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSScripting.cpp index bd4dc1af2a57..f779274a6a9b 100644 --- a/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSScripting.cpp +++ b/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSScripting.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "NativeJSScripting.h" diff --git a/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSScripting.h b/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSScripting.h index 537a3099f08c..d877e0face2d 100644 --- a/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSScripting.h +++ b/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSScripting.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" diff --git a/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSStructDeserializerBackend.cpp b/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSStructDeserializerBackend.cpp index 03f7dbb3ed0d..89e77284a50e 100644 --- a/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSStructDeserializerBackend.cpp +++ b/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSStructDeserializerBackend.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "NativeJSStructDeserializerBackend.h" #include "NativeJSScripting.h" diff --git a/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSStructDeserializerBackend.h b/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSStructDeserializerBackend.h index 8b2529ee3060..6d8bba9dfe1c 100644 --- a/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSStructDeserializerBackend.h +++ b/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSStructDeserializerBackend.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSStructSerializerBackend.cpp b/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSStructSerializerBackend.cpp index b95509eb86b5..a6ef7cf82186 100644 --- a/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSStructSerializerBackend.cpp +++ b/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSStructSerializerBackend.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "Native/NativeJSStructSerializerBackend.h" diff --git a/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSStructSerializerBackend.h b/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSStructSerializerBackend.h index fd5eeeeb0df5..10df01e45eae 100644 --- a/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSStructSerializerBackend.h +++ b/Engine/Source/Runtime/WebBrowser/Private/Native/NativeJSStructSerializerBackend.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/WebBrowser/Private/Native/NativeWebBrowserProxy.cpp b/Engine/Source/Runtime/WebBrowser/Private/Native/NativeWebBrowserProxy.cpp index 3dc2279da26d..e24cb18d547b 100644 --- a/Engine/Source/Runtime/WebBrowser/Private/Native/NativeWebBrowserProxy.cpp +++ b/Engine/Source/Runtime/WebBrowser/Private/Native/NativeWebBrowserProxy.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "NativeWebBrowserProxy.h" diff --git a/Engine/Source/Runtime/WebBrowser/Private/Native/NativeWebBrowserProxy.h b/Engine/Source/Runtime/WebBrowser/Private/Native/NativeWebBrowserProxy.h index 0ad71f63fdf0..48402603e518 100644 --- a/Engine/Source/Runtime/WebBrowser/Private/Native/NativeWebBrowserProxy.h +++ b/Engine/Source/Runtime/WebBrowser/Private/Native/NativeWebBrowserProxy.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" diff --git a/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11Query.cpp b/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11Query.cpp index 1d25fe0850cb..cac1b7e34a0a 100644 --- a/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11Query.cpp +++ b/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11Query.cpp @@ -44,12 +44,17 @@ public: inline void Add(FRenderQueryRHIRef NewQuery) { + if(!ActiveBatches.Num()) + { + StartNewBatch(); + } FRenderQueryBatch& CurrentBatch = ActiveBatches.Last(); CurrentBatch.Queries.Add(NewQuery); } void PollQueryResults() { + QUICK_SCOPE_CYCLE_COUNTER(STAT_PollQueryResults); FD3D11DynamicRHI* D3D11RHI = static_cast(GDynamicRHI); for (int32 BatchIdx = 0; BatchIdx < ActiveBatches.Num(); ++BatchIdx) @@ -80,6 +85,7 @@ public: void PollQueryResults_RenderThread() { + QUICK_SCOPE_CYCLE_COUNTER(STAT_PollQueryResults_RenderThread); FScopedRHIThreadStaller RHITStaller(FRHICommandListExecutor::GetImmediateCommandList()); PollQueryResults(); } @@ -187,21 +193,30 @@ bool FD3D11DynamicRHI::RHIGetRenderQueryResult(FRenderQueryRHIParamRef QueryRHI, { check(IsInRenderingThread()); FD3D11RenderQuery* Query = ResourceCast(QueryRHI); - - static uint32 LastQueryBatchId = 0xffffffff; - uint32 CurrentQueryBatchId = FD3D11RenderQueryBatcher::Get().GetCurrentId(); - if (LastQueryBatchId != CurrentQueryBatchId && Query->QueryType == RQT_Occlusion) - { - LastQueryBatchId = CurrentQueryBatchId; - FD3D11RenderQueryBatcher::Get().PollQueryResults_RenderThread(); - } - bool bSuccess = true; - if(!Query->bResultIsCached) + if(Query->QueryType == RQT_AbsoluteTime && !bWait) { - bSuccess = GetQueryData(Query->Resource,&Query->Result,sizeof(Query->Result),Query->QueryType,bWait,true); + if(!Query->bResultIsCached) + { + return false; + } + } + else + { + static uint32 LastQueryBatchId = 0xffffffff; + uint32 CurrentQueryBatchId = FD3D11RenderQueryBatcher::Get().GetCurrentId(); + if (LastQueryBatchId != CurrentQueryBatchId && Query->QueryType == RQT_Occlusion) + { + LastQueryBatchId = CurrentQueryBatchId; + FD3D11RenderQueryBatcher::Get().PollQueryResults_RenderThread(); + } - Query->bResultIsCached = bSuccess; + if(!Query->bResultIsCached) + { + bSuccess = GetQueryData(Query->Resource,&Query->Result,sizeof(Query->Result),Query->QueryType,bWait,true); + + Query->bResultIsCached = bSuccess; + } } if(Query->QueryType == RQT_AbsoluteTime) @@ -243,6 +258,10 @@ void FD3D11DynamicRHI::RHIEndRenderQuery(FRenderQueryRHIParamRef QueryRHI) { FD3D11RenderQuery* Query = ResourceCast(QueryRHI); Query->bResultIsCached = false; // for occlusion queries, this is redundant with the one in begin + if(Query->QueryType == RQT_AbsoluteTime) + { + FD3D11RenderQueryBatcher::Get().Add(QueryRHI); + } Direct3DDeviceIMContext->End(Query->Resource); //@todo - d3d debug spews warnings about OQ's that are being issued but not polled, need to investigate diff --git a/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11RenderTarget.cpp b/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11RenderTarget.cpp index 4321dc2aaed5..a2fe128aa715 100644 --- a/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11RenderTarget.cpp +++ b/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11RenderTarget.cpp @@ -978,6 +978,21 @@ static void ConvertRAWSurfaceDataToFColor(DXGI_FORMAT Format, uint32 Width, uint } } } + else if (Format == DXGI_FORMAT_R8_UNORM) + { + // Read the data out of the buffer, converting it from ABGR to ARGB. + for (uint32 Y = 0; Y < Height; Y++) + { + uint8* SrcPtr = (uint8*)(In + Y * SrcPitch); + FColor* DestPtr = Out + Y * Width; + for (uint32 X = 0; X < Width; X++) + { + *DestPtr = FColor(*SrcPtr, 0, 0, 0); + ++SrcPtr; + ++DestPtr; + } + } + } else { // not supported yet diff --git a/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11Texture.cpp b/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11Texture.cpp index 0798cf06fcd4..e326a21d8ded 100644 --- a/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11Texture.cpp +++ b/Engine/Source/Runtime/Windows/D3D11RHI/Private/D3D11Texture.cpp @@ -549,13 +549,6 @@ TD3D11Texture2D* FD3D11DynamicRHI::CreateD3D11Texture2D(uint32 D3D11_USAGE TextureUsage = D3D11_USAGE_DEFAULT; bool bCreateShaderResource = true; - // NV12 doesn't support SRV in NV12 format so don't create SRV for it. - // Todo: add support for SRVs of underneath luminance & chrominance textures. - if (Format == PF_NV12) - { - bCreateShaderResource = false; - } - uint32 ActualMSAACount = NumSamples; uint32 ActualMSAAQuality = GetMaxMSAAQuality(ActualMSAACount); @@ -614,6 +607,13 @@ TD3D11Texture2D* FD3D11DynamicRHI::CreateD3D11Texture2D(uint32 TextureDesc.CPUAccessFlags = CPUAccessFlags; TextureDesc.MiscFlags = bCubeTexture ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; + // NV12 doesn't support SRV in NV12 format so don't create SRV for it. + // Todo: add support for SRVs of underneath luminance & chrominance textures. + if (Format == PF_NV12) + { + bCreateShaderResource = false; + } + if (Flags & TexCreate_DisableSRVCreation) { bCreateShaderResource = false; diff --git a/Engine/Source/Runtime/Windows/D3D11RHI/Private/Windows/WindowsD3D11Device.cpp b/Engine/Source/Runtime/Windows/D3D11RHI/Private/Windows/WindowsD3D11Device.cpp index f2cd34a5a3e1..e890b1cb5d2c 100644 --- a/Engine/Source/Runtime/Windows/D3D11RHI/Private/Windows/WindowsD3D11Device.cpp +++ b/Engine/Source/Runtime/Windows/D3D11RHI/Private/Windows/WindowsD3D11Device.cpp @@ -1676,6 +1676,8 @@ void FD3D11DynamicRHI::InitD3DDevice() void FD3D11DynamicRHI::RHIPerFrameRHIFlushComplete() { + RHIPollRenderQueryResults(); + extern void D3D11RHIQueryBatcherPerFrameCleanup(); D3D11RHIQueryBatcherPerFrameCleanup(); diff --git a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/HighlightRecorder.cpp b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/HighlightRecorder.cpp index 50204916efb3..531e1724280c 100644 --- a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/HighlightRecorder.cpp +++ b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/HighlightRecorder.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "HighlightRecorder.h" #include "WmfMp4Writer.h" diff --git a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/HighlightRecorder.h b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/HighlightRecorder.h index e78aefc8f933..d8bcb94b7d1c 100644 --- a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/HighlightRecorder.h +++ b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/HighlightRecorder.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsPlatformFeatures.cpp b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsPlatformFeatures.cpp index b230511622bf..0e225168c76e 100644 --- a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsPlatformFeatures.cpp +++ b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsPlatformFeatures.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "WindowsPlatformFeatures.h" #include "WmfPrivate.h" diff --git a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsPlatformFeatures.h b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsPlatformFeatures.h index 943f47738146..2961643b6381 100644 --- a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsPlatformFeatures.h +++ b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsPlatformFeatures.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsPlatformFeaturesCommon.h b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsPlatformFeaturesCommon.h index 0613842f9e83..5f47e9a9e3bc 100644 --- a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsPlatformFeaturesCommon.h +++ b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsPlatformFeaturesCommon.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsVideoRecordingSystem.cpp b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsVideoRecordingSystem.cpp index d240a46a4149..d40d067824c1 100644 --- a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsVideoRecordingSystem.cpp +++ b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsVideoRecordingSystem.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "WindowsVideoRecordingSystem.h" #include "Modules/ModuleManager.h" #include "Engine/GameEngine.h" diff --git a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsVideoRecordingSystem.h b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsVideoRecordingSystem.h index e2eabf4cef0a..17dd1b6a5cd7 100644 --- a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsVideoRecordingSystem.h +++ b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WindowsVideoRecordingSystem.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfMp4Writer.cpp b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfMp4Writer.cpp index 8a2aad7894b9..4cd1b199f5ce 100644 --- a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfMp4Writer.cpp +++ b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfMp4Writer.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "WmfMp4Writer.h" diff --git a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfMp4Writer.h b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfMp4Writer.h index 78080728dae0..273a5896d3f6 100644 --- a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfMp4Writer.h +++ b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfMp4Writer.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfPrivate.h b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfPrivate.h index 5d195c4bed62..c7e0814a82bc 100644 --- a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfPrivate.h +++ b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfPrivate.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfRingBuffer.cpp b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfRingBuffer.cpp index 355ed2e82282..5d49823fc41e 100644 --- a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfRingBuffer.cpp +++ b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfRingBuffer.cpp @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "WmfRingBuffer.h" #include "Misc/ScopeLock.h" diff --git a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfRingBuffer.h b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfRingBuffer.h index bc6a82cfaf29..65a37eba2e66 100644 --- a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfRingBuffer.h +++ b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/Private/WmfRingBuffer.h @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once diff --git a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/WindowsPlatformFeatures.Build.cs b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/WindowsPlatformFeatures.Build.cs index 92d35abb5275..db909d9155cb 100644 --- a/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/WindowsPlatformFeatures.Build.cs +++ b/Engine/Source/Runtime/Windows/WindowsPlatformFeatures/WindowsPlatformFeatures.Build.cs @@ -1,4 +1,4 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. using System.IO; using UnrealBuildTool; diff --git a/Engine/Source/ThirdParty/Expat/expat-2.2.0/BuildForUE/IOS/BuildForIOS.sh b/Engine/Source/ThirdParty/Expat/expat-2.2.0/BuildForUE/IOS/BuildForIOS.sh index 522d0f512576..6107c876dc79 100755 --- a/Engine/Source/ThirdParty/Expat/expat-2.2.0/BuildForUE/IOS/BuildForIOS.sh +++ b/Engine/Source/ThirdParty/Expat/expat-2.2.0/BuildForUE/IOS/BuildForIOS.sh @@ -17,7 +17,7 @@ cd "${BUILD_DIR}" function build() { CONFIGURATION=$1 - xcodebuild -project expat.xcodeproj -configuration $CONFIGURATION -destination generic/platform=iOS + xcodebuild BITCODE_GENERATION_MODE=bitcode -project expat.xcodeproj -configuration $CONFIGURATION -destination generic/platform=iOS mkdir -p ../${CONFIGURATION}/ cp -v ${CONFIGURATION}-iphoneos/* ../${CONFIGURATION}/ } diff --git a/Engine/Source/ThirdParty/HarfBuzz/harfbuzz-1.2.4/BuildForUE/IOS/iOS.cmake b/Engine/Source/ThirdParty/HarfBuzz/harfbuzz-1.2.4/BuildForUE/IOS/iOS.cmake index c57ae47f6b62..e166f4c3c5e7 100644 --- a/Engine/Source/ThirdParty/HarfBuzz/harfbuzz-1.2.4/BuildForUE/IOS/iOS.cmake +++ b/Engine/Source/ThirdParty/HarfBuzz/harfbuzz-1.2.4/BuildForUE/IOS/iOS.cmake @@ -77,6 +77,8 @@ set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") # Hidden visibilty is required for cxx on iOS add_definitions(-fvisibility=hidden -fvisibility-inlines-hidden) +add_definitions(-fembed-bitcode) + # Add in ios min version flag if (DEFINED CMAKE_IOS_DEPLOYMENT_TARGET) add_definitions(-mios-version-min=${CMAKE_IOS_DEPLOYMENT_TARGET}) diff --git a/Engine/Source/ThirdParty/PhysX3/PhysX_3.4/Source/compiler/cmake/ios/CMakeLists.txt b/Engine/Source/ThirdParty/PhysX3/PhysX_3.4/Source/compiler/cmake/ios/CMakeLists.txt index d2454366e945..80bc36dbc022 100644 --- a/Engine/Source/ThirdParty/PhysX3/PhysX_3.4/Source/compiler/cmake/ios/CMakeLists.txt +++ b/Engine/Source/ThirdParty/PhysX3/PhysX_3.4/Source/compiler/cmake/ios/CMakeLists.txt @@ -14,7 +14,7 @@ IF (NOT ${TARGET_BUILD_PLATFORM} IN_LIST PLATFORM_LIST) MESSAGE(FATAL_ERROR "Invalid platform:" ${TARGET_BUILD_PLATFORM}) ENDIF() -SET(CMAKE_CXX_FLAGS "-std=c++11 -fno-rtti -fno-exceptions -ffast-math -ffunction-sections -fdata-sections -Werror -ferror-limit=0 -Wall -Wextra -fstrict-aliasing -Wstrict-aliasing=2 -Weverything -Wno-documentation-deprecated-sync -Wno-documentation-unknown-command -Wno-float-equal -Wno-padded -Wno-weak-vtables -Wno-cast-align -Wno-conversion -Wno-missing-noreturn -Wno-missing-variable-declarations -Wno-shift-sign-overflow -Wno-covered-switch-default -Wno-exit-time-destructors -Wno-global-constructors -Wno-missing-prototypes -Wno-unreachable-code -Wno-unused-macros -Wno-unused-member-function -Wno-used-but-marked-unused -Wno-weak-template-vtables -Wno-deprecated -Wno-non-virtual-dtor -Wno-invalid-noreturn -Wno-return-type-c-linkage -Wno-reserved-id-macro -Wno-c++98-compat-pedantic -Wno-unused-local-typedef -Wno-old-style-cast -Wno-newline-eof -Wno-unused-private-field -Wno-undefined-reinterpret-cast -Wno-invalid-offsetof -Wno-zero-as-null-pointer-constant -Wno-shadow-field -Wno-unused-template -Wno-c++2a-compat -Wno-unknown-warning-option -gdwarf-2") +SET(CMAKE_CXX_FLAGS "-std=c++11 -fno-rtti -fno-exceptions -ffast-math -Werror -ferror-limit=0 -Wall -Wextra -fstrict-aliasing -Wstrict-aliasing=2 -Weverything -Wno-documentation-deprecated-sync -Wno-documentation-unknown-command -Wno-float-equal -Wno-padded -Wno-weak-vtables -Wno-cast-align -Wno-conversion -Wno-missing-noreturn -Wno-missing-variable-declarations -Wno-shift-sign-overflow -Wno-covered-switch-default -Wno-exit-time-destructors -Wno-global-constructors -Wno-missing-prototypes -Wno-unreachable-code -Wno-unused-macros -Wno-unused-member-function -Wno-used-but-marked-unused -Wno-weak-template-vtables -Wno-deprecated -Wno-non-virtual-dtor -Wno-invalid-noreturn -Wno-return-type-c-linkage -Wno-reserved-id-macro -Wno-c++98-compat-pedantic -Wno-unused-local-typedef -Wno-old-style-cast -Wno-newline-eof -Wno-unused-private-field -Wno-undefined-reinterpret-cast -Wno-invalid-offsetof -Wno-zero-as-null-pointer-constant -Wno-shadow-field -Wno-unused-template -Wno-c++2a-compat -Wno-unknown-warning-option -gdwarf-2 -fembed-bitcode") SET(CMAKE_SHARED_LINKER_FLAGS "") @@ -59,7 +59,7 @@ SET(CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS sup SET(CMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS "iphoneos") SET(CMAKE_XCODE_ATTRIBUTE_SDKROOT ${CMAKE_IOS_SDK_ROOT}) -SET(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET "7.0") +SET(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET "8.0") # Controls PX_NVTX for all projects on iOS SET(PHYSX_IOS_ENABLE_NVTX 0) diff --git a/Engine/Source/ThirdParty/PhysX3/PxShared/src/compiler/cmake/ios/CMakeLists.txt b/Engine/Source/ThirdParty/PhysX3/PxShared/src/compiler/cmake/ios/CMakeLists.txt index eb23635306a7..cb71e1e2b912 100644 --- a/Engine/Source/ThirdParty/PhysX3/PxShared/src/compiler/cmake/ios/CMakeLists.txt +++ b/Engine/Source/ThirdParty/PhysX3/PxShared/src/compiler/cmake/ios/CMakeLists.txt @@ -13,7 +13,7 @@ IF (NOT ${TARGET_BUILD_PLATFORM} IN_LIST PLATFORM_LIST) MESSAGE(FATAL_ERROR "Invalid platform:" ${TARGET_BUILD_PLATFORM}) ENDIF() -SET(CMAKE_CXX_FLAGS "-std=c++11 -fno-rtti -fno-exceptions -ffast-math -ffunction-sections -fdata-sections -Werror -ferror-limit=0 -Wall -Wextra -fstrict-aliasing -Wstrict-aliasing=2 -Weverything -Wno-documentation-deprecated-sync -Wno-documentation-unknown-command -Wno-float-equal -Wno-padded -Wno-weak-vtables -Wno-cast-align -Wno-conversion -Wno-missing-noreturn -Wno-missing-variable-declarations -Wno-shift-sign-overflow -Wno-covered-switch-default -Wno-exit-time-destructors -Wno-global-constructors -Wno-missing-prototypes -Wno-unreachable-code -Wno-unused-macros -Wno-unused-member-function -Wno-used-but-marked-unused -Wno-weak-template-vtables -Wno-deprecated -Wno-non-virtual-dtor -Wno-invalid-noreturn -Wno-return-type-c-linkage -Wno-reserved-id-macro -Wno-c++98-compat-pedantic -Wno-unused-local-typedef -Wno-old-style-cast -Wno-newline-eof -Wno-unused-private-field -Wno-undefined-reinterpret-cast -Wno-invalid-offsetof -Wno-zero-as-null-pointer-constant -Wno-shadow-field -Wno-unused-template -gdwarf-2") +SET(CMAKE_CXX_FLAGS "-std=c++11 -fno-rtti -fno-exceptions -ffast-math -Werror -ferror-limit=0 -Wall -Wextra -fstrict-aliasing -Wstrict-aliasing=2 -Weverything -Wno-documentation-deprecated-sync -Wno-documentation-unknown-command -Wno-float-equal -Wno-padded -Wno-weak-vtables -Wno-cast-align -Wno-conversion -Wno-missing-noreturn -Wno-missing-variable-declarations -Wno-shift-sign-overflow -Wno-covered-switch-default -Wno-exit-time-destructors -Wno-global-constructors -Wno-missing-prototypes -Wno-unreachable-code -Wno-unused-macros -Wno-unused-member-function -Wno-used-but-marked-unused -Wno-weak-template-vtables -Wno-deprecated -Wno-non-virtual-dtor -Wno-invalid-noreturn -Wno-return-type-c-linkage -Wno-reserved-id-macro -Wno-c++98-compat-pedantic -Wno-unused-local-typedef -Wno-old-style-cast -Wno-newline-eof -Wno-unused-private-field -Wno-undefined-reinterpret-cast -Wno-invalid-offsetof -Wno-zero-as-null-pointer-constant -Wno-shadow-field -Wno-unused-template -gdwarf-2 -fembed-bitcode") SET(CMAKE_SHARED_LINKER_FLAGS "") @@ -57,7 +57,7 @@ SET(CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS sup SET(CMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS "iphoneos") SET(CMAKE_XCODE_ATTRIBUTE_SDKROOT ${CMAKE_IOS_SDK_ROOT}) -SET(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET "7.0") +SET(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET "8.0") SET(PXSHARED_IOS_COMPILE_DEFS _LIB;DISABLE_CUDA_PHYSX;DISABLE_COMPUTE_PHYSX) SET(PXSHARED_IOS_DEBUG_COMPILE_DEFS _DEBUG;PX_DEBUG=1;PX_CHECKED=1) diff --git a/Engine/Source/ThirdParty/Vivox/vivoxclientapi/BuildForUE4/Android/BuildForAndroid.bat b/Engine/Source/ThirdParty/Vivox/vivoxclientapi/BuildForUE4/Android/BuildForAndroid.bat index 44d87df9b6f3..de106865882f 100644 --- a/Engine/Source/ThirdParty/Vivox/vivoxclientapi/BuildForUE4/Android/BuildForAndroid.bat +++ b/Engine/Source/ThirdParty/Vivox/vivoxclientapi/BuildForUE4/Android/BuildForAndroid.bat @@ -53,7 +53,7 @@ rmdir "%ANDROID_BUILD_PATH%" /s/q echo Building for Android (32-bit) Debug mkdir "%ANDROID_BUILD_PATH%" pushd "%ANDROID_BUILD_PATH%" -..\..\..\..\..\..\..\Extras\ThirdPartyNotUE\CMake\bin\cmake.exe -G "MinGW Makefiles" -DVIVOXSDK_PATH=../../vivox-sdk/Include -DCMAKE_TOOLCHAIN_FILE="%PATH_TO_CMAKE_FILE%\Android\Android.cmake" -DANDROID_NDK="%NDKROOT%" -DCMAKE_MAKE_PROGRAM=%MAKE% -DCMAKE_BUILD_TYPE=Debug -DANDROID_NATIVE_API_LEVEL=android-19 -DANDROID_ABI=armeabi-v7a -DANDROID_STL=gnustl_shared %PATH_TO_CMAKE_FILE% +..\..\..\..\..\..\Extras\ThirdPartyNotUE\CMake\bin\cmake.exe -G "MinGW Makefiles" -DVIVOXSDK_PATH=../../vivox-sdk/Include -DCMAKE_TOOLCHAIN_FILE="%PATH_TO_CMAKE_FILE%\Android\Android.cmake" -DANDROID_NDK="%NDKROOT%" -DCMAKE_MAKE_PROGRAM=%MAKE% -DCMAKE_BUILD_TYPE=Debug -DANDROID_NATIVE_API_LEVEL=android-19 -DANDROID_ABI=armeabi-v7a -DANDROID_STL=gnustl_shared %PATH_TO_CMAKE_FILE% REM Now compile it %MAKE% diff --git a/Engine/Source/ThirdParty/Vivox/vivoxclientapi/BuildForUE4/IOS/BuildForIOS.sh b/Engine/Source/ThirdParty/Vivox/vivoxclientapi/BuildForUE4/IOS/BuildForIOS.sh index c36974aa4145..4ac75bfab0f0 100755 --- a/Engine/Source/ThirdParty/Vivox/vivoxclientapi/BuildForUE4/IOS/BuildForIOS.sh +++ b/Engine/Source/ThirdParty/Vivox/vivoxclientapi/BuildForUE4/IOS/BuildForIOS.sh @@ -20,7 +20,8 @@ function build() xcodebuild -project vivoxclientapi.xcodeproj -configuration $CONFIGURATION -destination generic/platform=iOS } -build Release +build RelWithDebInfo +mv -v ../RelWithDebInfo/libvivoxclientapi.a ../Release/libvivoxclientapi.a build Debug cd "${SCRIPT_DIR}" rm -rf "${BUILD_DIR}" diff --git a/Engine/Source/ThirdParty/Vivox/vivoxclientapi/vivoxclientapi/src/vivoxclientsdk.cpp b/Engine/Source/ThirdParty/Vivox/vivoxclientapi/vivoxclientapi/src/vivoxclientsdk.cpp index daf361fc63fb..9c513921eb3c 100644 --- a/Engine/Source/ThirdParty/Vivox/vivoxclientapi/vivoxclientapi/src/vivoxclientsdk.cpp +++ b/Engine/Source/ThirdParty/Vivox/vivoxclientapi/vivoxclientapi/src/vivoxclientsdk.cpp @@ -103,12 +103,15 @@ static std::string UTF8ToCodePage(const char *uBuf, size_t uBufLen) #define _strdup(s) strdup(s) #endif -#define CHECK_RET(x) if(!(x)) { m_app->onAssert(__FUNCTION__, __LINE__, #x); return; } -#define CHECK_RET1(x, y) if(!(x)) { m_app->onAssert(__FUNCTION__, __LINE__, #x); return y; } -#define CHECK(x) if(!(x)) { m_app->onAssert(__FUNCTION__, __LINE__, #x); } -#define CHECK_CONT(x) if(!(x)) { m_app->onAssert(__FUNCTION__, __LINE__, #x); continue; } -#define CHECK_BREAK(x) if(!(x)) { m_app->onAssert(__FUNCTION__, __LINE__, #x); break; } -#define ALWAYS_ASSERT(x) m_app->onAssert(__FUNCTION__, __LINE__, #x) +#define CHECK_RET(x) if(!(x)) { if (m_app) { m_app->onAssert(__FUNCTION__, __LINE__, #x); } return; } +#define CHECK_RET1(x, y) if(!(x)) { if (m_app) { m_app->onAssert(__FUNCTION__, __LINE__, #x); } return y; } +#define CHECK(x) if(!(x)) { if (m_app) { m_app->onAssert(__FUNCTION__, __LINE__, #x); } } +#define CHECK_CONT(x) if(!(x)) { if (m_app) { m_app->onAssert(__FUNCTION__, __LINE__, #x); } continue; } +#define CHECK_BREAK(x) if(!(x)) { if (m_app) { m_app->onAssert(__FUNCTION__, __LINE__, #x); } break; } +#define ALWAYS_ASSERT(x) { if (m_app) { m_app->onAssert(__FUNCTION__, __LINE__, #x); } } + +#define CHECK_STATUS_RET(x) if((x) != 0) { if (m_app) { m_app->onAssert(__FUNCTION__, __LINE__, #x); } return; } +#define CHECK_STATUS_RETVAL(x) { int RetVal = (x); if(RetVal != 0) { if (m_app) { m_app->onAssert(__FUNCTION__, __LINE__, #x); } return VCSStatus(RetVal); }} namespace VivoxClientApi { @@ -235,8 +238,8 @@ namespace VivoxClientApi { void NextState(const std::string &sessionHandle, const Uri &channelUri) { (void)channelUri; if (!m_volumeRequestInProgress && m_currentVolume != m_desiredVolume) { - vx_req_session_set_participant_volume_for_me_t *req; - vx_req_session_set_participant_volume_for_me_create(&req); + vx_req_session_set_participant_volume_for_me_t *req = nullptr; + CHECK_STATUS_RET(vx_req_session_set_participant_volume_for_me_create(&req)); req->session_handle = vx_strdup(sessionHandle.c_str()); req->participant_uri = vx_strdup(m_uri.ToString()); req->volume = m_desiredVolume; @@ -244,8 +247,8 @@ namespace VivoxClientApi { m_volumeRequestInProgress = true; } if (!m_mutedForMeRequestInProgress && m_currentMutedForMe != m_desiredMutedForMe) { - vx_req_session_set_participant_mute_for_me_t *req; - vx_req_session_set_participant_mute_for_me_create(&req); + vx_req_session_set_participant_mute_for_me_t *req = nullptr; + CHECK_STATUS_RET(vx_req_session_set_participant_mute_for_me_create(&req)); req->session_handle = vx_strdup(sessionHandle.c_str()); req->participant_uri = vx_strdup(m_uri.ToString()); req->mute = m_desiredMutedForMe ? 1 : 0; @@ -348,8 +351,8 @@ namespace VivoxClientApi { if(m_currentState == ChannelStateDisconnected && m_desiredState == ChannelStateConnected) { CHECK_RET(m_channelUri.IsValid()); { - vx_req_sessiongroup_add_session *req; - vx_req_sessiongroup_add_session_create(&req); + vx_req_sessiongroup_add_session_t *req = nullptr; + CHECK_STATUS_RET(vx_req_sessiongroup_add_session_create(&req)); req->connect_audio = 1; req->connect_text = 0; req->uri = vx_strdup(m_channelUri.ToString()); @@ -365,8 +368,8 @@ namespace VivoxClientApi { issueRequest(&req->base); } } else if((m_currentState == ChannelStateConnecting || m_currentState == ChannelStateConnected) && m_desiredState == ChannelStateDisconnected) { - vx_req_sessiongroup_remove_session *req; - vx_req_sessiongroup_remove_session_create(&req); + vx_req_sessiongroup_remove_session_t *req = nullptr; + CHECK_STATUS_RET(vx_req_sessiongroup_remove_session_create(&req)); req->session_handle = vx_strdup(m_sessionHandle.c_str()); req->sessiongroup_handle = vx_strdup(sessionGroupHandle.c_str()); m_currentState = ChannelStateDisconnecting; @@ -374,8 +377,8 @@ namespace VivoxClientApi { } else if (m_currentState == ChannelStateConnected) { if (!m_volumeRequestInProgress && m_currentVolume != m_desiredVolume && !m_sessionMuted) { - vx_req_session_set_local_speaker_volume_t *req; - vx_req_session_set_local_speaker_volume_create(&req); + vx_req_session_set_local_speaker_volume_t *req = nullptr; + CHECK_STATUS_RET(vx_req_session_set_local_speaker_volume_create(&req)); req->session_handle = vx_strdup(m_sessionHandle.c_str()); req->volume = m_desiredVolume; issueRequest(&req->base); @@ -453,16 +456,16 @@ namespace VivoxClientApi { } VCSStatus SetTransmissionToThisChannel(){ - vx_req_sessiongroup_set_tx_session_t *req; - vx_req_sessiongroup_set_tx_session_create(&req); + vx_req_sessiongroup_set_tx_session_t *req = nullptr; + CHECK_STATUS_RETVAL(vx_req_sessiongroup_set_tx_session_create(&req)); req->session_handle = vx_strdup(m_sessionHandle.c_str()); return issueRequest(&req->base); } VCSStatus Set3DPosition(const Vector &speakerPosition, const Vector &listenerPosition, const Vector &listenerForward, const Vector &listenerUp) { - vx_req_session_set_3d_position_t *req; - vx_req_session_set_3d_position_create(&req); + vx_req_session_set_3d_position_t *req = nullptr; + CHECK_STATUS_RETVAL(vx_req_session_set_3d_position_create(&req)); req->req_disposition_type = req_disposition_no_reply_required; req->session_handle = vx_strdup(m_sessionHandle.c_str()); @@ -706,8 +709,8 @@ namespace VivoxClientApi { fclose(fp); if(HasConnectedChannel()) { - vx_req_sessiongroup_control_audio_injection_t *req; - vx_req_sessiongroup_control_audio_injection_create(&req); + vx_req_sessiongroup_control_audio_injection_t *req = nullptr; + CHECK_STATUS_RETVAL(vx_req_sessiongroup_control_audio_injection_create(&req)); req->audio_injection_control_type = VX_SESSIONGROUP_AUDIO_INJECTION_CONTROL_RESTART; req->sessiongroup_handle = vx_strdup(m_sessionGroupHandle.c_str()); req->filename = vx_strdup(filename); @@ -718,8 +721,8 @@ namespace VivoxClientApi { void StopPlayFileIntoChannels() { - vx_req_sessiongroup_control_audio_injection_t *req; - vx_req_sessiongroup_control_audio_injection_create(&req); + vx_req_sessiongroup_control_audio_injection_t *req = nullptr; + CHECK_STATUS_RET(vx_req_sessiongroup_control_audio_injection_create(&req)); req->audio_injection_control_type = VX_SESSIONGROUP_AUDIO_INJECTION_CONTROL_STOP; req->sessiongroup_handle = vx_strdup(m_sessionGroupHandle.c_str()); issueRequest(&req->base); @@ -766,8 +769,8 @@ namespace VivoxClientApi { if (volume == 100) { s->SetSessionMuted(false); volume=s->GetDesiredVolume(); } // issue the request, there is no state information to worry about if it fails. - vx_req_session_set_local_speaker_volume_t *req; - vx_req_session_set_local_speaker_volume_create(&req); + vx_req_session_set_local_speaker_volume_t *req = nullptr; + CHECK_STATUS_RETVAL(vx_req_session_set_local_speaker_volume_create(&req)); req->session_handle = vx_strdup(s->GetSessionHandle().c_str()); req->volume = volume; @@ -801,9 +804,8 @@ namespace VivoxClientApi { Channel *s = FindChannel(channel); if (s == NULL) return VCSStatus(VX_E_NO_EXIST); - vx_req_channel_mute_user_t *req; - - vx_req_channel_mute_user_create(&req); + vx_req_channel_mute_user_t *req = nullptr; + CHECK_STATUS_RETVAL(vx_req_channel_mute_user_create(&req)); req->account_handle = vx_strdup(m_accountHandle.c_str()); req->channel_uri = vx_strdup(channel.ToString()); req->participant_uri = vx_strdup(target.ToString()); @@ -934,18 +936,22 @@ namespace VivoxClientApi { } break; case ChannelTransmissionPolicy::vx_channel_transmission_policy_all: - m_channelTransmissionPolicyRequestInProgress = true; - vx_req_sessiongroup_set_tx_all_sessions_t *req_all; - vx_req_sessiongroup_set_tx_all_sessions_create(&req_all); + { + m_channelTransmissionPolicyRequestInProgress = true; + vx_req_sessiongroup_set_tx_all_sessions_t *req_all = nullptr; + CHECK_STATUS_RET(vx_req_sessiongroup_set_tx_all_sessions_create(&req_all)); req_all->sessiongroup_handle = vx_strdup(m_sessionGroupHandle.c_str()); issueRequest(&req_all->base); + } break; case ChannelTransmissionPolicy::vx_channel_transmission_policy_none: - m_channelTransmissionPolicyRequestInProgress = true; - vx_req_sessiongroup_set_tx_no_session_t *req_none; - vx_req_sessiongroup_set_tx_no_session_create(&req_none); + { + m_channelTransmissionPolicyRequestInProgress = true; + vx_req_sessiongroup_set_tx_no_session_t *req_none = nullptr; + CHECK_STATUS_RET(vx_req_sessiongroup_set_tx_no_session_create(&req_none)); req_none->sessiongroup_handle = vx_strdup(m_sessionGroupHandle.c_str()); issueRequest(&req_none->base); + } break; default: break; @@ -1159,8 +1165,8 @@ namespace VivoxClientApi { VCSStatus IssueGetStats(bool reset) { - vx_req_sessiongroup_get_stats *req; - vx_req_sessiongroup_get_stats_create(&req); + vx_req_sessiongroup_get_stats_t *req = nullptr; + CHECK_STATUS_RETVAL(vx_req_sessiongroup_get_stats_create(&req)); req->sessiongroup_handle = vx_strdup(GetSessionGroupHandle().c_str()); req->reset_stats = reset ? 1 : 0; return issueRequest(&req->base); @@ -1211,8 +1217,8 @@ namespace VivoxClientApi { CHECK(!m_accountName.IsValid() || m_accountName == accountName); m_accountHandle = accountHandle; m_accountName = accountName; - vx_req_sessiongroup_create *req; - vx_req_sessiongroup_create_create(&req); + vx_req_sessiongroup_create_t *req = nullptr; + CHECK_STATUS_RET(vx_req_sessiongroup_create_create(&req)); req->account_handle = vx_strdup(accountHandle.c_str()); req->base.cookie = GetNextRequestId(NULL, "G"); req->sessiongroup_handle = vx_strdup(req->base.cookie); @@ -1325,8 +1331,8 @@ namespace VivoxClientApi { { if(m_currentLoginState == LoginStateLoggedOut && m_desiredLoginState == LoginStateLoggedIn) { - vx_req_account_anonymous_login_t *req; - vx_req_account_anonymous_login_create(&req); + vx_req_account_anonymous_login_t *req = nullptr; + CHECK_STATUS_RET(vx_req_account_anonymous_login_create(&req)); req->connector_handle = vx_strdup(m_connectorHandle.c_str()); req->base.cookie = GetNextRequestId(NULL, "A"); req->account_handle = vx_strdup(req->base.cookie); @@ -1349,8 +1355,8 @@ namespace VivoxClientApi { //m_currentPassword = m_desiredPassword; issueRequest(&req->base); } else if((m_currentLoginState == LoginStateLoggedIn || m_currentLoginState == LoginStateLoggingIn) && m_desiredLoginState == LoginStateLoggedOut) { - vx_req_account_logout_t *req; - vx_req_account_logout_create(&req); + vx_req_account_logout_t *req = nullptr; + CHECK_STATUS_RET(vx_req_account_logout_create(&req)); req->account_handle = vx_strdup(m_accountHandle.c_str()); m_currentLoginState = LoginStateLoggingOut; issueRequest(&req->base); @@ -1373,8 +1379,8 @@ namespace VivoxClientApi { std::string blockedStr = blocked.str(); std::string unblockedStr = unblocked.str(); if(!blockedStr.empty()) { - vx_req_account_control_communications_t *req; - vx_req_account_control_communications_create(&req); + vx_req_account_control_communications_t *req = nullptr; + CHECK_STATUS_RET(vx_req_account_control_communications_create(&req)); req->account_handle = vx_strdup(m_accountHandle.c_str()); req->user_uris = vx_strdup(blockedStr.c_str()); req->operation = vx_control_communications_operation_block; @@ -1382,8 +1388,8 @@ namespace VivoxClientApi { } if(!unblockedStr.empty()) { - vx_req_account_control_communications_t *req; - vx_req_account_control_communications_create(&req); + vx_req_account_control_communications_t *req = nullptr; + CHECK_STATUS_RET(vx_req_account_control_communications_create(&req)); req->account_handle = vx_strdup(m_accountHandle.c_str()); req->user_uris = vx_strdup(unblockedStr.c_str()); req->operation = vx_control_communications_operation_unblock; @@ -1466,7 +1472,7 @@ namespace VivoxClientApi { VCSStatus KickUser(const Uri &channel, const Uri &userUri) { vx_req_channel_kick_user_t *req; - vx_req_channel_kick_user_create(&req); + CHECK_STATUS_RETVAL(vx_req_channel_kick_user_create(&req)); req->account_handle = vx_strdup(m_accountHandle.c_str()); req->channel_uri = vx_strdup(channel.ToString()); req->participant_uri = vx_strdup(userUri.ToString()); @@ -1855,8 +1861,8 @@ namespace VivoxClientApi { CHECK_RET1(fp, VCSStatus(VX_E_FILE_OPEN_FAILED)); #endif fclose(fp); - vx_req_aux_render_audio_start_t *req; - vx_req_aux_render_audio_start_create(&req); + vx_req_aux_render_audio_start_t *req = nullptr; + CHECK_STATUS_RETVAL(vx_req_aux_render_audio_start_create(&req)); req->sound_file_path = vx_strdup(filename); req->loop = 1; issueRequest(&req->base); @@ -1867,8 +1873,8 @@ namespace VivoxClientApi { void StopAudioOutputDeviceTest() { if (m_audioOutputDeviceTestIsRunning) { - vx_req_aux_render_audio_stop_t *req; - vx_req_aux_render_audio_stop_create(&req); + vx_req_aux_render_audio_stop_t *req = nullptr; + CHECK_STATUS_RET(vx_req_aux_render_audio_stop_create(&req)); issueRequest(&req->base); m_audioOutputDeviceTestIsRunning = false; } @@ -1884,8 +1890,8 @@ namespace VivoxClientApi { CHECK_RET1(m_audioOutputDeviceTestIsRunning == false, VCSStatus(VX_E_FAILED)); CHECK_RET1(m_audioInputDeviceTestIsPlayingBack == false, VCSStatus(VX_E_FAILED)); CHECK_RET1(m_audioInputDeviceTestIsRecording == false, VCSStatus(VX_E_FAILED)); - vx_req_aux_start_buffer_capture *req; - vx_req_aux_start_buffer_capture_create(&req); + vx_req_aux_start_buffer_capture_t *req = nullptr; + CHECK_STATUS_RETVAL(vx_req_aux_start_buffer_capture_create(&req)); issueRequest(&req->base); m_audioInputDeviceTestIsRecording = true; return VCSStatus(0); @@ -1894,8 +1900,8 @@ namespace VivoxClientApi { void StopAudioInputDeviceTestRecord() { if (m_audioInputDeviceTestIsRecording) { - vx_req_aux_capture_audio_stop_t *req; - vx_req_aux_capture_audio_stop_create(&req); + vx_req_aux_capture_audio_stop_t *req = nullptr; + CHECK_STATUS_RET(vx_req_aux_capture_audio_stop_create(&req)); issueRequest(&req->base); m_audioInputDeviceTestIsRecording = false; m_audioInputDeviceTestHasAudioToPlayback = true; @@ -1907,8 +1913,8 @@ namespace VivoxClientApi { CHECK_RET1(m_audioOutputDeviceTestIsRunning == false, VCSStatus(VX_E_FAILED)); CHECK_RET1(m_audioInputDeviceTestIsPlayingBack == false, VCSStatus(VX_E_FAILED)); CHECK_RET1(m_audioInputDeviceTestIsRecording == false, VCSStatus(VX_E_FAILED)); - vx_req_aux_play_audio_buffer_t *req; - vx_req_aux_play_audio_buffer_create(&req); + vx_req_aux_play_audio_buffer_t *req = nullptr; + CHECK_STATUS_RETVAL(vx_req_aux_play_audio_buffer_create(&req)); issueRequest(&req->base); m_audioInputDeviceTestIsPlayingBack = true; return VCSStatus(0); @@ -1917,8 +1923,8 @@ namespace VivoxClientApi { void StopAudioInputDeviceTestPlayback() { if (m_audioInputDeviceTestIsPlayingBack) { - vx_req_aux_render_audio_stop_t *req; - vx_req_aux_render_audio_stop_create(&req); + vx_req_aux_render_audio_stop_t *req = nullptr; + CHECK_STATUS_RET(vx_req_aux_render_audio_stop_create(&req)); issueRequest(&req->base); m_audioInputDeviceTestIsPlayingBack = false; } @@ -2076,15 +2082,15 @@ namespace VivoxClientApi { void RequestAudioInputDevices() { - vx_req_aux_get_capture_devices_t *capture_req; - vx_req_aux_get_capture_devices_create(&capture_req); + vx_req_aux_get_capture_devices_t *capture_req = nullptr; + CHECK_STATUS_RET(vx_req_aux_get_capture_devices_create(&capture_req)); issueRequest(&capture_req->base); } void RequestAudioOutputDevices() { - vx_req_aux_get_render_devices_t *render_req; - vx_req_aux_get_render_devices_create(&render_req); + vx_req_aux_get_render_devices_t *render_req = nullptr; + CHECK_STATUS_RET(vx_req_aux_get_render_devices_create(&render_req)); issueRequest(&render_req->base); } @@ -2405,8 +2411,8 @@ namespace VivoxClientApi { if(m_currentState == ConnectorStateUninitialized) { CHECK_RET(m_connectorHandle.empty()); CHECK_RET(!m_currentServer.IsValid()); - vx_req_connector_create_t *req; - vx_req_connector_create_create(&req); + vx_req_connector_create_t *req = nullptr; + CHECK_STATUS_RET(vx_req_connector_create_create(&req)); req->acct_mgmt_server = vx_strdup(m_desiredServer.ToString()); req->application = vx_strdup(m_application.c_str()); req->base.cookie = GetNextRequestId(NULL, "C"); @@ -2422,8 +2428,8 @@ namespace VivoxClientApi { if(m_currentState == ConnectorStateInitialized) { CHECK_RET(m_currentServer.IsValid()); CHECK_RET(!m_connectorHandle.empty()); - vx_req_connector_initiate_shutdown *req; - vx_req_connector_initiate_shutdown_create(&req); + vx_req_connector_initiate_shutdown *req = nullptr; + CHECK_STATUS_RET(vx_req_connector_initiate_shutdown_create(&req)); req->connector_handle = vx_strdup(m_connectorHandle.c_str()); m_currentState = ConnectorStateUninitializing; issueRequest(&req->base); @@ -2439,8 +2445,8 @@ namespace VivoxClientApi { // audio device and master volume states if (!(m_currentAudioInputDevicePolicy == m_desiredAudioInputDevicePolicy)) { /// This only puts in a change request if the effective device would change - vx_req_aux_set_capture_device_t *req; - vx_req_aux_set_capture_device_create(&req); + vx_req_aux_set_capture_device_t *req = nullptr; + CHECK_STATUS_RET(vx_req_aux_set_capture_device_create(&req)); req->base.vcookie = new AudioDevicePolicy(m_desiredAudioInputDevicePolicy); @@ -2450,8 +2456,8 @@ namespace VivoxClientApi { } if (!(m_currentAudioOutputDevicePolicy == m_desiredAudioOutputDevicePolicy)) { /// This only puts in a change request if the effective device would change - vx_req_aux_set_render_device_t *req; - vx_req_aux_set_render_device_create(&req); + vx_req_aux_set_render_device_t *req = nullptr; + CHECK_STATUS_RET(vx_req_aux_set_render_device_create(&req)); req->base.vcookie = new AudioDevicePolicy(m_desiredAudioOutputDevicePolicy); @@ -2461,8 +2467,8 @@ namespace VivoxClientApi { } if (m_masterAudioInputDeviceVolume != m_desiredAudioInputDeviceVolume) { if (!m_masterAudioInputDeviceVolumeRequestInProgress) { - vx_req_connector_set_local_mic_volume_t *req; - vx_req_connector_set_local_mic_volume_create(&req); + vx_req_connector_set_local_mic_volume_t *req = nullptr; + CHECK_STATUS_RET(vx_req_connector_set_local_mic_volume_create(&req)); req->volume = m_desiredAudioInputDeviceVolume; issueRequest(&req->base); m_masterAudioInputDeviceVolumeRequestInProgress = true; @@ -2471,8 +2477,8 @@ namespace VivoxClientApi { } if (m_masterAudioOutputDeviceVolume != m_desiredAudioOutputDeviceVolume) { if (!m_masterAudioOutputDeviceVolumeRequestInProgress) { - vx_req_connector_set_local_speaker_volume_t *req; - vx_req_connector_set_local_speaker_volume_create(&req); + vx_req_connector_set_local_speaker_volume_t *req = nullptr; + CHECK_STATUS_RET(vx_req_connector_set_local_speaker_volume_create(&req)); req->volume = m_desiredAudioOutputDeviceVolume; issueRequest(&req->base); m_masterAudioOutputDeviceVolumeRequestInProgress = true; @@ -2481,8 +2487,8 @@ namespace VivoxClientApi { } if (m_autoVad != m_desiredAutoVad || (!m_autoVad && m_masterVadSensitivity != m_desiredVadSensitivity)) { if (!m_masterVoiceActivateDetectionRequestInProgress) { - vx_req_aux_set_vad_properties *req; - vx_req_aux_set_vad_properties_create(&req); + vx_req_aux_set_vad_properties_t *req = nullptr; + CHECK_STATUS_RET(vx_req_aux_set_vad_properties_create(&req)); req->vad_sensitivity = m_desiredVadSensitivity; req->vad_noise_floor = 576; req->vad_hangover = 2000; @@ -3186,8 +3192,8 @@ namespace VivoxClientApi { { if (value != m_audioOutputDeviceMuted) { m_audioOutputDeviceMuted = value; - vx_req_connector_mute_local_speaker_t *req; - vx_req_connector_mute_local_speaker_create(&req); + vx_req_connector_mute_local_speaker_t *req = nullptr; + CHECK_STATUS_RET(vx_req_connector_mute_local_speaker_create(&req)); req->mute_level = value ? 1 : 0; vx_issue_request(&req->base); } @@ -3202,8 +3208,8 @@ namespace VivoxClientApi { { if (value != m_audioInputDeviceMuted) { m_audioInputDeviceMuted = value; - vx_req_connector_mute_local_mic_t *req; - vx_req_connector_mute_local_mic_create(&req); + vx_req_connector_mute_local_mic_t *req = nullptr; + CHECK_STATUS_RET(vx_req_connector_mute_local_mic_create(&req)); req->mute_level = value ? 1 : 0; vx_issue_request(&req->base); } @@ -3221,8 +3227,8 @@ namespace VivoxClientApi { /// void EnteredBackground() { - vx_req_aux_notify_application_state_change *req; - vx_req_aux_notify_application_state_change_create(&req); + vx_req_aux_notify_application_state_change_t *req = nullptr; + CHECK_STATUS_RET(vx_req_aux_notify_application_state_change_create(&req)); req->notification_type = vx_application_state_notification_type_before_background; issueRequest(&req->base); } @@ -3234,8 +3240,8 @@ namespace VivoxClientApi { /// void WillEnterForeground() { - vx_req_aux_notify_application_state_change *req; - vx_req_aux_notify_application_state_change_create(&req); + vx_req_aux_notify_application_state_change_t *req = nullptr; + CHECK_STATUS_RET(vx_req_aux_notify_application_state_change_create(&req)); req->notification_type = vx_application_state_notification_type_after_foreground; issueRequest(&req->base); } @@ -3247,8 +3253,8 @@ namespace VivoxClientApi { /// void OnBackgroundIdleTimeout() { - vx_req_aux_notify_application_state_change *req; - vx_req_aux_notify_application_state_change_create(&req); + vx_req_aux_notify_application_state_change_t *req = nullptr; + CHECK_STATUS_RET(vx_req_aux_notify_application_state_change_create(&req)); req->notification_type = vx_application_state_notification_type_periodic_background_idle; issueRequest(&req->base); } @@ -3332,8 +3338,12 @@ namespace VivoxClientApi { m_app = NULL; m_desiredState = ConnectorStateUninitialized; m_currentState = ConnectorStateUninitialized; + m_connectorHandle.clear(); + m_logins.clear(); m_multiChannel = false; m_multiLogin = false; + m_audioOutputDeviceList.clear(); + m_audioInputDeviceList.clear(); m_audioInputDeviceListPopulated = false; m_audioOutputDeviceListPopulated = false; m_masterAudioInputDeviceVolume = 50; diff --git a/Engine/Source/ThirdParty/hlslcc/hlslcc/src/hlslcc_lib/ast_to_hir.cpp b/Engine/Source/ThirdParty/hlslcc/hlslcc/src/hlslcc_lib/ast_to_hir.cpp index a66651ec7f22..8d7007ac702c 100644 --- a/Engine/Source/ThirdParty/hlslcc/hlslcc/src/hlslcc_lib/ast_to_hir.cpp +++ b/Engine/Source/ThirdParty/hlslcc/hlslcc/src/hlslcc_lib/ast_to_hir.cpp @@ -1581,18 +1581,17 @@ ir_rvalue* ast_expression::hir(exec_list *instructions, struct _mesa_glsl_parse_ for (int i = 0; i < dim; ++i) { ir_constant* const array_index = new (ctx) ir_constant(i); - ir_dereference_array* array_bool = new(ctx)ir_dereference_array(op[0], array_index); + ir_dereference_array* array_bool = new(ctx)ir_dereference_array(op[0]->clone(ctx, nullptr), array_index); ir_dereference_array* array_out = new(ctx)ir_dereference_array(tmp, array_index); - ir_dereference_array* array_1 = new(ctx)ir_dereference_array(op[1], array_index); - ir_dereference_array* array_2 = new(ctx)ir_dereference_array(op[2], array_index); + ir_dereference_array* array_1 = new(ctx)ir_dereference_array(op[1]->clone(ctx, nullptr), array_index); + ir_dereference_array* array_2 = new(ctx)ir_dereference_array(op[2]->clone(ctx, nullptr), array_index); ir_if *const stmt = new(ctx)ir_if(array_bool); - stmt->then_instructions.push_tail(new(ctx)ir_assignment(array_out, array_1)); - stmt->else_instructions.push_tail(new(ctx)ir_assignment(array_out, array_2)); + stmt->then_instructions.push_tail(new(ctx)ir_assignment(new(ctx)ir_dereference_array(tmp, array_index), array_1)); + stmt->else_instructions.push_tail(new(ctx)ir_assignment(new(ctx)ir_dereference_array(tmp, array_index), array_2)); instructions->push_tail(stmt); } result = new(ctx)ir_dereference_variable(tmp); - } else if (apply_type_conversion(type, op[0], instructions, state, false, &loc)) { diff --git a/Engine/Source/ThirdParty/hlslcc/hlslcc/src/hlslcc_lib/ir_validate.cpp b/Engine/Source/ThirdParty/hlslcc/hlslcc/src/hlslcc_lib/ir_validate.cpp index 4797a36c27d6..939dde70d0a4 100644 --- a/Engine/Source/ThirdParty/hlslcc/hlslcc/src/hlslcc_lib/ir_validate.cpp +++ b/Engine/Source/ThirdParty/hlslcc/hlslcc/src/hlslcc_lib/ir_validate.cpp @@ -1009,7 +1009,7 @@ void ir_validate::validate_ir(ir_instruction *ir, void *data) if (hash_table_find(v->ht, ir)) { - _mesa_glsl_error(v->state, "internal compiler error: instruction node present twice in ir tree\n"); + _mesa_glsl_error(v->state, "internal compiler error: instruction node %d present twice in ir tree\n", ir->id); } hash_table_insert(v->ht, ir, ir); #endif diff --git a/Engine/Source/ThirdParty/libstrophe/libstrophe-0.9.1/BuildForUE/IOS/BuildForIOS.sh b/Engine/Source/ThirdParty/libstrophe/libstrophe-0.9.1/BuildForUE/IOS/BuildForIOS.sh index d1e1def40b94..0abc8222fb38 100755 --- a/Engine/Source/ThirdParty/libstrophe/libstrophe-0.9.1/BuildForUE/IOS/BuildForIOS.sh +++ b/Engine/Source/ThirdParty/libstrophe/libstrophe-0.9.1/BuildForUE/IOS/BuildForIOS.sh @@ -17,7 +17,7 @@ cd "${BUILD_DIR}" function build() { CONFIGURATION=$1 - xcodebuild -project libstrophe.xcodeproj -configuration $CONFIGURATION -destination generic/platform=iOS + xcodebuild BITCODE_GENERATION_MODE=bitcode -project libstrophe.xcodeproj -configuration $CONFIGURATION -destination generic/platform=iOS } build Release