source: trunk/src/ticker.cpp @ 289

Last change on this file since 289 was 289, checked in by sam, 11 years ago

Change the way the Scene object works.

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