It is currently Sat Aug 22, 2020 4:54 am


All times are UTC




Post new topic Reply to topic  [ 10 posts ] 
Author Message
 Post subject: Optimizing scrolling engine
PostPosted: Tue Jul 18, 2017 7:30 am 

Joined: Mon May 15, 2017 7:35 am
Posts: 8
Hello,

I am using Cubquity to generate a scrolling voxel terrain. The idea is to simulate a 2D isometric perspective using an orthographic camera. What I'm doing:

    - Generate a tile-based map in memory (e.g. 1000x1000 tiles)
    - Use VolumeData.SetVoxel to render a subset of the map on screen (e.g. 100x100)
    - When the player scrolls on the x axis, on each frame check whether he has traveled far enough to scroll. To scroll right:
      - Render a new column from the tile-based map to become the rightmost column rendered.
      - Remove the leftmost column by removing all associated voxels from the display

Here is some sample code:

Code:
          // Add rightmost column
         for (int i = 0; i < 100; i++) {
            Tile tile = mapManager.GetTile (rightMostX, i);
            QuantizedColor color;
            ...
            data.SetVoxel (rightMostX, 1, i, color);
         }

         // Remove leftmost column
         for (int i = 0; i < 100; i++) {
            data.SetVoxel (leftMostX, 1, i, new QuantizedColor(0, 0, 0, 0));
         }


This works surprisingly well when testing-- I am seeing around 50fps when testing in 2880x1800. This is with disabling the Volume Collider, and by only enabling "Receive Shadows" on the Volume Renderer (cast shadows is disabled).

Still, are the any other optimizations I can make to bring this closer to 60fps?


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Optimizing scrolling engine
PostPosted: Wed Jul 19, 2017 6:20 am 

Joined: Mon May 15, 2017 7:35 am
Posts: 8
Reducing the frequency of rendering the new columns also helps. In other words, rendering 10 columns at once only when the player scrolls past 10 columns. But the cost is the same-- it causes the framerate to stutter and drop from 60 to around 50fps.

Is there any way I can optimize this further?


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Optimizing scrolling engine
PostPosted: Wed Jul 19, 2017 2:40 pm 
Developer
User avatar

Joined: Sun May 04, 2008 6:35 pm
Posts: 1827
Sounds very interesting, can you post a screenshot for context?

In general I would expect SetVoxel() to be relatively fast compared to the process of regenerating the meshes. Internally the volume is broken down into blocks/chunks (I think 16x16x16 but might be 32x32x32) for rendering purposes. If you just write one column at a time between frames then any given chunk will have it's mesh regenerated 16 times overall. You might be better off writing a greater width (you tried 10, but ideally 16) and maybe less height (could spread updating the columns over several frames if you have some overhang off the edge of the screen.

Gryz wrote:
- Generate a tile-based map in memory (e.g. 1000x1000 tiles)
- Use VolumeData.SetVoxel to render a subset of the map on screen (e.g. 100x100)


So what is your source data structure here? Are you copying from one Cubiquity Volume to another, or your precomputed source is just e.g. an array?

Gryz wrote:
Still, are the any other optimizations I can make to bring this closer to 60fps?


I see that you keep referring to tiles rather than voxels, and I assume this is intentional? A tile in your case is a number of voxels? How many? In keeping with my previous comment, you might do well to match this tile size to Cubiquity's chunk size and update one tile per frame rather than one column per frame.

I guess the ideal solution for you would be if Cubiquity just let you render part of a volume rather than the whole thing. This is how Cubiquity 2 will work, but I don't think Cubiquity 1 had that ability (but I'm quite hazy to be honest!). If you were brave you might be able to fake something by iterating over the Unity scene graph for the whole volume and setting the visibility of node to false if they are more than a certain distance away from a user-specified point. Not sure what gotchas there might be though!


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Optimizing scrolling engine
PostPosted: Fri Jul 21, 2017 6:01 am 

Joined: Mon May 15, 2017 7:35 am
Posts: 8
Thanks for the reply! Here is a screenshot: https://pasteboard.co/GBUYM9t.png

Yes, the map is generated and stored in an array. The array is used to build the cube volume.

I still need to implement the changes, but reducing the number of voxels rendered during scrolling from 10x100 to 10x16 has improved performance significantly.

Some other questions and points:
- Is there a performance advantage in rendering part of the volume compared to the whole in my case? For every column of voxels added to the grid, a column of voxels is removed using:

data.SetVoxel(x, 1, z, new QuantizedColor (0, 0, 0, 0));

Or are you suggesting I break up the volume further into sub-volumes?

- Right now the ground is rendered entirely in voxels, but it will never be destructible or altered. So no doubt I can improve performance by replacing those voxels with a plane.

- I would like to provide a map view in my game. It would work just like a map in an RTS game-- clicking on the map would take the main camera to that point in the world. Do you have suggestions on how to implement this so that there is no wait time for the new location to render?

- I added a Cubes Collider to my volume. The collision detection is pretty unreliable-- colliding into a wall straight on the x or z axis stops the player. But the player can slip through a wall by traveling on both axes, or occasionally by colliding straight on one axis. Is there a way to fix this?


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Optimizing scrolling engine
PostPosted: Fri Jul 21, 2017 11:00 pm 
Developer
User avatar

Joined: Sun May 04, 2008 6:35 pm
Posts: 1827
Gryz wrote:
Thanks for the reply! Here is a screenshot: https://pasteboard.co/GBUYM9t.png

Yes, the map is generated and stored in an array. The array is used to build the cube volume.

I still need to implement the changes, but reducing the number of voxels rendered during scrolling from 10x100 to 10x16 has improved performance significantly.

Some other questions and points:
- Is there a performance advantage in rendering part of the volume compared to the whole in my case? For every column of voxels added to the grid, a column of voxels is removed using:

data.SetVoxel(x, 1, z, new QuantizedColor (0, 0, 0, 0));


So your array is 1000x1000 (it's a 2D array, not 3D?) and your Cubiquity Volume is 100x100 voxels. As the player moves around you write new voxels into the volume and also delete some voxels which go out of view? Wouldn't this mean you also need to scroll the rest of the voxels by one position? Or is the Volume actually the same size as the array, and you just keep it only partially filled?

This sounds like it might be overly complex... have you tried getting rid of the array and simply having a volume which get completely filled on startup with all the data? Then there is no need to do anything as the camera move around. Internally Cubiquity splits up the volume into chunks so Unity will still only render the parts which are actually on screen.

For a 1000x1000x16 volume I think you can do this (at least it is worth testing), but if it needs to be any bigger then you might have a problem. One of the example volumes (a bridge or a city, I forget which) is 512x512x64 voxels (so the same number of voxels in total) so perhaps test this too?

Gryz wrote:
Or are you suggesting I break up the volume further into sub-volumes?


No, I would advise against this. Cubiquity already breaks the volume down internally.

Gryz wrote:
- I would like to provide a map view in my game. It would work just like a map in an RTS game-- clicking on the map would take the main camera to that point in the world. Do you have suggestions on how to implement this so that there is no wait time for the new location to render?


Again this would become easier (trivial?) if the whole volume was loaded all the time.

Gryz wrote:
- I added a Cubes Collider to my volume. The collision detection is pretty unreliable-- colliding into a wall straight on the x or z axis stops the player. But the player can slip through a wall by traveling on both axes, or occasionally by colliding straight on one axis. Is there a way to fix this?


A few options:

  • You could re-enable Cubiquity's built-in collision mesh generation. It significantly slows down mesh generation but if you are doing most of that on startup it might not matter too much... could still be slow though.
  • You could maintain a pool of cube colliders (maybe 100 or so?) which you move around to always occupy voxels near to the character.
  • You could impement collision detection manually, by calling getVoxel() on voxels which the player is about the enter.

Some experimentation might be needed here :-)


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Optimizing scrolling engine
PostPosted: Sat Jul 22, 2017 3:43 am 

Joined: Mon May 15, 2017 7:35 am
Posts: 8
Thanks. Yes, the volume is set to the same size of the array (1000x1000x2).

Yes, I have tried testing the rendering at 1000x1000x2 voxels. It takes awhile, but seems doable.

I wanted to create a world larger than that though. What would be the maximum number of voxels I could have in a volume then, 16 million? Double checking because my world won't be 16 voxels on the y axis. In fact, using a plane as I mentioned would reduce the height to one dimension.


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Optimizing scrolling engine
PostPosted: Sat Jul 22, 2017 8:17 pm 

Joined: Mon May 15, 2017 7:35 am
Posts: 8
I tried rendering a larger number of voxels (3000x3000x1), and performance suffers significantly after scrolling for awhile (drops to 30 fps).

Attaching and removing columns seems like it will fix the performance issues, if the voxels are modified over several frames.

Quote:
You could re-enable Cubiquity's built-in collision mesh generation. It significantly slows down mesh generation but if you are doing most of that on startup it might not matter too much... could still be slow though.


How can I do this?

Quote:
- I would like to provide a map view in my game. It would work just like a map in an RTS game-- clicking on the map would take the main camera to that point in the world. Do you have suggestions on how to implement this so that there is no wait time for the new location to render?


Again this would become easier (trivial?) if the whole volume was loaded all the time.


If the volume is not loaded, is there another way to accomplish this? Otherwise, I imagine rendering the voxels will be too slow if the user is clicking around the map.


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Optimizing scrolling engine
PostPosted: Sat Jul 22, 2017 11:02 pm 
Developer
User avatar

Joined: Sun May 04, 2008 6:35 pm
Posts: 1827
Gryz wrote:
Yes, I have tried testing the rendering at 1000x1000x2 voxels. It takes awhile, but seems doable.


I should have mentioned it before, but I now remember there is actually a setting (Volume.maxSyncOperationsInPlayMode) which limits how quickly the mesh can be synchronised with the voxel data. See:

https://bitbucket.org/volumesoffun/cubi ... ume.cs-136

This is useful at runtime because it limits the impact of a large number of voxel changes on framerate. But in your case you might load the whole volume at start up time and then never modify it later, so you might want to raise this value (try doubling or quadrupling it). You will need to edit the Cubiquity source code to make this change as the property is not exposed.

Gryz wrote:
What would be the maximum number of voxels I could have in a volume then, 16 million?


There is no hard limit, but the biggest I have used myself is 512x512x64. I would say that your current size of 1000x1000 is probably as far as you can go but it will depend on your hardware of course.

However, the more I find out about your project the more I think Cubiquity is not an optimal solution for it. Of course it is easy to get started, but it has a number of problems in your case:

  • Cubiquity is designed for systems where the volume changes at runtime and so internally it breaks the volume down into a large number of chunks which can be updated individually. This results in many meshes and drawcalls, but in your case this is a waste because you could just represent you whole world with a small number of static meshes computed from the array at startup.
  • Cubiquity spends quite some effort checking whether the volume has changed each frame and synchronising. Again this is wasted time in your project.
  • Internally chunks are 16x16x16 (I think) which means your 1000x1000x1 volume actually gets padded to 1000x1000x16. The system is designed for 3D maps whereas yours is really 2D, so again this is unnecessary overhead.

I expect you would get better performance by dropping Cubiquity and generating a few static meshes from your array on startup. The algorithm for building a mesh from a 2D array is not trivial but I expect it is easier than the effort you will spend trying to adapt Cubiquity to your needs.

Gryz wrote:
Quote:
You could re-enable Cubiquity's built-in collision mesh generation. It significantly slows down mesh generation but if you are doing most of that on startup it might not matter too much... could still be slow though.

How can I do this?


I just meant you could put back the VolumeCollider, as you mentioned previously that you had removed it. The VolumeCollider actually just generates a set of MeshColliders internally (one for each 16x16x16 chunk).

Gryz wrote:
If the volume is not loaded, is there another way to accomplish this? Otherwise, I imagine rendering the voxels will be too slow if the user is clicking around the map.


I'm afraid nothing springs to mind for this.


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Optimizing scrolling engine
PostPosted: Sun Jul 23, 2017 6:02 pm 

Joined: Mon May 15, 2017 7:35 am
Posts: 8
Thanks for the honest answer. I will rethink my choice here.

But what I did not mention is that I plan to make the walls in the game destructible, so Cubiquity would help with that. Think a mining game with generated caves.

Quote:
I just meant you could put back the VolumeCollider, as you mentioned previously that you had removed it. The VolumeCollider actually just generates a set of MeshColliders internally (one for each 16x16x16 chunk).


I see. Yes, this is what I tried, but was seeing spotty collision detection with my cube, especially when moving on two axes at once.


Top
Offline Profile  
Reply with quote  
 Post subject: Re: Optimizing scrolling engine
PostPosted: Tue Jul 25, 2017 11:14 pm 
Developer
User avatar

Joined: Sun May 04, 2008 6:35 pm
Posts: 1827
Gryz wrote:
I will rethink my choice here.


One approach you might consider would be to simply render your map as instanced cubes. You could draw the ground plane separately and then keep a set of a few hundred cubes which you update the position of as the player moves around the map. Updating the position of these cubes should be fast (and may be similar to the per-column updates you experimented with in Cubiquity).

The algorithm seems straightforward and the whole thing should render in two draw calls, one for the plane and one for all the cubes (provided they are instanced and not completely separate objects). I'm not sure how instancing works in Unity but you could check it out.

Not sure if it helps with your mini-map issue though...


Top
Offline Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 10 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Theme created StylerBB.net