Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

Question Take snapshot and render it in front of camera

Discussion in 'Project Tiny' started by Curly_chlorophytum, Jan 7, 2021.

  1. Curly_chlorophytum

    Curly_chlorophytum

    Joined:
    Nov 18, 2020
    Posts:
    6
    Hi everyone! Im new to unity and game development. Now Im making a web project using tiny.
    I need to render >25k static entity prefabs in front of the static camera. But performance is very low in wasm/asm builds on Androids and Chrome, I need more than 20fps for my UI buttons.

    So I think my solution could be:
    1) Take the snapshot of the current view (2D texture);
    2) Spawn a Plane object in front of the Camera view ;
    3) Add Material with 2d texture snapshot to the Plane ;
    4) Reduce Camera.clipZFar to hide all entities from rendering. Only the Plane with snapshot and UI will be rendered ;
    5) Delete snapshot if need ;

    I my case there will be 4 different static views(like drone view) with all entities and 1 dynamic where I don't need snapshots. I don't need to save snapshot, only store them at runntime.
    Is it possible to make in wasm/asm builds? Can you help me with 1-3, 5?
    Any thoughts?
     
  2. AbdulAlgharbi

    AbdulAlgharbi

    Unity Technologies

    Joined:
    Jul 27, 2018
    Posts:
    319
    Hi,
    its interesting problem but I feel like taking screenshot may not be the ideal solution
    Have you tried the release build?
    also, can you share more example project on what you are trying to achieve we may optimize a few things by looking at your code
    moreover you can use the unity profiler to profile your build and see what systems are taking most of the time so you can optimize your project accordingly
     
  3. Curly_chlorophytum

    Curly_chlorophytum

    Joined:
    Nov 18, 2020
    Posts:
    6
    Yes, I use release build. On ios(safari) and desktop (Chrome) performance is good, but poor on the android's chrome.

    By far I came up with this solution:
    At runntime I have 4 hidden entities(2d and 3d meshes) with tag components (ManSpriteTag, WomanSpriteTag, ManPrefabTag and WomenPrefabTag) and spawned 20k+ entities with DeadTag, ManTag(53%), WomenTag(47%).

    Based on the current view I iterate over 20k+ entities with DeadTag(mantag and womentag) and set new MeshRenderer Components from 4 tag entities.
    Still on drone view I got low fps for ui input, but its better than rendering 3d prefabs. Are there another ways how can I implement or optimize it?
    My code need some refactoring but key idea is readable. Thanks a lot!

    Code (CSharp):
    1.  
    2. public class UIViewSystem : SystemBase
    3.     {
    4.  
    5.  
    6.         private bool ViewTime;
    7.         public bool refresh = true;
    8.         protected override void OnUpdate()
    9.         {
    10.             var toggler = GetSingleton<Toggle>();
    11.             var isView = toggler.IsView;
    12.             if (ViewTime != isView)
    13.             {
    14.                 ViewTime = isView;
    15.                 refresh = true;
    16.             }
    17.             if (refresh)
    18.             {
    19.                 // change position of ui based on View
    20.                 Entities.ForEach((ref UITag ui, ref Translation pos) =>
    21.                 {
    22.                     if (isView && (ui.ShowOn == UITag.ShowCondition.IsView))
    23.                     {
    24.                         pos.Value = float3.zero;
    25.                     }
    26.                     if (isView && (ui.ShowOn != UITag.ShowCondition.IsView))
    27.                     {
    28.                         pos.Value = new float3(0f, -300f, 0f);
    29.                     }
    30.                     if (!isView && (ui.ShowOn == UITag.ShowCondition.IsMove))
    31.                     {
    32.                         pos.Value = float3.zero;
    33.                     }
    34.                     if (!isView && (ui.ShowOn != UITag.ShowCondition.IsMove))
    35.                     {
    36.                         pos.Value = new float3(0f, -300f, 0f);
    37.                     }
    38.                 }).Run();
    39.                 if (isView)
    40.                 {
    41.                     // existing enteties of meshes at runntime
    42.                     var manSpriteMesh = EntityManager.GetComponentData<MeshRenderer>(GetSingletonEntity<ManSpriteTag>());
    43.                     var womanSpriteMesh = EntityManager.GetComponentData<MeshRenderer>(GetSingletonEntity<WomanSpriteTag>());
    44.                     // get coordinates for View mode
    45.                     var defaultView = GetSingletonEntity<DefaultView>();
    46.                     var defaultPos = EntityManager.GetComponentData<Translation>(defaultView).Value;
    47.                     var defaultRot = EntityManager.GetComponentData<Rotation>(defaultView).Value;
    48.  
    49.  
    50.                     // Change visible area of camera
    51.                     Entities.ForEach((ref Camera cam, ref Rotation rot, ref Translation pos) =>
    52.                     {
    53.                         cam.clipZFar = 400f;
    54.                         rot.Value = defaultRot;
    55.                         pos.Value = defaultPos;
    56.                     }).ScheduleParallel();
    57.  
    58.                     // Turn off fog
    59.                     Entities.ForEach((ref Fog fog) =>
    60.                     {
    61.                         fog.mode = Fog.Mode.None;
    62.                     }).ScheduleParallel();
    63.  
    64.                     // Change man prefabs to 2dtexture
    65.                     Entities.WithAll<ManTag>().ForEach((Entity e, ref MeshRenderer mesh) =>
    66.                     {
    67.                         EntityManager.SetComponentData(e, manSpriteMesh);
    68.  
    69.                     }).WithoutBurst().Run();
    70.  
    71.                     // Change woman prefabs to 2dtexture
    72.                     Entities.WithAll<WomanTag>().ForEach((Entity e, ref MeshRenderer mesh) =>
    73.                     {
    74.                         EntityManager.SetComponentData(e, womanSpriteMesh);
    75.  
    76.                     }).WithoutBurst().Run();
    77.  
    78.                     //rotate entities to camera
    79.                     Entities.WithAll<DeadTag>().ForEach((ref Rotation rot, in Translation pos) =>
    80.                 {
    81.                     rot.Value = quaternion.LookRotationSafe((defaultPos - pos.Value), new float3(0f, 1f, 0f));
    82.  
    83.                 }).ScheduleParallel();
    84.  
    85.                 }
    86.                 if (!isView)
    87.                 {
    88.                     // existing enteties of meshes at runntime
    89.                     var manPrefabMesh = EntityManager.GetComponentData<MeshRenderer>(GetSingletonEntity<ManPrefabTag>());
    90.                     var womanPrefabMesh = EntityManager.GetComponentData<MeshRenderer>(GetSingletonEntity<WomanPrefabTag>());
    91.  
    92.                     // Change visible area of camera
    93.                     Entities.ForEach((ref Camera cam) =>
    94.                     {
    95.                         cam.clipZFar = 120f;
    96.                     }).ScheduleParallel();
    97.  
    98.                     //turn on 3d fog
    99.                     Entities.ForEach((ref Fog fog) =>
    100.                     {
    101.                         fog.mode = Fog.Mode.Linear;
    102.                     }).ScheduleParallel();
    103.  
    104.                     // change 2d textures to prefabs
    105.                     Entities.WithAll<ManTag>().ForEach((Entity e, ref MeshRenderer mesh) =>
    106.                     {
    107.                         EntityManager.SetComponentData(e, manPrefabMesh);
    108.  
    109.                     }).WithoutBurst().Run();
    110.  
    111.                     // change 2d textures to prefabs
    112.                     Entities.WithAll<WomanTag>().ForEach((Entity e, ref MeshRenderer mesh) =>
    113.                     {
    114.                         EntityManager.SetComponentData(e, womanPrefabMesh);
    115.  
    116.                     }).WithoutBurst().Run();
    117.  
    118.  
    119.                     // Rotate prefabs to target
    120.                     Entities.WithAll<DeadTag>().ForEach((ref Rotation rot, in Translation pos) =>
    121.                 {
    122.                     rot.Value = quaternion.LookRotationSafe((new float3(-95f, 0f, -1.2f) - pos.Value), new float3(0f, 1f, 0f));
    123.  
    124.                 }).ScheduleParallel();
    125.                 }
    126.  
    127.                 refresh = false;
    128.             }
    129.         }
    130.     }
    131. }
    132.  

    This is my spawner system
    Code (CSharp):
    1.  
    2. protected override void OnUpdate()
    3.         {
    4.             var refresher = GetSingleton<CountText>();
    5.             var dead = GetSingleton<EntityCompData>();
    6.             var toggle = GetSingleton<Toggle>();
    7.             if (!started)
    8.             {
    9.                 world = EntityManager.Instantiate(dead.worldPrefab);
    10.                 EntityManager.SetComponentData(world, new Translation { Value = float3.zero });
    11.                 started = true;
    12.             }
    13.             if (refresher.Refresh && (refresher.CountPrefabs != humanCounter))
    14.             {
    15.  
    16.                 if (humanCounter != 0)
    17.                 {
    18.                     DestroyEntities();
    19.                 }
    20.                 humanCounter = refresher.CountPrefabs;
    21.                 // target for lookRotation
    22.                 var target = new float3(-95f, 0f, -1.2f);
    23.  
    24.                 if (!toggle.IsView)
    25.                 {
    26.                     for (int i = 0; i < humanCounter; i++)
    27.                     {
    28.                         entityPosition = new float3(xCords[i], 0, yCords[i]);
    29.                         // 53% are men
    30.                         if (i % 2 == 0 || i % 100 < 4)
    31.                             deadEntity = EntityManager.Instantiate(dead.manPrefab);
    32.                         else
    33.                             deadEntity = EntityManager.Instantiate(dead.womanPrefab);
    34.  
    35.  
    36.                         EntityManager.SetComponentData(deadEntity, new Translation { Value = entityPosition });
    37.  
    38.                     }
    39.                 }
    40.  
    41.                 if (toggle.IsView)
    42.                 {
    43.                     // change target if View
    44.                     target = EntityManager.GetComponentData<Translation>(GetSingletonEntity<Camera>()).Value;
    45.                     for (int i = 0; i < humanCounter; i++)
    46.                     {
    47.                         entityPosition = new float3(xCords[i], 0, yCords[i]);
    48.                         // 53% are men
    49.                         if (i % 2 == 0 || i % 100 < 4)
    50.                             deadEntity = EntityManager.Instantiate(dead.manSprite);
    51.                         else
    52.                             deadEntity = EntityManager.Instantiate(dead.womanSprite);
    53.  
    54.  
    55.                         EntityManager.SetComponentData(deadEntity, new Translation { Value = entityPosition });
    56.  
    57.                     }
    58.                 }
    59.  
    60.                 Entities.WithAll<DeadTag>().ForEach((ref Rotation rot, in Translation pos) =>
    61.                 {
    62.                     // TODO implement random noise for rotation.
    63.                     // var rand = new Random((uint)pos.Value.x);  new float3(-95f + rand.NextFloat(-10f, 10f), 0f, -1.2f + rand.NextFloat(-10f, 10f))
    64.                     rot.Value = quaternion.LookRotationSafe((target - pos.Value), new float3(0f, 1f, 0f));
    65.  
    66.                 }).ScheduleParallel();
    67.  
    68.             }
    69.  
    70.             // turn off refresher after refresh
    71.             refresher.Refresh = false;
    72.             SetSingleton(refresher);
    73.         }
    74.         private void DestroyEntities()
    75.         {
    76.             Entities.WithAll<DeadTag>().WithStructuralChanges().ForEach((Entity ent) =>
    77.             {
    78.                 EntityManager.DestroyEntity(ent);
    79.             }).Run();
    80.         }
    81.     }
    82. }
    83.  
     
  4. AbdulAlgharbi

    AbdulAlgharbi

    Unity Technologies

    Joined:
    Jul 27, 2018
    Posts:
    319
    I wonder how complicated your meshes are. if the 3d mesh has multiple sub meshes try to combine them into one
    that will significantly reduce the draw calls and potentially improve the performance
    we also have UI support in TIny 0.32 that is coming this month
    you should try it out and see if you get better performance
     
  5. Curly_chlorophytum

    Curly_chlorophytum

    Joined:
    Nov 18, 2020
    Posts:
    6
    My 3d meshes have only 1 submesh, they are low-poly with 270 triangles, 816 indices.
    2d meshes have 18 triangles, 54 indices.
    In profiler I can see that in case when there are a lot of 2d meshes to render: Tiny.Rendering.SubmitFrameSystem(75ms), Tiny.Rendering.SubmitSystemGroup(26ms) and Tiny.Rendering.SubmitStaticLitMeshChunked(26ms) take most of the time. My systems take much less time. This profiler is for the desktop web Chrome, on Android devices it becomes very-very slow

    I also tried to use multithreading option for wasm, but I get error on release build. I found, that it'll be fixed in 0.32, is it true? Will it improve performance for android web?

    Also is it possible to pause rendering scene(show one frame) until user press ui button?
     
    Last edited: Jan 14, 2021
  6. AbdulAlgharbi

    AbdulAlgharbi

    Unity Technologies

    Joined:
    Jul 27, 2018
    Posts:
    319
    Yes, the multithreading issue is fixed in 0.32
    I can't tell if it will improve the performance or not because that depends on the hardware
    but I guess what you really need is GPU instancing
     
  7. Curly_chlorophytum

    Curly_chlorophytum

    Joined:
    Nov 18, 2020
    Posts:
    6
    @AbdulAlgharbi
    Does Tiny support GPU instancing now? If not, when will it be supported?
     
  8. AbdulAlgharbi

    AbdulAlgharbi

    Unity Technologies

    Joined:
    Jul 27, 2018
    Posts:
    319
    No, it doesn't have GPU instancing
    It's planned but it may take some time to get it ready
     
    newguy123 likes this.
  9. Curly_chlorophytum

    Curly_chlorophytum

    Joined:
    Nov 18, 2020
    Posts:
    6
    @AbdulAlgharbi Thanks for response)
    I found a way to pause my project using JavaScript function { _onpauseapp(1) } in wasm build.
    I wonder is it possible to implement part of my UI buttons in html template?
    So workflow may look like this:
    1) In C# when user click on UI pause button it pauses app and append <div class='UI-buttons'>...</div> above canvas.
    2) In <div class='UI-buttons'>...</div> user can input number and press "apply"
    3) When 'apply' is pressed it hides <div class='UI-buttons'>...</div> and resume app { _onpauseapp(0) }
    4) Then JavaScript function passes data to C#: 1 boolean for refresh and integer number from the input in the IComponentData
    5) My Systems refresh accordingly to the new data

    What do you think?
     
    tonialatalo likes this.
  10. AbdulAlgharbi

    AbdulAlgharbi

    Unity Technologies

    Joined:
    Jul 27, 2018
    Posts:
    319
    Hi
    We don't have an API in tiny where you can pause the game
    but I see you are using JavaScript to bypass that which is good as long as its working for your use case