Skip to content

Commit d7b2c44

Browse files
committed
Doc update: effect pacakges and performance
1 parent a794a0f commit d7b2c44

File tree

2 files changed

+64
-135
lines changed

2 files changed

+64
-135
lines changed

docs/user_guide/effects.rst

Lines changed: 53 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22
Effects
33
=======
44

5-
In order to actually draw something to the screen you need to make one or
5+
In order to actually render something to the screen you need to make one or
66
multiple effects. What these effects are doing is entirely up to you.
7-
Some like to put everything into one effect and switch what they draw by
8-
flipping some internal states, but this is probably not practical for more
9-
complex things.
7+
Effects have methods for fetching loaded resources and existing effect instances.
8+
Effects can also create new instances of effects if needed. This would
9+
happend during initialization.
1010

11-
An effect is a class with references to resources such as shaders, geometry,
12-
fbos and textures and a method for drawing. An effect is an independent python
13-
package of specific format.
11+
Effect examples can be found in the ``examples`` directory in the root of the repository.
1412

1513
The Effect Package
1614
------------------
@@ -21,133 +19,67 @@ named "cube").
2119
.. code-block:: bash
2220
2321
cube
24-
├── effect.py
25-
├── shaders
26-
│   └── cube
27-
│   └── ...
28-
└── textures
29-
   └── cube
30-
└── ...
22+
├── effects.py
23+
├── dependencies.py
24+
└── resources
25+
└── programs
26+
└── cube
27+
└── cube.glsl
28+
└── textures
29+
└── scenes
30+
└── data
3131
32-
The ``effect.py`` module is the actual code for the effect. Directories at the
33-
same level are for local resources for the effect.
32+
The ``effects.py`` module can contain one or multiple effects.
33+
The effect package can also have no effects and all and only
34+
provide resources for other effects to use. The ``effects.py``
35+
module is still required to be present.
3436

35-
.. Note:: Notice that the resource directories contains another sub-directory
36-
with the same name as the effect directory/package. This is because these
37-
folders are by default added to a project wide search path
38-
(for each resource type),
39-
so we should place it in a directory to reduce the chance of a name collisions.
37+
Dependencies
38+
------------
4039

41-
We can also decide not to have any effect-local resources and configure
42-
a project-global resource directory. More about this `settings`.
40+
The ``dependencies.py`` module is required to be present. It describes
41+
its own resources and what effect packages it may depend on.
4342

44-
Registry
45-
--------
43+
Example::
4644

47-
For an effect to be recognised by the system, it has to be registered
48-
in the ``EFFECTS`` tuple/list in your settings module.
49-
Simply add the full python path to the package. If our cube example
50-
above is located inside a ``myproject`` project package we need to add
51-
the string ``myproject.cube``. See `settings`.
45+
from demosys.resources.meta import ProgramDescription
5246

53-
You can always run a single effect by using the ``runeffect`` command.
47+
effect_packages =
48+
'full.python.path.to.another.package',
49+
]
5450

55-
.. code-block:: bash
56-
57-
./manage.py runeffect myproject.cube
51+
resources = [
52+
ProgramDescription(label='cube_plain', path='cube_plain.glsl'),
53+
]
5854

59-
If you have multiple effects, you need to crate or use an existing
60-
:doc:`effectmanagers` that will decide what effect would be active at
61-
what time or state.
55+
Resources are given labels and effects can fetch them by this label.
56+
When adding effect package dependencies we make the system aware
57+
of this package so their resources are also loaded. The effects
58+
in the depending package will also be registered in the system
59+
and can be instantiated.
6260

6361
Resources
6462
---------
6563

66-
Resource loading is baked into the ``Effect`` base class. Methods are inherited
67-
from the base ``Effect`` class such as ``get_shader`` and ``get_texture``.
68-
69-
Methods fetching resources can take additional parameters to override defaults.
70-
71-
.. code-block:: python
72-
73-
# Generate mipmaps for the texture
74-
self.get_texture("cube/texture.png", mipmap=True)
75-
76-
The Effect Module
77-
-----------------
78-
79-
The effect module needs to be named ``effect.py`` and
80-
located in the root of the effect package. It can only contain a single effect
81-
class. The name of the class doesn't matter right now, but we are
82-
considering allowing multiple effects in the future, so giving it
83-
at least a descriptive name is a good idea.
84-
85-
There are two important methods in an effect:
86-
87-
- ``__init__()``
88-
- ``draw()``
89-
90-
The **initializer** is called before resources are loaded. This way the
91-
effects can register the resources they need. An opengl context should
92-
exist.
93-
94-
The ``draw`` method is called by the configured `EffectManager``
95-
(see :doc:`effectmanagers`) ever frame, or at least every frame
96-
the manager decides the effect should be active.
64+
The ``resources`` directory contains fixed directory names where resources
65+
of specific types are supposed to be located. When an effect package is loaded
66+
paths to these directories are added so the system can find them.
9767

98-
The standard effect example:
99-
100-
.. code-block:: python
101-
102-
import moderngl as mgl
103-
from demosys.effects import effect
104-
from demosys import geometry
105-
# from pyrr import matrix44
106-
107-
108-
class SimpleCubeEffect(effect.Effect):
109-
"""Generated default effect"""
110-
def __init__(self):
111-
self.shader = self.get_shader("cube_plain.glsl", local=True)
112-
self.cube = geometry.cube(4.0, 4.0, 4.0)
113-
114-
@effect.bind_target
115-
def draw(self, time, frametime, target):
116-
self.ctx.enable(mgl.DEPTH_TEST)
117-
118-
# Rotate and translate
119-
m_mv = self.create_transformation(rotation=(time * 1.2, time * 2.1, time * 0.25),
120-
translation=(0.0, 0.0, -8.0))
121-
122-
# Apply the rotation and translation from the system camera
123-
# m_mv = matrix44.multiply(m_mv, self.sys_camera.view_matrix)
124-
125-
# Create normal matrix from model-view
126-
m_normal = self.create_normal_matrix(m_mv)
127-
128-
# Draw the cube
129-
self.shader.uniform("m_proj", self.sys_camera.projection.tobytes())
130-
self.shader.uniform("m_mv", m_mv.astype('f4').tobytes())
131-
self.shader.uniform("m_normal", m_normal.astype('f4').tobytes())
132-
self.shader.uniform("time", time)
133-
self.cube.draw(self.shader)
134-
135-
The parameters in the draw effect is:
136-
137-
- ``time``: The current time reported by our configured ``Timer`` in seconds.
138-
- ``frametime``: The time a frame is expected to take in seconds.
139-
- ``target`` is the target FBO of the effect
68+
.. Note:: Notice that the resource directories contains another sub-directory
69+
with the same name as the effect package. This is because these
70+
folders are by default added to a project wide search path
71+
(for each resource type),
72+
so we should place it in a directory to reduce the chance of a name collisions.
14073

141-
Time can potentially move at any speed or direction, so it's good practice
142-
to make sure the effect can run when time is moving in any direction.
74+
Having resources in the effect package itself is entirely optional.
75+
Resources can be located anywhere you want as long as you tell the system
76+
where they can be found. This is covered in :doc:`/settings`.
14377

144-
The ``bind_target`` decorator is useful when you want to ensure
145-
that an FBO passed to the effect is bound on entry and released on exit.
146-
By default a fake FBO is passed in representing the window frame buffer.
147-
EffectManagers can be used to pass in your own FBOs or another effect
148-
can call ``draw(..)`` requesting the result to end up in the FBO it passes in
149-
and then use this FBO as a texture on a cube or do post processing.
78+
Reasons to have resources in effect packages is to create an independent
79+
resuable effect package you could even distribute. Also when a project
80+
grows with lots of effect packages it can be nice to keep the effect
81+
specific resources in the effect package they belong to instead of
82+
putting all resources for the entire project in the same location.
15083

151-
As we can see in the example, the ``Effect`` base class have a couple
152-
of convenient methods for doing basic matrix math, but generally you
153-
are expected do to these calculations yourself.
84+
The Effect base class have methods avaiable for fetching loaded resources.
85+
See the :py:class:`demosys.effects.Effect`

docs/user_guide/performance.rst

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,28 @@ This is ignoring delays or blocks caused by OpenGL calls.
1111

1212
How important performance is will of course depend on the project.
1313
Visualization for a scientific application doing some heavy
14-
calculations would probably not need to run at 60 fps.
14+
calculations would probably not need to run at 60+ fps.
1515
It's also not illegal to not care about performance.
1616

1717
Probably the biggest enemy to performance in python is **memory allocation**.
18-
19-
Try to avoid creating new objects every frame if possible. This includes
20-
all mutable data types such as lists, sets, dicts.
21-
22-
Another area is updating buffer object data such as VBOs and
23-
Textures. If these are of a fairly small size it might not be a problem,
24-
but do not expect pure Python code to be able to efficiently feed CPU-generated data
25-
to OpenGL. If this data comes from a library though ctypes and we
26-
can avoid re-allocating memory for each frame we might be good,
27-
but this is not always easy to determine and will needs testing.
18+
Try to avoid creating new objects when possible.
2819

2920
Try to do as much as possible on the GPU. Use features like transform
3021
feedback to alter buffer data and use your creativity to find efficient
3122
solutions.
3223

24+
When doing many draw calls, do as little as possible between those
25+
draw calls. Doing matrix math in python even with numpy or pyrr
26+
is **extremely slow**. Try to calculate them ahead of time. Also
27+
moving the matrix calculations inside the shader programs can
28+
help greatly. You can easily do 1000 draw calls using the same
29+
cube and still run 60+ fps even on older hardware. The minute
30+
you throw in some matrix calculation in that loop you might
31+
be able to draw 50 before the framerate tanks.
32+
3333
Performance in rendering is not straight forward to measure in any language.
3434
Simply adding timers in the code will not really tell us much unless
3535
we also query OpenGL about the performance.
3636

37-
We could also try to compile your project with pypy, but we have not tested
38-
this (yet).
39-
4037
We can also strive to do more with less. Rendering, in the end, is really just
4138
about creating illusions.

0 commit comments

Comments
 (0)