1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
|
============================
Screen Language Optimization
============================
Ren'Py uses a number of techniques to optimize screen language speed. When
using Ren'Py to create complex interfaces, such as those used by simulation
games, it may help to understand how screen language works so you can
achieve maximal performance.
This guide is applicable to the second implementation of screen language,
which was added to Ren'Py 6.18. If your game was created in Ren'Py 6.17
or earlier, it may be necessary to chose the "Force Recompile" option
in the launcher to ensure its screens are upgraded to the latest version.
This guide isn't a substitute for good programming practice. If a screen
uses nested loops to do a lot of unproductive work, it will be slower than
a screen that avoids such looping. While understanding the techniques in
this guide is important, avoiding work entirely is always better than
letting Ren'Py optimize the work for you.
Parameter List
==============
For best performance, all screens should be defined with a parameter list.
When a screen doesn't take parameters, it should be defined with an empty
parameter list. The screen::
screen test():
vbox:
for i in range(10):
text "[i]"
is faster than::
screen test:
vbox:
for i in range(10):
text "[i]"
When a screen is defined without a parameter list, any name used in that
screen can be redefined when the screen is shown. This requires Ren'Py to be
more conservative when analyzing the screen, which can limit the optimization
it performs.
Prediction
==========
Screens perform better when they're predicted in advance. That's because
Ren'Py will execute the screen during prediction time, and load in images
that are used by the screen.
There are two ways Ren'Py automatically predicts screens:
* Ren'Py will predict screens shown by the ``show screen`` and ``call screen``
statements.
* Ren'Py will predict screens that will be shown by the :func:`Show` and :func:`ShowMenu`
actions.
If screens are shown from Python, it's a good idea to start predicting
the screen before it is shown. To start predicting a screen, use the
:func:`renpy.start_predict_screen` function. To stop predicting a screen,
use the :func:`renpy.stop_predict_screen` function.
Displayable Reuse
=================
When evaluating a screen language statement that creates a displayable, Ren'Py
will check to see if the positional arguments and properties given to that
displayable are equal to the positional arguments and properties given the
last time that statement was evaluated. If they are, instead of making a new
displayable, Ren'Py will reuse the existing displayable.
Displayable reuse has a number of performance implications. It saves the cost
of creating a new displayable, which may be significant for displayables that
contain a lot of internal state. More importantly, reusing a displayable means
that in many cases, Ren'Py will not need to re-render the displayable before
showing it to the user, which can lead to another significant speedup.
To compare positional arguments and properties, Ren'Py uses the notion of
equality embodied by Python's == operator. We've extended this notion of
equality to actions by deciding two actions should be equal when they are
indistinguishable from each other – when it doesn't matter which action
is invoked, or which action is queried to determine sensitivity or
selectedness.
All actions provided with Ren'Py conform to this definition. When defining
your own actions, it makes sense to provide them with this notion of
equality. This can be done by supplying an appropriate ``__eq__`` method.
For example::
class TargetShip(Action):
def __init__(self, ship):
self.ship = ship
def __eq__(self, other):
if not isinstance(other, TargetShip):
return False
return self.ship is other.ship
def __call__(self):
global target
target = self.ship
It's important to define the ``__eq__`` function carefully, making sure it
compares all fields, and uses equality (==) and identity (is) comparison
as appropriate.
Const Expressions and Pure Functions
====================================
Ren'Py can exploit the properties of const variables and pure functions
to improve the speed of screen evaluation, and to entirely avoid the
evaluation of some parts of screens.
Definitions
-----------
An expression is **const** (short for constant) if it always represents the
same value when it is evaluated. For Ren'Py's purposes, an expression is
const if and only if the following expressions always evaluate to the same
const value or are undefined:
* Applying any unary, binary, or ternary operator to the expression, provided
the other operands are also const.
* Accessing a field on the expression.
* Indexing the expression, either using a number or an object.
Python numbers and strings are const, as are list, tuple, set, and dict
literals for which all components are const. Ren'Py marks
variables defined using the ``define`` statement as const.
The :func:`renpy.const` and :func:`renpy.not_const` functions
can be used to further control what Ren'Py considers to be const. The
default list of const names is given in the :ref:`Const Names <const-names>`
section below.
If you have a variable that will never change, it makes sense to use ``define``
to both define it and declare it const. For example::
define GRID_WIDTH = 20
define GRID_HEIGHT = 10
A callable function, class, or action is **pure** if, when all of its arguments
are const values, it always gives the same const value. Alternatively, an
expression that invokes a pure function with const expression is also a
const expression.
A large number of default functions, classes, and actions are marked as
pure. These functions are listed in the :ref:`Pure Names <pure-names>`
section below.
Functions are declared pure using the :func:`renpy.pure` function, which
can be used as a decorator for functions declared in the default store.
Const expressions and pure functions do not need to retain the same value
across the following events:
* The end of the init phase.
* A change of the language.
* A style rebuild.
How Const Optimizes Screen Language
-----------------------------------
There are three advantages to ensuring that screen language arguments and
properties are const.
The first is that const arguments and properties are evaluated when
screens are prepared, which is at the end of the init phase, when the
language is changed, or when styles are rebuilt. After that, it is no
longer necessary to spend time evaluating const arguments and properties.
The second is that const works well with displayable reuse. When all of
the arguments and properties of a displayable are const, the displayable
can always be reused, which gains all the benefits of displayable reuse.
Lastly, when Ren'Py encounters a tree of displayables such that all
arguments, properties, and expressions affecting control flow are
also const, Ren'Py will reuse the entire tree without evaluating
expressions or creating displayables. This can yield a significant
performance boost.
For example, the following screen does not execute any Python or create
any displayables after the first time it is predicted or shown::
screen mood_picker():
hbox:
xalign 1.0
yalign 0.0
textbutton "Happy" action SetVariable("mood", "happy")
textbutton "Sad" action SetVariable("mood", "sad")
textbutton "Angry" action SetVariable("mood", "angry")
.. _const-text:
Const Text
----------
When defining text, please note that strings containing new-style text
substitutions are const::
$ t = "Hello, world."
text "[t]"
Supplying a variable containing the text directly is generally not const::
$ t = "Hello, world."
text t
Neither is using percent-substitution::
$ t = "Hello, world."
text "%s" % t
Lastly, note that the _ text translation function is pure, so if it contains
a string, the entire expression is const::
text _("Your score is: [score]")
If a variable containing the text contains substitution, it's necessary to use
the ``!i`` conversion flag::
$ who = "Jane"
$ t = "Hello, [who]!"
text 'Then I told her, "[t!i]"'
Const Functions
----------------
.. include:: inc/const
Profiling
=========
Ren'Py supports profiling screen execution through the ``renpy.profile_screen``
function:
.. include:: inc/profile_screen
.. _const-names:
Const Names
===========
The following names are const by default.
.. include:: inc/const_vars
.. _pure-names:
Pure Names
==========
The following names are both pure and const by default.
.. include:: inc/pure_vars
|