diff --git a/testproject/Assets/Prefabs/PerfTestNetworkObject.prefab b/testproject/Assets/Prefabs/PerfTestNetworkObject.prefab new file mode 100644 index 0000000000..91de39f222 --- /dev/null +++ b/testproject/Assets/Prefabs/PerfTestNetworkObject.prefab @@ -0,0 +1,49 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &5637023994061915634 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5637023994061915632} + - component: {fileID: 5637023994061915633} + m_Layer: 0 + m_Name: PerfTestNetworkObject + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5637023994061915632 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5637023994061915634} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &5637023994061915633 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5637023994061915634} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: + GlobalObjectIdHash: 951099334 + AlwaysReplicateAsRoot: 0 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 diff --git a/testproject/Assets/Prefabs/PerfTestNetworkObject.prefab.meta b/testproject/Assets/Prefabs/PerfTestNetworkObject.prefab.meta new file mode 100644 index 0000000000..feebc7c48c --- /dev/null +++ b/testproject/Assets/Prefabs/PerfTestNetworkObject.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b0952a471c5a147cb92f6afcdb648f8a +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Prefabs/PerfTestVisualNetworkObject.prefab b/testproject/Assets/Prefabs/PerfTestVisualNetworkObject.prefab new file mode 100644 index 0000000000..6673fbefcd --- /dev/null +++ b/testproject/Assets/Prefabs/PerfTestVisualNetworkObject.prefab @@ -0,0 +1,100 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &9115731988109684252 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9115731988109684241} + - component: {fileID: 9115731988109684240} + - component: {fileID: 9115731988109684243} + - component: {fileID: 9115731988109684253} + m_Layer: 0 + m_Name: PerfTestVisualNetworkObject + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &9115731988109684241 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9115731988109684252} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &9115731988109684240 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9115731988109684252} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &9115731988109684243 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9115731988109684252} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &9115731988109684253 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9115731988109684252} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: + GlobalObjectIdHash: 951099334 + AlwaysReplicateAsRoot: 0 + DontDestroyWithOwner: 0 diff --git a/testproject/Assets/Prefabs/PerfTestVisualNetworkObject.prefab.meta b/testproject/Assets/Prefabs/PerfTestVisualNetworkObject.prefab.meta new file mode 100644 index 0000000000..f268093417 --- /dev/null +++ b/testproject/Assets/Prefabs/PerfTestVisualNetworkObject.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c2851feb7276442cc86a6f2d1d69ea11 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Scenes/MultiprocessTestScene.unity b/testproject/Assets/Scenes/MultiprocessTestScene.unity new file mode 100644 index 0000000000..d4a6e985ce --- /dev/null +++ b/testproject/Assets/Scenes/MultiprocessTestScene.unity @@ -0,0 +1,922 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657874, g: 0.49641275, b: 0.5748172, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 130932425} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &127222500 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 127222502} + - component: {fileID: 127222501} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &127222501 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 127222500} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &127222502 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 127222500} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!850595691 &130932425 +LightingSettings: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + serializedVersion: 3 + m_GIWorkflowMode: 1 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_RealtimeEnvironmentLighting: 1 + m_BounceScale: 1 + m_AlbedoBoost: 1 + m_IndirectOutputScale: 1 + m_UsingShadowmask: 1 + m_BakeBackend: 1 + m_LightmapMaxSize: 1024 + m_BakeResolution: 40 + m_Padding: 2 + m_TextureCompression: 1 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAO: 0 + m_MixedBakeMode: 2 + m_LightmapsBakeMode: 1 + m_FilterMode: 1 + m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_RealtimeResolution: 2 + m_ForceWhiteAlbedo: 0 + m_ForceUpdates: 0 + m_FinalGather: 0 + m_FinalGatherRayCount: 256 + m_FinalGatherFiltering: 1 + m_PVRCulling: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_LightProbeSampleCountMultiplier: 4 + m_PVRBounces: 2 + m_PVRMinBounces: 1 + m_PVREnvironmentMIS: 1 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 +--- !u!1 &160940364 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 160940368} + - component: {fileID: 160940367} + - component: {fileID: 160940366} + - component: {fileID: 160940365} + m_Layer: 0 + m_Name: Boundary bottom left + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!135 &160940365 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 160940364} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &160940366 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 160940364} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &160940367 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 160940364} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &160940368 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 160940364} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -10, y: -10, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &941021721 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 941021724} + - component: {fileID: 941021723} + - component: {fileID: 941021722} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &941021722 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 941021721} + m_Enabled: 1 +--- !u!20 &941021723 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 941021721} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &941021724 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 941021721} + m_LocalRotation: {x: 0.21736304, y: -0, z: -0, w: 0.97609085} + m_LocalPosition: {x: 0, y: 9.15, z: -27.5} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 25.108, y: 0, z: 0} +--- !u!1 &996484657 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 996484661} + - component: {fileID: 996484660} + - component: {fileID: 996484659} + - component: {fileID: 996484658} + m_Layer: 0 + m_Name: Boundary center + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!135 &996484658 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 996484657} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &996484659 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 996484657} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &996484660 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 996484657} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &996484661 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 996484657} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1206022453 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1206022457} + - component: {fileID: 1206022456} + - component: {fileID: 1206022455} + - component: {fileID: 1206022454} + m_Layer: 0 + m_Name: Boundary top right + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!135 &1206022454 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1206022453} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1206022455 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1206022453} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1206022456 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1206022453} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1206022457 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1206022453} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 10, y: 10, z: 10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1211923374 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1211923376} + - component: {fileID: 1211923375} + - component: {fileID: 1211923377} + - component: {fileID: 1211923378} + m_Layer: 0 + m_Name: NetworkManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1211923375 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1211923374} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 593a2fe42fa9d37498c96f9a383b6521, type: 3} + m_Name: + m_EditorClassIdentifier: + DontDestroy: 0 + RunInBackground: 1 + LogLevel: 1 + NetworkConfig: + ProtocolVersion: 0 + NetworkTransport: {fileID: 1674777073} + RegisteredScenes: + - MultiprocessTestScene + - SampleScene + AllowRuntimeSceneChanges: 0 + PlayerPrefab: {fileID: 4700706668509470175, guid: 7eeaaf9e50c0afc4dab93584a54fb0d6, + type: 3} + NetworkPrefabs: + - Override: 0 + Prefab: {fileID: 9115731988109684252, guid: c2851feb7276442cc86a6f2d1d69ea11, + type: 3} + SourcePrefabToOverride: {fileID: 0} + SourceHashToOverride: 0 + OverridingTargetPrefab: {fileID: 0} + ReceiveTickrate: 64 + NetworkTickIntervalSec: 0.05 + MaxReceiveEventsPerTickRate: -1 + EventTickrate: 64 + ClientConnectionBufferTimeout: 10 + ConnectionApproval: 0 + ConnectionData: + EnableTimeResync: 0 + TimeResyncInterval: 30 + EnableNetworkVariable: 1 + EnsureNetworkVariableLengthSafety: 0 + EnableSceneManagement: 1 + ForceSamePrefabs: 1 + RecycleNetworkIds: 1 + NetworkIdRecycleDelay: 120 + RpcHashSize: 0 + LoadSceneTimeOut: 120 + EnableMessageBuffering: 1 + MessageBufferTimeout: 20 + EnableNetworkLogs: 1 +--- !u!4 &1211923376 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1211923374} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1674777072} + - {fileID: 2027640072} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1211923377 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1211923374} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 068bf11ceb1344667af4cc40950f44f4, type: 3} + m_Name: + m_EditorClassIdentifier: + referencedPrefab: {fileID: 5637023994061915634, guid: b0952a471c5a147cb92f6afcdb648f8a, + type: 3} +--- !u!114 &1211923378 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1211923374} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 55d1c75ce242745ac98f7e7aca6d2d19, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1 &1274245423 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1274245425} + - component: {fileID: 1274245424} + - component: {fileID: 1274245426} + m_Layer: 0 + m_Name: TestCoordinator + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1274245424 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1274245423} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: + GlobalObjectIdHash: 2217825759 + AlwaysReplicateAsRoot: 0 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 1 +--- !u!4 &1274245425 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1274245423} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 10, y: 10, z: 10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1274245426 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1274245423} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ef1240e0784f84eadb77fe822e2e03c7, type: 3} + m_Name: + m_EditorClassIdentifier: + isRegistering: 0 + hasRegistered: 0 +--- !u!1 &1674777071 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1674777072} + - component: {fileID: 1674777073} + m_Layer: 0 + m_Name: UNET + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1674777072 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1674777071} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1211923376} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1674777073 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1674777071} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b84c2d8dfe509a34fb59e2b81f8e1319, type: 3} + m_Name: + m_EditorClassIdentifier: + MessageBufferSize: 50000 + MaxConnections: 100 + MaxSentMessageQueueSize: 50000 + ConnectAddress: 127.0.0.1 + ConnectPort: 7777 + ServerListenPort: 7777 + ServerWebsocketListenPort: 8887 + SupportWebsocket: 0 + Channels: [] + UseMLAPIRelay: 0 + MLAPIRelayAddress: 184.72.104.138 + MLAPIRelayPort: 8888 + MessageSendMode: 0 +--- !u!1 &2027640071 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2027640072} + - component: {fileID: 2027640073} + m_Layer: 0 + m_Name: UTP + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2027640072 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2027640071} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1211923376} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &2027640073 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2027640071} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc5ef7b69296d69458910681f29471e6, type: 3} + m_Name: + m_EditorClassIdentifier: + Port: 7777 + Address: 127.0.0.1 diff --git a/testproject/Assets/Scenes/MultiprocessTestScene.unity.meta b/testproject/Assets/Scenes/MultiprocessTestScene.unity.meta new file mode 100644 index 0000000000..5a9d45d780 --- /dev/null +++ b/testproject/Assets/Scenes/MultiprocessTestScene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 76743cb7b342c49279327834918a9c6e +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/BaseMultiprocessTests.cs b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/BaseMultiprocessTests.cs new file mode 100644 index 0000000000..f6b82d71db --- /dev/null +++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/BaseMultiprocessTests.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.TestTools; + +namespace MLAPI.MultiprocessRuntimeTests +{ + public class MultiprocessTestsAttribute : CategoryAttribute + { + public const string MultiprocessCategoryName = "Multiprocess"; + public MultiprocessTestsAttribute() : base(MultiprocessCategoryName) { } + } + + [MultiprocessTests] + public abstract class BaseMultiprocessTests + { + protected virtual bool m_IsPerformanceTest => true; + + private bool ShouldIgnoreTests => m_IsPerformanceTest && Application.isEditor || MultiprocessOrchestration.IsUsingUTR(); // todo remove UTR check once we have proper automation + + /// + /// Implement this to specify the amount of workers to spawn from your main test runner + /// TODO there's a good chance this will be refactored with something fancier once we start integrating with bokken + /// + protected abstract int WorkerCount { get; } + + [OneTimeSetUp] + public virtual void SetupTestSuite() + { + if (ShouldIgnoreTests) + { + Assert.Ignore("Ignoring tests that shouldn't run from unity editor. Performance tests should be run from remote test execution on device (this can be ran using the \"run selected tests (your platform)\" button"); + } + + SceneManager.LoadScene(BuildMultiprocessTestPlayer.MainSceneName, LoadSceneMode.Single); + SceneManager.sceneLoaded += OnSceneLoaded; + + for (int i = 0; i < WorkerCount; i++) + { + MultiprocessOrchestration.StartWorkerNode(); // will automatically start built player as clients + } + } + + private static void OnSceneLoaded(Scene scene, LoadSceneMode mode) + { + SceneManager.sceneLoaded -= OnSceneLoaded; + NetworkManager.Singleton.StartHost(); + } + + [UnitySetUp] + public virtual IEnumerator Setup() + { + yield return new WaitUntil(() => NetworkManager.Singleton != null && NetworkManager.Singleton.IsServer); + + var startTime = Time.time; + while (NetworkManager.Singleton.ConnectedClients.Count <= WorkerCount) + { + yield return new WaitForSeconds(0.2f); + + if (Time.time - startTime > TestCoordinator.MaxWaitTimeoutSec) + { + throw new Exception($"waiting too long to see clients to connect, got {NetworkManager.Singleton.ConnectedClients.Count - 1} clients, but was expecting {WorkerCount}, failing"); + } + } + + TestCoordinator.Instance.KeepAliveClientRpc(); + } + + [TearDown] + public virtual void Teardown() + { + if (!ShouldIgnoreTests) + { + TestCoordinator.Instance.TestRunTeardown(); + } + } + + [OneTimeTearDown] + public virtual void TeardownSuite() + { + if (!ShouldIgnoreTests) + { + TestCoordinator.Instance.CloseRemoteClientRpc(); + NetworkManager.Singleton.StopHost(); + } + } + } +} + diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/BaseMultiprocessTests.cs.meta b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/BaseMultiprocessTests.cs.meta new file mode 100644 index 0000000000..6b52bd9e90 --- /dev/null +++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/BaseMultiprocessTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f77e60aa394b9419784b6c46618fb553 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs index 77f877a115..4bfc7ab20f 100644 --- a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs +++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs @@ -15,7 +15,7 @@ public static class BuildMultiprocessTestPlayer public const string MultiprocessBaseMenuName = "MLAPI/Multiprocess Test"; public const string BuildAndExecuteMenuName = MultiprocessBaseMenuName + "/Build Test Player #t"; public const string MainSceneName = "MultiprocessTestScene"; - private static string BuildPathDirectory => Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds","MultiprocessTests"); + private static string BuildPathDirectory => Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds", "MultiprocessTests"); public static string BuildPath => Path.Combine(BuildPathDirectory, "MultiprocessTestPlayer"); #if UNITY_EDITOR diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs index b09ee09914..6dec09b4cb 100644 --- a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs +++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs @@ -2,6 +2,7 @@ using System.ComponentModel; using System.Diagnostics; using System.IO; +using System.Linq; using UnityEngine; using Debug = UnityEngine.Debug; @@ -60,4 +61,10 @@ public static void StartWorkerNode() throw; } } + + // todo remove this once we have proper automation + public static bool IsUsingUTR() + { + return Environment.GetCommandLineArgs().Contains("-automated"); + } } diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/TestCoordinator.cs b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/TestCoordinator.cs new file mode 100644 index 0000000000..fc0efec2e1 --- /dev/null +++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/TestCoordinator.cs @@ -0,0 +1,331 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using MLAPI; +using MLAPI.Messaging; +using NUnit.Framework; +using UnityEngine; +using Debug = UnityEngine.Debug; + +/// +/// TestCoordinator +/// Used for coordinating multiprocess end to end tests. Used to call RPCs on other nodes and gather results +/// This is needed to coordinate server and client execution steps. The current remote player test runner hardcodes test +/// to run in a bootstrap scene before launching the player and doesn't call each tests individually. There's not opportunity +/// to coordinate test execution between client and server with that model. +/// The only per tests communication already existing is to get the results per test as they are running +/// With this test coordinator, it's not possible to start a main test node with the test runner and have that server start other worker nodes +/// on which to execute client tests. We use MLAPI as both a test framework and as the target of our performance tests. +/// +[RequireComponent(typeof(NetworkObject))] +public class TestCoordinator : NetworkBehaviour +{ + public const int PerTestTimeoutSec = 5 * 60; // seconds + + public const float MaxWaitTimeoutSec = 20; + private const char k_MethodFullNameSplitChar = '@'; + + private bool m_ShouldShutdown; + private float m_TimeSinceLastConnected; + private float m_TimeSinceLastKeepAlive; + + public static TestCoordinator Instance; + + private Dictionary> m_TestResultsLocal = new Dictionary>(); // this isn't super efficient, but since it's used for signaling around the tests, shouldn't be too bad + private Dictionary m_ClientIsFinished = new Dictionary(); + + public static List AllClientIdsWithResults => Instance.m_TestResultsLocal.Keys.ToList(); + public static List AllClientIdsExceptMine => NetworkManager.Singleton.ConnectedClients.Keys.ToList().FindAll(client => client != NetworkManager.Singleton.LocalClientId); + + private void Awake() + { + if (Instance != null) + { + Debug.LogError("Multiple test coordinator, destroying this instance"); + Destroy(gameObject); + return; + } + + Instance = this; + } + + public void Start() + { + bool isClient = Environment.GetCommandLineArgs().Any(value => value == MultiprocessOrchestration.IsWorkerArg); + if (isClient) + { + Debug.Log("starting MLAPI client"); + NetworkManager.Singleton.StartClient(); + } + + NetworkManager.OnClientDisconnectCallback += OnClientDisconnectCallback; + + // ExecuteStepInContext.InitializeAllSteps(); + } + + public void Update() + { + if (Time.time - m_TimeSinceLastKeepAlive > PerTestTimeoutSec) + { + QuitApplication(); + Assert.Fail("Stayed idle too long"); + } + + if ((IsServer && NetworkManager.Singleton.IsListening) || (IsClient && NetworkManager.Singleton.IsConnectedClient)) + { + m_TimeSinceLastConnected = Time.time; + } + else if (Time.time - m_TimeSinceLastConnected > MaxWaitTimeoutSec || m_ShouldShutdown) + { + // Make sure we don't have zombie processes + Debug.Log($"quitting application, shouldShutdown set to {m_ShouldShutdown}, is listening {NetworkManager.Singleton.IsListening}, is connected client {NetworkManager.Singleton.IsConnectedClient}"); + if (!m_ShouldShutdown) + { + QuitApplication(); + Assert.Fail($"something wrong happened, was not connected for {Time.time - m_TimeSinceLastConnected} seconds"); + } + } + } + + private static void QuitApplication() + { +#if UNITY_EDITOR + UnityEditor.EditorApplication.isPlaying = false; +#else + Application.Quit(); +#endif + } + + public void TestRunTeardown() + { + m_TestResultsLocal.Clear(); + } + + public void OnDestroy() + { + if (NetworkObject != null && NetworkManager != null) + { + NetworkManager.OnClientDisconnectCallback -= OnClientDisconnectCallback; + } + } + + private static void OnClientDisconnectCallback(ulong clientId) + { + if (clientId == NetworkManager.Singleton.ServerClientId || clientId == NetworkManager.Singleton.LocalClientId) + { + // if disconnect callback is for me or for server, quit, we're done here + Debug.Log($"received disconnect from {clientId}, quitting"); + QuitApplication(); + } + } + + private static string GetMethodInfo(Action method) + { + return $"{method.Method.DeclaringType.FullName}{k_MethodFullNameSplitChar}{method.Method.Name}"; + } + + private static string GetMethodInfo(Action method) + { + return $"{method.Method.DeclaringType.FullName}{k_MethodFullNameSplitChar}{method.Method.Name}"; + } + + public static IEnumerable<(ulong clientId, float result)> ConsumeCurrentResult() + { + foreach (var kv in Instance.m_TestResultsLocal) + { + while (kv.Value.Count > 0) + { + var toReturn = (kv.Key, kv.Value[0]); + kv.Value.RemoveAt(0); + yield return toReturn; + } + } + } + + public static IEnumerable ConsumeCurrentResult(ulong clientID) + { + var allResults = Instance.m_TestResultsLocal[clientID]; + while (allResults.Count > 0) + { + var toReturn = allResults[0]; + allResults.RemoveAt(0); + yield return toReturn; + } + } + + public static float PeekLatestResult(ulong clientId) + { + if (Instance.m_TestResultsLocal.ContainsKey(clientId) && Instance.m_TestResultsLocal[clientId].Count > 0) + { + return Instance.m_TestResultsLocal[clientId].Last(); + } + + return float.NaN; + } + + /// + /// Returns appropriate lambda according to parameters + /// Includes time check to make sure this times out + /// + /// + /// + /// + public static Func ResultIsSet(bool useTimeoutException = true) + { + var startWaitTime = Time.time; + return () => + { + if (Time.time - startWaitTime > MaxWaitTimeoutSec) + { + if (useTimeoutException) + { + throw new Exception($"timeout while waiting for results, didn't get results for {Time.time - startWaitTime} seconds"); + } + + return true; + } + + foreach (var clientIdAndTestResultList in Instance.m_TestResultsLocal) + { + if (clientIdAndTestResultList.Value.Count > 0) + { + return true; + } + } + + return false; + }; + } + + public static Func ConsumeClientIsFinished(ulong clientId, bool useTimeoutException = true) + { + var startWaitTime = Time.time; + return () => + { + if (Time.time - startWaitTime > MaxWaitTimeoutSec) + { + if (useTimeoutException) + { + throw new Exception($"timeout while waiting for client finished, didn't get results for {Time.time - startWaitTime} seconds"); + } + else + { + return true; + } + } + + if (Instance.m_ClientIsFinished.ContainsKey(clientId) && Instance.m_ClientIsFinished[clientId]) + { + Instance.m_ClientIsFinished[clientId] = false; // consume + return true; + } + + return false; + }; + } + + [ServerRpc(RequireOwnership = false)] + public void ClientFinishedServerRpc(ServerRpcParams p = default) + { + // signal from clients to the server to say the client is done with it's task + m_ClientIsFinished[p.Receive.SenderClientId] = true; + } + + public void InvokeFromMethodActionRpc(Action methodInfo, params byte[] args) + { + var methodInfoString = GetMethodInfo(methodInfo); + InvokeFromMethodNameClientRpc(methodInfoString, args, new ClientRpcParams { Send = new ClientRpcSendParams { TargetClientIds = AllClientIdsExceptMine.ToArray() } }); + } + + public void InvokeFromMethodActionRpc(Action methodInfo) + { + var methodInfoString = GetMethodInfo(methodInfo); + InvokeFromMethodNameClientRpc(methodInfoString, null, new ClientRpcParams { Send = new ClientRpcSendParams { TargetClientIds = AllClientIdsExceptMine.ToArray() } }); + } + + [ClientRpc] + public void TriggerActionIDClientRpc(string actionID, byte[] args, ClientRpcParams clientRpcParams = default) + { + Debug.Log($"received RPC from server, client side triggering action ID {actionID}"); + try + { + //ExecuteStepInContext.AllActions[actionId].Invoke(args); + } + catch (Exception e) + { + WriteErrorServerRpc(e.Message); + throw; + } + } + + [ClientRpc] + public void InvokeFromMethodNameClientRpc(string methodInfoString, byte[] args, ClientRpcParams clientRpcParams = default) + { + try + { + var split = methodInfoString.Split(k_MethodFullNameSplitChar); + var (classToExecute, staticMethodToExecute) = (split[0], split[1]); + + var foundType = Type.GetType(classToExecute); + if (foundType == null) + { + throw new Exception($"couldn't find {classToExecute}"); + } + + var foundMethod = foundType.GetMethod(staticMethodToExecute, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + if (foundMethod == null) + { + throw new MissingMethodException($"couldn't find method {staticMethodToExecute}"); + } + + foundMethod.Invoke(null, args != null ? new object[] { args } : null); + } + catch (Exception e) + { + WriteErrorServerRpc(e.Message); + throw; + } + } + + [ClientRpc] + public void CloseRemoteClientRpc() + { + try + { + NetworkManager.Singleton.StopClient(); + m_ShouldShutdown = true; // wait until isConnectedClient is false to run Application Quit in next update + Debug.Log("Quitting player cleanly"); + Application.Quit(); + } + catch (Exception e) + { + WriteErrorServerRpc(e.Message); + throw; + } + } + + [ClientRpc] + public void KeepAliveClientRpc() + { + m_TimeSinceLastKeepAlive = Time.time; + } + + [ServerRpc(RequireOwnership = false)] + public void WriteTestResultsServerRpc(float result, ServerRpcParams receiveParams = default) + { + var senderId = receiveParams.Receive.SenderClientId; + if (!m_TestResultsLocal.ContainsKey(senderId)) + { + m_TestResultsLocal[senderId] = new List(); + } + + m_TestResultsLocal[senderId].Add(result); + } + + [ServerRpc(RequireOwnership = false)] + public void WriteErrorServerRpc(string errorMessage, ServerRpcParams receiveParams = default) + { + Debug.LogError($"Got Exception client side {errorMessage}, from client {receiveParams.Receive.SenderClientId}"); + } +} diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/TestCoordinator.cs.meta b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/TestCoordinator.cs.meta new file mode 100644 index 0000000000..f8c2a3c472 --- /dev/null +++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/TestCoordinator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ef1240e0784f84eadb77fe822e2e03c7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/TestCoordinatorTests.cs b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/TestCoordinatorTests.cs new file mode 100644 index 0000000000..79d53ed053 --- /dev/null +++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/TestCoordinatorTests.cs @@ -0,0 +1,69 @@ +using System.Collections; +using System.Linq; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace MLAPI.MultiprocessRuntimeTests +{ + [TestFixture(1)] + [TestFixture(2)] + public class TestCoordinatorTests : BaseMultiprocessTests + { + private int m_WorkerCount; + protected override int WorkerCount => m_WorkerCount; + + protected override bool m_IsPerformanceTest => false; + + public TestCoordinatorTests(int workerCount) + { + m_WorkerCount = workerCount; + } + + private static void ExecuteSimpleCoordinatorTest() + { + TestCoordinator.Instance.WriteTestResultsServerRpc(float.PositiveInfinity); + } + + private static void ExecuteWithArgs(byte[] args) + { + TestCoordinator.Instance.WriteTestResultsServerRpc(args[0]); + } + + [UnityTest] + public IEnumerator CheckTestCoordinator() + { + // Sanity check for TestCoordinator + // Call the method + TestCoordinator.Instance.InvokeFromMethodActionRpc(ExecuteSimpleCoordinatorTest); + + var nbResults = 0; + for (int i = 0; i < WorkerCount; i++) // wait and test for the two clients + { + yield return new WaitUntil(TestCoordinator.ResultIsSet()); + + var (clientId, result) = TestCoordinator.ConsumeCurrentResult().Take(1).Single(); + Assert.Greater(result, 0f); + nbResults++; + } + Assert.That(nbResults, Is.EqualTo(WorkerCount)); + } + + [UnityTest] + public IEnumerator CheckTestCoordinatorWithArgs() + { + TestCoordinator.Instance.InvokeFromMethodActionRpc(ExecuteWithArgs, 99); + var nbResults = 0; + + for (int i = 0; i < WorkerCount; i++) // wait and test for the two clients + { + yield return new WaitUntil(TestCoordinator.ResultIsSet()); + + var (clientId, result) = TestCoordinator.ConsumeCurrentResult().Take(1).Single(); + Assert.That(result, Is.EqualTo(99)); + nbResults++; + } + Assert.That(nbResults, Is.EqualTo(WorkerCount)); + } + } +} diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/TestCoordinatorTests.cs.meta b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/TestCoordinatorTests.cs.meta new file mode 100644 index 0000000000..104be914d0 --- /dev/null +++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/TestCoordinatorTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f5e62651568514685a0b50d623fa8a96 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/com.unity.multiplayer.mlapi.multiprocessruntime.asmdef b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/com.unity.multiplayer.mlapi.multiprocessruntime.asmdef new file mode 100644 index 0000000000..43f80edd04 --- /dev/null +++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/com.unity.multiplayer.mlapi.multiprocessruntime.asmdef @@ -0,0 +1,25 @@ +{ + "name": "Unity.Multiplayer.MLAPI.MultiprocessRuntime", + "rootNamespace": "", + "references": [ + "Unity.Multiplayer.MLAPI.Runtime", + "UnityEngine.TestRunner", + "UnityEditor.TestRunner", + "ScriptsForAutomatedTesting", + "Unity.PerformanceTesting", + "MultiprocessTestsHelpers" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll" + ], + "autoReferenced": false, + "defineConstraints": [ + "UNITY_INCLUDE_TESTS" + ], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/com.unity.multiplayer.mlapi.multiprocessruntime.asmdef.meta b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/com.unity.multiplayer.mlapi.multiprocessruntime.asmdef.meta new file mode 100644 index 0000000000..a06b37d08b --- /dev/null +++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/com.unity.multiplayer.mlapi.multiprocessruntime.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 66273ab9e01074f7da305fe84e13da47 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/ProjectSettings/EditorBuildSettings.asset b/testproject/ProjectSettings/EditorBuildSettings.asset index b0682b43e8..0aad2d2bec 100644 --- a/testproject/ProjectSettings/EditorBuildSettings.asset +++ b/testproject/ProjectSettings/EditorBuildSettings.asset @@ -44,4 +44,7 @@ EditorBuildSettings: - enabled: 1 path: Assets/Tests/Manual/NetworkAnimatorTests/NetworkAnimatorEnhancement.unity guid: f88da8bb8d07e11418eaad6524d5cc12 + - enabled: 1 + path: Assets/Scenes/MultiprocessTestScene.unity + guid: 76743cb7b342c49279327834918a9c6e m_configObjects: {}