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