source: trunk/src/ticker.cpp @ 210

Last change on this file since 210 was 210, checked in by sam, 10 years ago

Allow for different priorities in game and draw tick levels.

  • Property svn:keywords set to Id
File size: 7.4 KB
Line 
1//
2// Deus Hax (working title)
3// Copyright (c) 2010 Sam Hocevar <sam@hocevar.net>
4//
5
6#if defined HAVE_CONFIG_H
7#   include "config.h"
8#endif
9
10#include <cstdlib>
11#include <cstdio>
12#include <stdint.h>
13
14#include "core.h"
15
16/*
17 * Ticker implementation class
18 */
19
20static class TickerData
21{
22    friend class Ticker;
23
24public:
25    TickerData() :
26        todolist(0), autolist(0),
27        nentities(0),
28        frame(0), deltams(0), bias(0)
29    {
30        for (int i = 0; i < Entity::ALLGROUP_END; i++)
31            list[i] = NULL;
32    }
33
34    ~TickerData()
35    {
36#if !FINAL_RELEASE
37        if (nentities)
38            fprintf(stderr, "ERROR: still %i entities in ticker\n", nentities);
39        if (autolist)
40        {
41            int count = 0;
42            for (Entity *e = autolist; e; e = e->autonext, count++)
43                ;
44            fprintf(stderr, "ERROR: still %i autoreleased entities\n", count);
45        }
46        fprintf(stderr, "INFO: %i frames required to quit\n",
47                frame - quitframe);
48#endif
49    }
50
51private:
52    /* Entity management */
53    Entity *todolist, *autolist;
54    Entity *list[Entity::ALLGROUP_END];
55    int nentities;
56
57    /* Fixed framerate management */
58    int frame, quitframe;
59    Timer timer;
60    float deltams, bias;
61}
62tickerdata;
63
64static TickerData * const data = &tickerdata;
65
66/*
67 * Ticker public class
68 */
69
70void Ticker::Register(Entity *entity)
71{
72    /* If we are called from its constructor, the object's vtable is not
73     * ready yet, so we do not know which group this entity belongs to. Wait
74     * until the first tick. */
75    entity->gamenext = data->todolist;
76    data->todolist = entity;
77    /* Objects are autoreleased by default. Put them in a circular list. */
78    entity->autorelease = 1;
79    entity->autonext = data->autolist;
80    data->autolist = entity;
81    entity->ref = 1;
82
83    data->nentities++;
84}
85
86void Ticker::Ref(Entity *entity)
87{
88#if !FINAL_RELEASE
89    if (entity->destroy)
90        fprintf(stderr, "ERROR: refing entity scheduled for destruction\n");
91#endif
92    if (entity->autorelease)
93    {
94        /* Get the entity out of the autorelease list. This is usually
95         * very fast since the first entry in autolist is the last
96         * registered entity. */
97        for (Entity *e = data->autolist, *prev = NULL; e;
98             prev = e, e = e->autonext)
99        {
100            if (e == entity)
101            {
102                (prev ? prev->autonext : data->autolist) = e->autonext;
103                break;
104            }
105        }
106        entity->autorelease = 0;
107    }
108    else
109        entity->ref++;
110}
111
112int Ticker::Unref(Entity *entity)
113{
114#if !FINAL_RELEASE
115    if (entity->ref <= 0)
116        fprintf(stderr, "ERROR: dereferencing unreferenced entity\n");
117    if (entity->autorelease)
118        fprintf(stderr, "ERROR: dereferencing autoreleased entity\n");
119#endif
120    return --entity->ref;
121}
122
123void Ticker::TickGame()
124{
125    Profiler::Stop(Profiler::STAT_TICK_FRAME);
126    Profiler::Start(Profiler::STAT_TICK_FRAME);
127
128    Profiler::Start(Profiler::STAT_TICK_GAME);
129
130#if 0
131    fprintf(stderr, "-------------------------------------\n");
132    for (int i = 0; i < Entity::ALLGROUP_END; i++)
133    {
134        fprintf(stderr, "%s Group %i\n",
135                (i < Entity::GAMEGROUP_END) ? "Game" : "Draw", i);
136
137        for (Entity *e = data->list[i]; e; )
138        {
139            fprintf(stderr, "  \\-- %s (ref %i, destroy %i)\n", e->GetName(), e->ref, e->destroy);
140            e = (i < Entity::GAMEGROUP_END) ? e->gamenext : e->drawnext;
141        }
142    }
143#endif
144
145    data->frame++;
146
147    data->deltams = data->timer.GetMs();
148    data->bias += data->deltams;
149
150    /* Garbage collect objects that can be destroyed. We can do this
151     * before inserting awaiting objects, because only objects already in
152     * the tick lists can be marked for destruction. */
153    for (int i = 0; i < Entity::ALLGROUP_END; i++)
154        for (Entity *e = data->list[i], *prev = NULL; e; )
155        {
156            if (e->destroy && i < Entity::GAMEGROUP_END)
157            {
158                /* If entity is to be destroyed, remove it from the
159                 * game tick list. */
160                (prev ? prev->gamenext : data->list[i]) = e->gamenext;
161
162                e = e->gamenext;
163            }
164            else if (e->destroy)
165            {
166                /* If entity is to be destroyed, remove it from the
167                 * draw tick list and destroy it. */
168                (prev ? prev->drawnext : data->list[i]) = e->drawnext;
169
170                Entity *tmp = e;
171                e = e->drawnext; /* Can only be in a draw group list */
172                delete tmp;
173
174                data->nentities--;
175            }
176            else
177            {
178                if (e->ref <= 0 && i >= Entity::DRAWGROUP_BEGIN)
179                    e->destroy = 1;
180                prev = e;
181                e = (i < Entity::GAMEGROUP_END) ? e->gamenext : e->drawnext;
182            }
183        }
184
185    /* Insert waiting objects into the appropriate lists */
186    while (data->todolist)
187    {
188        Entity *e = data->todolist;
189        data->todolist = e->gamenext;
190
191        e->gamenext = data->list[e->gamegroup];
192        data->list[e->gamegroup] = e;
193        e->drawnext = data->list[e->drawgroup];
194        data->list[e->drawgroup] = e;
195    }
196
197    /* Tick objects for the game loop */
198    for (int i = Entity::GAMEGROUP_BEGIN; i < Entity::GAMEGROUP_END; i++)
199        for (Entity *e = data->list[i]; e; e = e->gamenext)
200            if (!e->destroy)
201            {
202#if !FINAL_RELEASE
203                if (e->state != Entity::STATE_IDLE)
204                    fprintf(stderr, "ERROR: entity not idle for game tick\n");
205                e->state = Entity::STATE_PRETICK_GAME;
206#endif
207                e->TickGame(data->deltams);
208#if !FINAL_RELEASE
209                if (e->state != Entity::STATE_POSTTICK_GAME)
210                    fprintf(stderr, "ERROR: entity missed super game tick\n");
211                e->state = Entity::STATE_IDLE;
212#endif
213            }
214
215    Profiler::Stop(Profiler::STAT_TICK_GAME);
216}
217
218void Ticker::TickDraw()
219{
220    Profiler::Start(Profiler::STAT_TICK_DRAW);
221
222    /* Tick objects for the draw loop */
223    for (int i = Entity::DRAWGROUP_BEGIN; i < Entity::DRAWGROUP_END; i++)
224        for (Entity *e = data->list[i]; e; e = e->drawnext)
225            if (!e->destroy)
226            {
227#if !FINAL_RELEASE
228                if (e->state != Entity::STATE_IDLE)
229                    fprintf(stderr, "ERROR: entity not idle for draw tick\n");
230                e->state = Entity::STATE_PRETICK_DRAW;
231#endif
232                e->TickDraw(data->deltams);
233#if !FINAL_RELEASE
234                if (e->state != Entity::STATE_POSTTICK_DRAW)
235                    fprintf(stderr, "ERROR: entity missed super draw tick\n");
236                e->state = Entity::STATE_IDLE;
237#endif
238            }
239
240    Profiler::Stop(Profiler::STAT_TICK_DRAW);
241    Profiler::Start(Profiler::STAT_TICK_BLIT);
242}
243
244void Ticker::ClampFps(float deltams)
245{
246    Profiler::Stop(Profiler::STAT_TICK_BLIT);
247
248    if (deltams > data->bias + 200.0f)
249        deltams = data->bias + 200.0f; // Don't go below 5 fps
250    if (deltams > data->bias)
251        data->timer.WaitMs(deltams - data->bias);
252    data->bias -= deltams;
253}
254
255int Ticker::GetFrameNum()
256{
257    return data->frame;
258}
259
260void Ticker::Shutdown()
261{
262    /* We're bailing out. Release all autorelease objects. */
263    while (data->autolist)
264    {
265        data->autolist->ref--;
266        data->autolist = data->autolist->autonext;
267    }
268
269    data->quitframe = data->frame;
270}
271
272int Ticker::Finished()
273{
274    return !data->nentities;
275}
276
Note: See TracBrowser for help on using the repository browser.