2014-04-24 15 views
5

Tôi đang viết một ứng dụng C++/SDL/OpenGL và tôi đã có lỗi đặc biệt nhất. Trò chơi dường như hoạt động tốt với một dấu thời gian biến đổi đơn giản. Nhưng sau đó FPS bắt đầu hành xử kỳ lạ. Tôi đã tìm ra rằng cả Sleep (1) và SDL_Delay (1) mất 15 ms để hoàn thành.Ngủ (1) và SDL_Delay (1) mất 15 ms

Bất kỳ đầu vào những chức năng giữa 0-15 mất 15ms để hoàn thành, khóa FPS vào khoảng 64. Nếu tôi đặt nó là 16, phải mất 30 MS o.o

Tôi không có ý tưởng tại sao điều này đang xảy ra. Đây là lỗi kỳ lạ nhất mà tôi từng gặp phải.

vòng lặp của tôi trông như thế này:

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

Nó sẽ rất hiếm khi mất 1ms khi nó được yêu cầu, nhưng phần lớn thời gian phải mất 15ms.

Hệ điều hành của tôi là các cửa sổ 8.1. CPU là một i7 intel. Tôi đang sử dụng SDL2. Bất kỳ ý tưởng nào cũng sẽ được đánh giá cao vì tôi không biết gì.

+2

có thể trùng lặp của [WinAPI Sleep() chức năng ngủ lâu hơn mong đợi] (http://stackoverflow.com/questions/9518106/winapi-sleep-function- call-sleeps-for-longer-than-expected) –

+0

Bạn không muốn đặt một thread/process để ngủ nếu bạn mong đợi nó thức dậy trong thời gian thực. Sử dụng một spinlock nếu bạn không muốn lo lắng về việc lập kế hoạch. –

Trả lời

8

Giá trị mặc định là 64 hz hoặc 15,625 ms/tick. Bạn cần thay đổi điều này thành 1000hz == 1ms với timeBeginPeriod (1). MSDN bài viết:

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

Nếu mục tiêu ở đây là để có được một chuỗi tần số cố định, bạn nên sử dụng một bộ đếm thời gian độ phân giải cao hơn, nhưng tiếc là những chỉ có thể được thăm dò, do đó, một sự kết hợp của bầu cử và ngủ để giảm cpu chi phí là cần thiết. Ví dụ mã, giả định rằng một giấc ngủ (1) có thể mất đến gần 2 ms (mà không xảy ra với Windows XP, nhưng không phải với các phiên bản sau của 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 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 

    /* wait for some event to start this thread code */ 
    timeBeginPeriod(1);     /* set period to 1ms */ 
    Sleep(128);       /* wait for it to stabilize */ 

    u2ms = ((UI64)(liPerfFreq.QuadPart)+499)/((UI64)500); 

    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 */ 
+1

Cảm ơn rất nhiều! Tôi đã không nhận thức được điều này! – user3346893

+0

Vì uPrev được dựa trên số lượng được tính từ khi đọc ban đầu của bộ hẹn giờ, sẽ không có bất kỳ sự trôi dạt nào theo thời gian sử dụng phương pháp này trái ngược với việc dựa vào đồng bằng giữa thời gian đọc hiện tại và trước đó của bộ hẹn giờ. Kể từ khi giấc ngủ được thiết lập để cho phép lên đến 2 ms chậm trễ, mã ví dụ nên được tốt cho đến khoảng 400Hz. Nếu không hỗ trợ Windows XP, thì sự chậm trễ có thể giả định rằng Sleep (1) sẽ mất khoảng 1 ms (có thể muốn thêm một số lợi nhuận cho điều này), trong trường hợp đó nó sẽ tốt cho 800Hz. – rcgldr

+0

Nếu khoảng thời gian cố định là bội số chính xác của 1ms và không cần hỗ trợ Windows XP, thì có thể sử dụng chức năng sự kiện hẹn giờ đa phương tiện. Bài viết MSDN: http://msdn.microsoft.com/en-us/library/windows/desktop/dd742877(v=vs.85).aspx. Windows XP là một vấn đề, ticker sẽ thực sự chạy ở 1024Hz, và 1000Hz được mô phỏng bằng cách thêm một dấu tick sau 42, 42, sau đó là 41 ticks để 128 tick thực tế kết thúc ở 125 pseudo ticks (để trở về ranh giới 1ms) . – rcgldr

0

SDL_Delay()/Sleep() không thể được sử dụng đáng tin cậy với thời gian dưới 10-15 mili giây. CPU ticks không đăng ký đủ nhanh để phát hiện sự khác biệt 1 ms.

Xem SDL docs here.

+0

Bạn là chính xác, cảm ơn tôi đã không nhận thức được điều đó! – user3346893

+1

CPU ticks đăng ký nhanh hơn đủ, vấn đề thực sự là những gì xảy ra khi bạn đặt một thread đang chạy vào giấc ngủ. Nó đi vào mặt sau của một hàng đợi sẵn sàng, và sau đó nó là ở lòng thương xót của lịch trình. Một số khung công tác đủ thông minh để tránh hành vi này nếu khoảng thời gian đủ nhỏ, nhưng dường như đây không phải là một trong số chúng. Ngoài ra, bạn có thể đặt ưu tiên quy trình thành thời gian thực và quá trình này sẽ thường xuyên ưu tiên những người khác trước lượng tử 10-15 ms điển hình. –

1

Dường như 15 ms là lát nhỏ nhất mà hệ điều hành sẽ phân phối cho bạn. Tôi không chắc chắn về khuôn khổ cụ thể của bạn, nhưng giấc ngủ thường đảm bảo một thời gian ngủ tối thiểu. (ví dụ: nó sẽ ngủ ít nhất 1ms.)

Các vấn đề liên quan