Since the EON software is used to generate real-time interactive 3D graphics, to be displayed on quite immersive environment usually, it’s utterly important as an EON application developer to understand how to optimize the content so it can be rendered as fast as possible, to obtain a solid and high frame rate. Failure to do this will result in a dissatisfied user experience, and with immersive displays such HMD (H
isplays) and Icube
, even discomfort, nausea and related symptoms.
Therefore, we have created this guide to give you a better understanding of how the EON engine works internally so you can author EON content that can be rendered faster, while also giving you general best practices that are applicable to all 3D real-time interactive graphics applications.
How the EON renderer works
After all initialization is done, the EON engine will enter a process, which on a high level will look similar to this:
On the left side we have the simulation loop, which will be repeatedly reading sensor/tracking data and feeding this into the simulation engine. Based on these readings, the simulation logics will perform various activities that will result in new states being set on the application, such as a new color on a box because the user has clicked on it. However, before this new color can be displayed on the display, this new state must be synchronized over to the rendering loop, which is run in parallel to the simulation loop (separate thread) and will take care of the rendering.
The reason we have two parallel loops (threads) going on is to take advantage of the multi-CPU cores almost all devices have nowadays, even on mobile devices. Another reason is that usually the rendering part is quite heavy, so instead of waiting for it to complete, we can start to prepare the application states for the next frame.
Notice that the simulation loop cannot runaway as fast as possible, because doing so will increase the latency between the input (the user interaction) and the reaction (see the end result of the interaction). Instead, when the simulation loop is done preparing the next frame, it will sit and wait until the previous rendering frame has been completed, and then synchronize over the new states.
On the rendering side, the major activities are to receive the new states and then use them to prepare a new visual scene graph and then renders out this. However, before the new image can be displayed, it has to wait for the vertical blanking interval to occur, so that it does not swap the image buffer while the image is being transmitted halfway through.
In order to produce as many frames as possible per second, each activity in the diagram has to be executed as fast as possible and there are certain ones (in red outline) that are usually more heavier than the others.
Updating the application states
In this activity, it includes delivering and handling of route events, and all script processing. So it’s ultimately important to write efficient scripts. This means things that can be pre-processed should be done so, and use of acceleration structures might be needed to avoid unnecessary delays.
Render the new scene
In almost all EON content, this is usually the most time-consuming activity. So it’s instrumental to optimize this part as much as possible. In the sections below, you can read about some best practices how to do this. Notice also that in this activity, the work is mostly done on the GPU, and since we have two parallel tracks, it’s actually possible to squeeze in more logics and processing such as AI, without increasing the final throughput of rendering frames, since these will be handled by the simulation thread and it would be sitting idle otherwise anyway.
How to spot bottlenecks in the rendering pipeline
In order to see how much time is spent on each block in the pipeline, you can use the Simulation Statistics which is displayed on the simulation window title bar (you turn it on with the menu command Simulation->Show Simulation Statistics).
Here is a breakdown of the statistics data:
||Average framerate (frames per second)
||Total time required to render (the previous) frame (ms)
||Total time required to render one frame, exclusive system processing duties (ms)
||Time spent on traversing the simulation tree and updating the application states (ms)
||The figure inside the parenthesis shows number of nodes visited during this frame
||Time spent on delivering and handling route events, including scripts execution (ms)
||Time spent on synchronizing the visual states from application to render engine (ms)
||Time spent on rendering the frame, inclusive waiting for vertical blanking (ms)
||Number of triangles being drawn in this frame (count)
||Number of batches in this frame (count)
||Total amount of texture memory used currently (kB, kilobytes)
||Total amount of Vertex memory used currently (kB, kilobytes)
Here is a graph showing how these timings are related to each other:
Note: it’s important to disable vertical sync when using the simulation statistics data. Otherwise, the framerate will be clamped to a multiplier of the vertical refresh rate. This can be done on the graphics driver control panel. You know the simulation is no longer locked to the vertical refresh rate when the HZ is higher than the refresh rate (or a multiplier of it), when running an empty simulation.
How to interpret the simulation statistics
- If you have a high app time, it means your content is limited by the processing of the nodes. It could be overly complex design, nodes that are taking unusually long time to update or you simply have too many nodes. You could try to lower this figure by hiding/disabling nodes that are not needed currently, by partitioning the scene into areas for instance. It’s also better to use Group node for organizing the scene, instead of Frame/Transform node, as the latter is more complex.
- If you have a high eve time, it means your scripts are doing lots of work, and/or there are many routes to process. Try to optimize the scripts by following the guidelines given in this article. Try to see if you can redesign/simply the routings. Note that you can connect and disconnect routes from the scripts, while running the simulation. So you can connect routes only when needed and then disconnect them again after use.
- If you have a high rs time, it means your content has many visual things that are changed frequently. The engine always try to only synchronize visual states that are changed, but sometimes a change on a node field might inadvertently trigger many other states to change as well. Also, setting some fields (e.g.: X, Y, Width, Height on the Viewport node) will even trigger a re-initialization of the viewport graphics object in the rendering engine. Therefore, refrain from changing the node field unless there is really a change. Most nodes will trigger a synchronization even if the same value is being set, so make sure to avoid setting the value if it’s already the same value, to avoid unnecessary state synchronization.
- If you have a high drw time, it means the framerate is limited by the rendering and graphics hardware, which is the most common scenario. Getting a faster graphics card will improve this timing (while all the aforementioned ones are CPU bound), but you should also consult the section below on how to make content render faster.
- If you have a high batch count, it means you have lots of objects. It’s faster to render few big objects than many small ones even though the triangle count is the same in both cases, as there is a certain fix overhead to setup each object. You should try to lower the batch account as much as possible. Optimally, you should only have as many batches as you have unique materials in the scene, but usually it’s not possible due to the need to group and partition the scene into sub objects. Try out the Merge shapes tool (Tools->Merge Shapes) to collapse objects with the same material into one object quickly inside EON Professional, or do this in the modelling tool.
- High amount of triangle (#tri) will affect the rendering speed, as there will be more triangles to process, but nowadays the GPU will really good at crunching triangles, so this metric is not as critical as it used to be. Try the techniques outlined below to reduce the geometry complexity if this figure is too high.
- If the amount of texture and vertex memory used (tm, vm) combined exceed the amount of memory your graphics card is equipped with, the rendering will slow down abruptly (and in some cases rendering artifacts with black textures will show up). You have to make sure these figures stay a bit from the available memory you have, because the framebuffer and desktop display will also require some memory on the GPU. This amount depends on your current resolution. If you remove all textures from the scene and the drw time drops down dramatically, you know you have hit the memory limit.
How to make content renders faster in EON
- Reduce overdraw - if something is hidden behind something else, it will still be drawn, so it’s wasted effort unless the object in front is semi-transparent. You could hide/show various parts to reduce overdraw for instance.
- Avoid the use of transparency - it’s expensive to draw semi-transparent objects, as the graphics pipeline is not optimized for that. If you need to have irregular shapes on some textures, try to use binary alpha (set ForceSceenDoorTransparency on the Texture node), which is much less expensive performance wise.
- Reduce geometry complexity - if you are bringing in CAD data, try to optimize it first by using a polygon reducer available in many modelling tools. If you are modeling from scratch, consider using normal mapping technique to project geometry details into normal maps. Also, far away objects can be replaced with billboard or similar.
- Turn on mip mapping for textures - this will improve not only the visual appearance (no more shimmering effects on the texture when viewing from a sharp angle), but also rendering speed as the GPU can fetch the texture data faster due to the smaller mipmap levels being able to fit into texture cache more easily.
- Do manipulation with shader if possible - since shaders are executed on the GPU hardware, it will be magnitude faster than doing the same manipulation on the CPU and then transfer the change over to the GPU. For instance, if you need to modulate the vertices of a geometry, consider using a texture to specify the modulation and move the vertices in a vertex shader.
- Use texture to add details - it’s much cheaper to add details via textures, than modelling them as triangles. Using the normal mapping technique, you can further enhance the appearance.
- Use as few materials as possible - try to reuse material as much as possible. It will simply be faster to get the objects through the graphics pipeline, since each material change will cause a slow down in the rendering process.
- Use as few objects as possible - this is related to the previous one, but it has more profound impact as it’s even more expensive to setup a new object to render than changing materials. Merge objects if they have the same material, unless you need to animate them individually.
How to make scripts run faster in EON
In more complex EON applications, the scripts will make up a major part of the execution time. However, since it’s running on the CPU, it usually does not matter unless they take longer to process than the rendering (drw time). If you need to optimize the scripting, consider the following:
- If you have many in-fields and you will use them to derive a result, it’s better to define an eventsProcessed() function and do the calculation there, instead of doing this in every handler of the in-fields, since the field handlers will be invoked as soon as the field is changed, which could happen many times during one frame, while the eventsProcessed() is guaranteed to be called only once per frame, and only when at least one field of the script has been changed.
- If an object is no longer needed, consider setting the reference to it to null. This will make the object subject for garbage collection and will decrease the memory pressure.
- Create function of commonly used code block, instead of copy-paste it everywhere. Not only is this a good software engineering practice, it will also make it easier for the script engine to spot which parts of your script code are executed more often and it will apply optimization technique on those parts to make them run faster.