This page has been translated automatically.
Video Tutorials
Interface
Essentials
Advanced
How To
UnigineEditor
Interface Overview
Assets Workflow
Settings and Preferences
Working With Projects
Adjusting Node Parameters
Setting Up Materials
Setting Up Properties
Lighting
Landscape Tool
Sandworm
Using Editor Tools for Specific Tasks
Extending Editor Functionality
Built-in Node Types
Nodes
Objects
Effects
Decals
Light Sources
Geodetics
World Nodes
Sound Objects
Pathfinding Objects
Players
Programming
Setting Up Development Environment
Usage Examples
C++
C#
UnigineScript
UUSL (Unified UNIGINE Shader Language)
Plugins
File Formats
Rebuilding the Engine Tools
GUI
Double Precision Coordinates
API
Containers
Common Functionality
Controls-Related Classes
Engine-Related Classes
Filesystem Functionality
GUI-Related Classes
Math Functionality
Node-Related Classes
Objects-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
IG Plugin
CIGIConnector Plugin
Rendering-Related Classes
Content Creation
Content Optimization
Materials
Material Nodes Library
Miscellaneous
Input
Math
Matrix
Textures
Art Samples
Tutorials
Warning! This version of documentation is OUTDATED, as it describes an older SDK version! Please switch to the documentation for the latest SDK version.
Warning! This version of documentation describes an old SDK version which is no longer supported! Please upgrade to the latest SDK version.

Engine Main Loop

This article describes in detail the steps taken by the UNIGINE engine when the Engine::update(), Engine::render(), and Engine::swap() functions are called. For other steps and general information about execution sequence, see the Execution Sequence article.

Notice
Each stage of the main loop is initiated by the application window created during the Initialization stage.

The total time the main loop has taken is displayed by the Total counter in the Performance Profiler.

Update#

The update part of the execution sequence includes the following:

  1. The FPS value of the application starts to be calculated.

    Notice
    Calculation of FPS starts only with the second rendered frame, while the very first one is skipped.
  2. All pending console commands that were called during the previous frame are executed. The commands are executed in the beginning of the update() cycle, but before the scripts are updated, because otherwise they may violate the current process of rendering or physical calculations.
  3. If a world has been unloaded during the previous frame, another world is loaded: WorldLogic::init().
  4. If the video_restart console command was executed previously (not in the current update stage), world shaders are created.

    Notice
    When video is restarted (for example, when the video mode is changed), the application window calls the destroyRenderResources() methods of the system and editor logic and plugins. However, it is performed not in the current update stage, but earlier.
  5. Properties, input, and controls are updated.
  6. Update of streamed textures, sound, and landscape.
  7. Game FPS and materials are updated.
  8. Ambient sound sources timers are updated.
  9. The World Logic init() function for the newly loaded world is called.
  10. The update() function of all plugins is called. What happens during it, solely depends on the content of this custom function.
  11. The Editor-related Logic is updated by calling the Editor update() function.
  12. The System Logic is updated. The default system script update() function performs the following:
    • The system script handles the mouse. It controls whether the mouse is grabbed when clicked (by default), the mouse cursor disappears when not moved for some time (set by the MOUSE_SOFT definition), or not handled by the system (set by the MOUSE_USER definition, which allows input handling by some custom module).
    • The main menu logic is updated (if the MENU_USER definition is not set).
    • Other system-related user input is handled. For example, the world state is saved or restored, or a screenshot of the current UNIGINE window contents is made, if required.
    • If the GPU Monitor plugin is initialized (the HAS_GPU_MONITOR definition is set), the plugin output is displayed in the application window and additional console commands become available.

      Warning
      The functionality described in this paragraph is not available in the Community SDK edition.
      You should upgrade to Sim SDK edition to use it.
  13. If the world is loaded (it can be done from the console or via the System Logic), the World Logic gets updated. In the World Logic update() function, you can code frame-by-frame behavior of your application (see details here).

    Notice
    Physics, if any, and continuous operations (pushing a car forward depending on current motor's RPM, simulating wind blowing constantly, performing immediate collision response, etc.), can be implemented separately in the updatePhysics() function. This function is called with a fixed frame rate (while the update() function is called each frame).

    The world and its world logic are updated in the following order:

    1. The World Logic updateAsyncThread() function is executed. It is designed to perform logic functions that should be called every frame independently of the rendering thread. This function doesn't block the Main Thread.
    2. The World Logic updateSyncThread() function is executed: all parallel logic functions that should be executed before update(). This function blocks the Main Thread until all calls are completed.
    3. The World Logic update() function is executed: node parameters are updated, transformations for non-physical nodes are set and so on.
    4. The state of nodes existing in the world is updated (mostly for visible nodes): skinned animation is played, particle systems spawn new particles, players are repositioned, and so on. Triggered world callbacks are added to a stack (they will be executed later).
    5. World Expressions are updated. Code in World Expressions can be written via UnigineEditor.
    6. GUI is updated.
    7. The World Logic postUpdate() function is called.
  14. The System Logic postUpdate() function is executed, if necessary (see details here). It can access the updated data on node states and correct the behavior accordingly in the same frame.
  15. The Editor Logic postUpdate() function is executed.
  16. The postUpdate() function of all plugins is called.
  17. The world spatial tree is updated.
  18. The file system is updated.

At the end of the update stage, physics and pathfinding start to be updated in their separate threads. Then they perform their tasks on all available threads in parallel to rendering.

Notice
Nodes with computationally heavy bodies (like clothes and ropes) should not have one parent; otherwise, they will be updated in one thread.

The total time of update stage is displayed by the Update counter in the Performance Profiler.

Rendering#

As soon as the update stage is completed, UNIGINE can start rendering the world. In parallel, the pathfinding is performed in a separate thread. Depending on the physics update mode, parts of the physics calculations are performed in the main thread or in parallel to it. This approach enables to effectively balance the load between CPU and GPU, and thus allows for higher framerate in the UNIGINE-based application.

Here is what happens during the render stage:

Render Physics Pathfinding
  1. The Editor Logic render() function is called.
  2. The render() function of plugins is called, if they exist.
  3. UNIGINE renders the graphics scene (the world) and the sound scene, as they should be in the current frame. The graphics scene is sent to GPU, while the sound scene is sent to the sound card. As soon as the CPU finishes preparation of data and feeds rendering commands to the GPU, the GPU becomes busy with rendering the frame.

    The total time of rendering stage is displayed by the Render counter in the Performance Profiler. After that, the CPU is free, so we can load it with calculations we need.

  4. The gui() function of plugins (if any) is called.
  5. At last and atop of all, GUI is rendered, if required. And the total time of interface rendering is displayed by the Interface counter in the Performance Profiler.
  1. The physics module calls the plugin updatePhysics() function, if it exists.
  2. The physics module calls the world logic updatePhysics() function. In the updatePhysics() function, you can modify physics.

    The updatePhysics() function is not called each frame. The physics module has its own fixed framerate, which does not depend on the rendering framerate. During each of such physics frames (or ticks), a number of calculation iterations are performed (updatePhysics() is called before each iteration).

  3. The physics module is updated: internal physics simulation starts. During this step, UNIGINE performs collision detection for all objects that have physical bodies and collision shapes.
    In the Performance Profiler, the total time of updatePhysics() together with physics simulation is displayed by the Physics counter.
  • The pathfinding module is updated. In the thread performance profiler, the total time of pathfinding is displayed by the PathFind counter.

Swap#

The swap stage is the last one in the main loop. It includes the following:

  1. If the video_grab console command is executed previously, on this stage, the taken screenshot is saved in folder where all application data is stored.
  2. Synchronization of physics and pathfinding with the rendered world, i.e. waiting for all additional threads to finish their tasks. Results of the physical calculations are applied to the world. That is, on the previous step we have calculated how physical bodies with collision shapes have changed their position and orientation (due to our update-based logic or interaction). Now these transformations can be finally applied to nodes, i.e. rendered meshes.

    Notice
    As the synchronization of physics follows the rendering stage (in default Async Rendering physics mode), applied physical transformations will be visible on the screen only in the next frame. To calculate physics before the rendering enable the Before Rendering physics mode.
  3. The World Logic swap() function is executed. It operates with the results of the updateAsyncThread() function.
  4. The plugin swap() function is called, if it exists.
  5. Unloading of the world, if another world is going to be loaded in the next frame.
  6. Deletion of all objects that are planned to be deleted.
  7. Values shown in the performance profiler are updated.

After the swap stage is completed, the application window initiates GPU buffers swapping as described here.

Last update: 2021-12-13
Build: ()