first pass at reviewing reduz' new docs

This commit is contained in:
clayjohn
2019-04-22 15:15:30 -07:00
parent f070f6cea1
commit 2bd33f5a46
6 changed files with 170 additions and 111 deletions

View File

@@ -1,40 +1,60 @@
.. _doc_using_multimesh:
Optimization Using Multimeshes
============
Optimization using MultiMeshes
==============================
For large amount of instances (in the thousands), that need to be constantly processed (and certain amount of control needs to be retained), :ref:`using servers directly<doc_using_servers>` is the recommended optimization.
For large amount of instances (in the thousands), that need to be constantly processed
(and certain amount of control needs to be retained),
:ref:`using servers directly <doc_using_servers>` is the recommended optimization.
When the amount of objects reach the dozens of thousands, hundreds of thousands or even millions, none of these approaches are efficient anymore. Still, depending on the requirements, there is one more optimization possible.
When the amount of objects reach the hundreds of thousands or millions,
none of these approaches are efficient anymore. Still, depending on the requirements, there
is one more optimization possible.
Multimeshes
MultiMeshes
-----------
A :ref:`MultiMesh<class_MultiMesh>` is a single draw primitive that can draw up to millions of objects in one go. It's extremely efficient because it uses the GPU hardware to do this (In OpenGL ES 2.0, it's less efficient because there is no hardware support for it, though).
A :ref:`MultiMesh<class_MultiMesh>` is a single draw primitive that can draw up to millions
of objects in one go. It's extremely efficient because it uses the GPU hardware to do this
(In OpenGL ES 2.0, it's less efficient because there is no hardware support for it, though).
The only drawback is that there is no *screen* or *frustum* culling possible for individual instances. This means, that millions of objects will be *always* or *never* drawn, depending on the visibility of the whole MultiMesh. It is possible to provide a custom visibility rect for them, but it will always be *all-or-none* visibility.
The only drawback is that there is no *screen* or *frustum* culling possible for individual instances.
This means, that millions of objects will be *always* or *never* drawn, depending on the visibility
of the whole MultiMesh. It is possible to provide a custom visibility rect for them, but it will always
be *all-or-none* visibility.
If the objects are simple enough (just a couple of vertices), this is generally not much of a problem as most modern GPUs are optimized for this use case. A workaround is to create several multimeshes for different areas of the world.
If the objects are simple enough (just a couple of vertices), this is generally not much of a problem
as most modern GPUs are optimized for this use case. A workaround is to create several MultiMeshes
for different areas of the world.
If logic is too simple, it is also possible to execute it inside the vertex shader (using the INSTANCE_ID integer built-in constant). Information to it can be provided via textures (there are floating point :ref:`Image<class_Image>` formats which are ideal for this).
It is also possible to execute some logic inside the vertex shader (using the INSTANCE_ID or
INSTANCE_CUSTOM built-in constants). For an example of animating thousands of objects in a MultiMesh,
see the :ref:`Animating thousands of fish <doc_animating_thousands_of_fish>` tutorial. Information
to the shader can be provided via textures (there are floating point :ref:`Image<class_Image>` formats
which are ideal for this).
Another alternative is to use GDNative and C++, which should be extremely efficient (it's possible to set the entire state for all objects using linear memory via the :ref:`VisualServer.multimesh_set_as_bulk_array()<class_VisualServer_method_multimesh_set_as_bulk_array>` function. This way, the array can be created with multiple threads, then set in one call, providing high cache efficiency.
Another alternative is to use GDNative and C++, which should be extremely efficient (it's possible
to set the entire state for all objects using linear memory via the
:ref:`VisualServer.multimesh_set_as_bulk_array() <class_VisualServer_method_multimesh_set_as_bulk_array>`
function. This way, the array can be created with multiple threads, then set in one call, providing
high cache efficiency.
Finally, it's not required to have all multimesh instances visible. The amount of visible ones can be controlled with the *MultiMesh.visible_instance_count* property. The typical workflow is to allocate the maximum amount of instances that will be used,
Finally, it's not required to have all MultiMesh instances visible. The amount of visible ones can be
controlled with the *MultiMesh.visible_instance_count* property. The typical workflow is to allocate
the maximum amount of instances that will be used,
then change the amount visible depending on how many are currently needed.
Multimesh Example
-----------
-----------------
Here is a simple example of using a multimesh from code (using GDScript). Other languages may be more efficient for millions of objects, but for a few thousands, GDScript should be fine.
Here is an example of using a MultiMesh from code (using GDScript). Other languages may be more
efficient for millions of objects, but for a few thousands, GDScript should be fine.
.. tabs::
.. code-tab:: gdscript GDScript
extends MultiMeshInstance
func _ready():
# Create multimesh
@@ -50,5 +70,3 @@ Here is a simple example of using a multimesh from code (using GDScript). Other
# Set instance transforms
for i in range(multimesh.visible_instance_count):
multimesh.set_instance_transform(i,Transform( Basis(), Vector3(i*20,0,0) ) )

View File

@@ -1,63 +1,86 @@
.. _doc_using_servers:
Optimization Using Servers
============
==========================
Engines like Godot provide increased ease of use thanks to it's high level constructs and features. Most of them are accessed and used via the :ref:`Scene System<doc_scene_tree>`. Using nodes and resources simplifies project organization and asset management
in complex games.
Engines like Godot provide increased ease of use thanks to it's high level constructs and features.
Most of them are accessed and used via the :ref:`Scene System<doc_scene_tree>`. Using nodes and
resources simplifies project organization and asset management in complex games.
There are, of course always drawbacks
There are, of course, always drawbacks:
* There is an extra layer of complexity
* Performance is lower than using simple APIs directly
* It is not possible to use multiple threads to control them
* More memory is needed.
In far most cases, this is not really a problem (Godot is very optimized, and most operations are handled with signals, so no polling is required). Still, sometimes it may be. As an example, dealing with dozens of thousands of instances for something that needs to be processed every frame can pose a bottleneck.
In many cases, this is not really a problem (Godot is very optimized, and most operations are handled
with signals, so no polling is required). Still, sometimes it can be. For example, dealing with
tens of thousands of instances for something that needs to be processed every frame can be a bottleneck.
This type of situation makes programmers regret they are using a game engine and wish they could go back to a more handcrafted, low level implementation of game code.
This type of situation makes programmers regret they are using a game engine and wish they could go
back to a more handcrafted, low level implementation of game code.
Still, Godot is designed to work around this problem.
Servers
-------
One of the most interesting design decisions for Godot, is the fact that the whole scene system is *optional*. While it's not currently possible to compile it out, it can be completely bypassed.
One of the most interesting design decisions for Godot, is the fact that the whole scene system is
*optional*. While it is not currently possible to compile it out, it can be completely bypassed.
At the core, Godot uses the concept of Servers. They are very low level APIs to control rendering, physics, sound, etc. The scene system is built on top of them and uses them directly. The most common servers are:
At the core, Godot uses the concept of Servers. They are very low level APIs to control
rendering, physics, sound, etc. The scene system is built on top of them and uses them directly. The most common servers are:
* :ref:`Visual Server<class_VisualServer>` handles everything related to graphics.
* :ref:`PhysicsServer<class_PhysicsServer>` handles everything related to 3D physics.
* :ref:`Physics2DServer<class_Physics2DServer>` handles everything related to 2D physics.
* :ref:`AudioServer<class_AudioServer>` handles everything related to audio.
* :ref:`Visual Server<class_VisualServer>`: handles everything related to graphics.
* :ref:`PhysicsServer<class_PhysicsServer>`: handles everything related to 3D physics.
* :ref:`Physics2DServer<class_Physics2DServer>`: handles everything related to 2D physics.
* :ref:`AudioServer<class_AudioServer>`: handles everything related to audio.
Just explore their APIs and you will realize that the all functions provided are low level implementations of everything Godot allows to do.
Just explore their APIs and you will realize that the all functions provided are low-level
implementations of everything Godot allows you to do.
RIDs
----
The key to using servers is understanding RID objects (RID means Resource ID). These are opaque handles to the sever implementation. They are allocated and freed manually. Almost every function in the servers requires RIDs to access the actual resource.
The key to using servers is understanding Resource ID (RID) objects. These are opaque
handles to the sever implementation. They are allocated and freed manually. Almost every
function in the servers requires RIDs to access the actual resource.
Most Godot nodes and resources contain internally these RIDs from the servers, and they can be obtained with different functions. In fact, anything that inherits :ref:`Resource<class_Resource>` can be directly casted to an RID (not all resources contain an RID, though, in such cases the RID will be empty). In fact, resources can be passed to server APIs as RIDs. Just make sure to keep references to the resources ouside the server, because if the resource is erased, the internal RID is erased too.
Most Godot nodes and resources contain these RIDs from the servers internally, and they can
be obtained with different functions. In fact, anything that inherits :ref:`Resource <class_Resource>`
can be directly casted to an RID (not all resources contain an RID, though, in such cases
the RID will be empty). In fact, resources can be passed to server APIs as RIDs. Just make
sure to keep references to the resources ouside the server, because if the resource is erased,
the internal RID is erased too.
For nodes, there are many functions available:
* For CanvasItem, the :ref:`CanvasItem.get_canvas_item()<class_CanvasItem_method_get_canvas_item>` method will return the canvas item RID in the server.
* For CanvasLayer, the :ref:`CanvasLayer.get_canvas()<class_CanvasLayer_method_get_canvas>` method will return the canvas RID in the server.
* For Viewport, the :ref:`Viewport.get_viewport_rid()<class_Viewport_method_get_viewport_rid>` method will return the viewport RID in the server.
* For 3D, the :ref:`World<class_World>` resource (obtainable in the *Viewport* and *Spatial* nodes, contains function to get the *VisualServer Scenario*, and the *PhysicsServer Space*. This allows creating 3D objects directly with the server API and using them.
* For 2D, the :ref:`World2D<class_World2D>` resource (obtainable in the *Viewport* and *CanvasItem* nodes, contains function to get the *VisualServer Canvas*, and the *Physics2DServer Space*. This allows creating 2D objects directly with the server API and using them.
* The :ref:`VisualInstance<class_VisualInstance` class, allows getting the scenario *instance* and *instance base* via the :ref:`VisualInstance.get_instance()<class_VisualInstance_method_get_instance>` and :ref:`VisualInstance.get_base()<class_VisualInstance_method_get_base>` respectively.
* For CanvasItem, the :ref:`CanvasItem.get_canvas_item()<class_CanvasItem_method_get_canvas_item>`
method will return the canvas item RID in the server.
* For CanvasLayer, the :ref:`CanvasLayer.get_canvas()<class_CanvasLayer_method_get_canvas>`
method will return the canvas RID in the server.
* For Viewport, the :ref:`Viewport.get_viewport_rid()<class_Viewport_method_get_viewport_rid>`
method will return the viewport RID in the server.
* For 3D, the :ref:`World<class_World>` resource (obtainable in the *Viewport* and *Spatial* nodes,
contains function to get the *VisualServer Scenario*, and the *PhysicsServer Space*. This
allows creating 3D objects directly with the server API and using them.
* For 2D, the :ref:`World2D<class_World2D>` resource (obtainable in the *Viewport* and *CanvasItem*
nodes, contains function to get the *VisualServer Canvas*, and the *Physics2DServer Space*. This
allows creating 2D objects directly with the server API and using them.
* The :ref:`VisualInstance<class_VisualInstance>` class, allows getting the scenario *instance* and
*instance base* via the :ref:`VisualInstance.get_instance()<class_VisualInstance_method_get_instance>`
and :ref:`VisualInstance.get_base()<class_VisualInstance_method_get_base>` respectively.
Just explore the nodes and resources you are familiar with and find the functions to obtain the server *RIDs*.
It is not advised to control RIDs from objects that already have a node associated. Instead, server functions should always be used for creating and controlling new ones and interacting with the existing ones.
It is not advised to control RIDs from objects that already have a node associated. Instead, server
functions should always be used for creating and controlling new ones and interacting with the existing ones.
Creating a sprite
Creating a Sprite
-----------------
This is a simple example of how to create a sprite from code and move it using the low level Canvas Item API.
This is a simple example of how to create a sprite from code and move it using the low-level Canvas Item API.
.. tabs::
.. code-tab:: gdscript GDScript
@@ -79,7 +102,9 @@ This is a simple example of how to create a sprite from code and move it using t
var xform = Transform2D().rotated( deg2rad(45) ).translated( Vector2( 20, 30 ) )
VisualServer.canvas_item_set_transform( ci_rid, xform )
The Canvas Item API in the server allows you to add draw primitives to it. Once added, they can't be modified. The Item needs to be cleared and the primitives re-added (this is not the case for setting the transform, which can be done as many times as desired).
The Canvas Item API in the server allows you to add draw primitives to it. Once added, they can't be modified.
The Item needs to be cleared and the primitives re-added (this is not the case for setting the transform,
which can be done as many times as desired).
Primitives are cleared this way:
@@ -89,10 +114,10 @@ Primitives are cleared this way:
VisualServer.canvas_item_clear( ci_rid )
Instantiating a Mesh into 3D Space
Instantiating a Mesh into 3D space
----------------------------------
The 3D APIs are considerably different than the 2D ones, so the instantiation API must be used.
The 3D APIs are different than the 2D ones, so the instantiation API must be used.
.. tabs::
.. code-tab:: gdscript GDScript
@@ -115,8 +140,8 @@ The 3D APIs are considerably different than the 2D ones, so the instantiation AP
var xform = Transform( Basis(), Vector3(20,100,0) )
VisualServer.instance_set_transform(instance,xform)
Creating a 2D Rigid Body and moving a sprite with it
-----------------------------------------------------
Creating a 2D RigidBody and moving a Sprite with it
----------------------------------------------------
This creates a *RigidBody* using the *Physics2DServer* API, and moves a *Canvas Item* when the body moves.
@@ -151,10 +176,11 @@ The 3D version should be very similar, as 2D and 3D physics servers are identica
Getting data from the servers
-----------------------------
Try to **never** request any information from *VisualServer*, *PhysicsServer* or *Physics2DServer* by calling functions unless you know what you are doing. These servers will often run asynchronously for performance and calling any function that returns a value will stall them and force them to process anything pending until the function is actually called. This will severely decrease performance if you call them every frame (and it won't be obvious why).
Because of this, most APIs in such servers are designed so it's not even possible to request information back, until it's actual data that can be saved.
Try to **never** request any information from *VisualServer*, *PhysicsServer* or *Physics2DServer*
by calling functions unless you know what you are doing. These servers will often run asynchronously
for performance and calling any function that returns a value will stall them and force them to process
anything pending until the function is actually called. This will severely decrease performance if you
call them every frame (and it won't be obvious why).
Because of this, most APIs in such servers are designed so it's not even possible to request information
back, until it's actual data that can be saved.