SDL太让我抓狂

2 Comments

转自己的两条微薄:

1. 搞嵌入式的人之所以有乐趣,就是因为需要程序员专注每一个字节,甚至每一个bite,如何用最少的空间和最快的时间完成一件事情;搞嵌入式的人之所以不开心,因为哪怕是错了一个字节,甚至是一个byte,都会及其抓狂调试查处问题的根源所在。虽然我不搞嵌入式,但是深深地体验了一把从后者到前者的过程。

2. 抓狂了我一整个周末时间的问题在这个时刻终于被搞定,原因就是SDL的EventType从原来的Uint8(unsigned char)提升到了Uint32(unsigned int), 所以很多参数传入之前的程序就会导致直接被截断,导致各种离谱的现象发生。所以提醒各位,在升级第三方 library的时候一定要看API Changes文档,不然自找苦吃。

------------------------------

最近继续搞视频播放器,从许久没有动过的毕设项目抓过来用。但是我还是很手贱地升级了一下其中的一个第三方库SDL。为什么说手贱,因为“不要什么都追求时髦,不要什么都追求最新版本,一定要本着够用万岁的心态,这样才能把事情做得更好。” 但是我还是升级了SDL库。于是悲剧就发生了。各种时间回调不出来,图像decode不出来,声音播放不出来,整整让人绝望了一个周末。。。所以这几天笔者十分焦躁,心理波动较大,如果做出了什么合适的事情上伤害到了某些人,谅解,我也不想这样。

在百般无奈之下只好怀疑是新版本的SDL不稳定,有bug。于是狂搜论坛,看帖子,发帖子,甚至直接给开发者写信过去,尽管现在都没有任何人鸟我。。。最后只要自己不得不看源代码了,并不断对比老版本的源代码。。。及其痛苦。。。而且还在多线程中间用printf之类的调试,一遍又一遍。。。最后终于发现了问题所在,就在这个时刻的前一个小时。
问题很简单其实,低版本的SDL因为event事件不多,所以eventType就用了Uint8表示,但是随着库的开发越来越多的事件加入了,开发者可能担心不够用了,于是把这个改成了Uint32。而这个时候我自己的程序还傻逼的停留在Uint8,所以穿进来的很多参数都溢出了,程序很自然地出错了。。。于是改之,程序活甭乱跳。

只能说,一直都在做应用层开发的人,突然跑到嵌入式的领域,浑然已经忘记了如何去照顾好每一个字节。而且在使用新lib的时候,看开发团队的api changs文档至关重要!

如何自己动手做timer

No Comments

有点标题党,最近在看开源游戏库SDL(http://www.libsdl.org/),只是因为要用到其中的thread和timer这些东西,所以就顺便看了看源代码,发现timer很精悍,所以分享其中的带代码。

1. 首先通过init函数创建一个timer自己的thread(暂且叫timer线程),所以在使用timer之前一点要先调用init函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int
SDL_SYS_TimerInit(void)
{
    timer_alive = 1;
    timer = SDL_CreateThread(RunTimer, NULL);
    if (timer == NULL)
        return (-1);
    return (SDL_SetTimerThreaded(1));
}
static int
RunTimer(void *unused)
{
    while (timer_alive) {
        if (SDL_timer_running) {
            SDL_ThreadedTimerCheck();
        }
        SDL_Delay(1);
    }
    return (0);
}

2. 添加一个timer, 新建一个维护timer信息的内部struct,并讲这个struct添加到维护有所有在running的timer链表中去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static SDL_TimerID
SDL_AddTimerInternal(Uint32 interval, SDL_NewTimerCallback callback,
                     void *param)
{
    SDL_TimerID t;
    t = (SDL_TimerID) SDL_malloc(sizeof(struct _SDL_TimerID));
    if (t) {
        t->interval = ROUND_RESOLUTION(interval);
        t->cb = callback;
        t->param = param;
        t->last_alarm = SDL_GetTicks();
        t->next = SDL_timers;
        SDL_timers = t;
        ++SDL_timer_running;
        list_changed = SDL_TRUE;
    }
#ifdef DEBUG_TIMERS
    printf("SDL_AddTimer(%d) = %08x num_timers = %d\n", interval, (Uint32) t,
           SDL_timer_running);
#endif
    return t;
}

2. timer线程的唯一工作就是不断地去更新timer的ticks,当发现timer的ticks满足interval的时候就触发timer并讲这个timer从链表中移出。

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
void
SDL_ThreadedTimerCheck(void)
{
    Uint32 now, ms;
    SDL_TimerID t, prev, next;
    SDL_bool removed;

    SDL_mutexP(SDL_timer_mutex);
    list_changed = SDL_FALSE;
    now = SDL_GetTicks();
    for (prev = NULL, t = SDL_timers; t; t = next) {
        removed = SDL_FALSE;
        ms = t->interval - SDL_TIMESLICE;
        next = t->next;
        if ((int) (now - t->last_alarm) > (int) ms) {
            struct _SDL_TimerID timer;

            if ((now - t->last_alarm) < t->interval) {
                t->last_alarm += t->interval;
            } else {
                t->last_alarm = now;
            }
#ifdef DEBUG_TIMERS
            printf("Executing timer %p (thread = %lu)\n", t, SDL_ThreadID());
#endif
            timer = *t;
            SDL_mutexV(SDL_timer_mutex);
            ms = timer.cb(timer.interval, timer.param);
            SDL_mutexP(SDL_timer_mutex);
            if (list_changed) {
                /* Abort, list of timers modified */
                /* FIXME: what if ms was changed? */
                break;
            }
            if (ms != t->interval) {
                if (ms) {
                    t->interval = ROUND_RESOLUTION(ms);
                } else {
                    /* Remove timer from the list */
#ifdef DEBUG_TIMERS
                    printf("SDL: Removing timer %p\n", t);
#endif
                    if (prev) {
                        prev->next = next;
                    } else {
                        SDL_timers = next;
                    }
                    SDL_free(t);
                    --SDL_timer_running;
                    removed = SDL_TRUE;
                }
            }
        }
        /* Don't update prev if the timer has disappeared */
        if (!removed) {
            prev = t;
        }
    }
    SDL_mutexV(SDL_timer_mutex);
}

这个过程也阐述了timer的基本工作原理,所以也证明了timer不能用来作为精确控制,而且在SDL里面timer只能最多精确到10ms。
并且联系这个过程可以联想到Cocoa中的NSTimer,其实NSTimer也是这样被添加到NSRunloop中,然后到时间后就触发。

  • RSS
  • Twitter
  • Buzz
  • LinkedIn
  • Flickr