Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 
 
 

289 rindas
7.6 KiB

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