睡眠(1)和SDL_Delay(1)需要15毫秒

Sleep(1) and SDL_Delay(1) takes 15 ms

本文关键字:需要 15毫秒 Delay 睡眠 SDL      更新时间:2023-10-16

我正在编写一个C++/SDL/OpenGL应用程序,我遇到了最特殊的错误。通过一个简单的可变时间步长,游戏似乎运行得很好。但随后FPS开始表现怪异。我发现Sleep(1)和SDL_Delay(1)都需要15毫秒才能完成。

在0-15之间对这些功能的任何输入都需要15毫秒才能完成,将FPS锁定在大约64。如果我把它设置为16,它需要30毫秒O.O

我的循环如下:

while (1){
    GLuint t = SDL_GetTicks();
    Sleep(1); //or SDL_Delay(1)
    cout << SDL_GetTicks() - t << endl; //outputs 15
}

它很少会像预期的那样需要1毫秒,但大多数时间都需要15毫秒。

我的操作系统是windows 8.1。CPU是英特尔i7。我正在使用SDL2。

股票代码默认为64赫兹,即15.625毫秒/分。您需要将其更改为1000hz==1ms,时间为BeginPeriod(1)。MSDN文章:

http://msdn.microsoft.com/en-us/library/windows/desktop/dd757624(v=vs.85).aspx

如果这里的目标是获得固定的频率序列,则应该使用更高分辨率的计时器,但不幸的是,这些计时器只能轮询,因此需要将轮询和睡眠相结合来减少cpu开销。示例代码,假设睡眠(1)可能需要长达2毫秒的时间(这在Windows XP中确实发生,但在更高版本的Windows中不会发生)。

/* code for a thread to run at fixed frequency */
#define FREQ 400                        /* frequency */
typedef unsigned long long UI64;        /* unsigned 64 bit int */
LARGE_INTEGER liPerfFreq;               /* used for frequency */
LARGE_INTEGER liPerfTemp;               /* used for query */
UI64 uFreq = FREQ;                      /* process frequency */
UI64 uOrig;                             /* original tick */
UI64 uWait;                             /* tick rate / freq */
UI64 uRem = 0;                          /* tick rate % freq */
UI64 uPrev;                             /* previous tick based on original tick */
UI64 uDelta;                            /* current tick - previous */
UI64 u2ms;                              /* 2ms of ticks */
#if 0                                   /* for optional error check */
static DWORD dwLateStep = 0;
#endif
    /* get frequency */
    QueryPerformanceFrequency(&liPerfFreq);
    u2ms = ((UI64)(liPerfFreq.QuadPart)+499) / ((UI64)500);
    /* wait for some event to start this thread code */
    timeBeginPeriod(1);                 /* set period to 1ms */
    Sleep(128);                         /* wait for it to stabilize */
    QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
    uOrig = uPrev = liPerfTemp.QuadPart;
    while(1){
        /* update uWait and uRem based on uRem */
        uWait = ((UI64)(liPerfFreq.QuadPart) + uRem) / uFreq;
        uRem  = ((UI64)(liPerfFreq.QuadPart) + uRem) % uFreq;
        /* wait for uWait ticks */
        while(1){
            QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
            uDelta = (UI64)(liPerfTemp.QuadPart - uPrev);
            if(uDelta >= uWait)
                break;
            if((uWait - uDelta) > u2ms)
                Sleep(1);
        }
        #if 0                    /* optional error check */
        if(uDelta >= (uWait*2))
            dwLateStep += 1;
        #endif
        uPrev += uWait;
        /* fixed frequency code goes here */
        /*  along with some type of break when done */
    }
    timeEndPeriod(1);                   /* restore period */

看起来15毫秒是操作系统将提供给您的最小切片。我不确定你的具体框架,但睡眠通常能保证最短的睡眠时间。(即,它将睡眠至少1ms。)

SDL_Delay()/Sleep()在时间低于10-15毫秒时无法可靠使用。CPU滴答声的注册速度不够快,无法检测到1毫秒的差异。

请参阅此处的SDL文档。