2010-01-14 5 views
6

OpenGL 컨텍스트 창에서 일부 애니메이션이 실행 중이므로 계속해서 그릴 필요가 있습니다. 그래서 다음 코드를 생각해 냈습니다 :.NET Folded by 60 FPS?

void InitializeRedrawTimer() 
    { 
     var timer = new Timer(); 
     timer.Interval = 1000/60; 
     timer.Tick += new EventHandler(timer_Tick); 
     timer.Start(); 
    } 

    void timer_Tick(object sender, EventArgs e) 
    { 
     glWin.Draw(); 
    } 

그게 단지 40FPS를 제공합니다. 그러나 간격을 1ms로 설정하면 60을 얻을 수 있습니다. 그래서 다른 20ms는 어디로 갔습니까? 타이머의 정확성이 떨어지거나 그 원인이 무엇입니까? 프로그램을 최대한 빨리 실행하고 싶다면 draw func을 계속 호출 할 수있는 방법이 있습니까?

답변

7

게임 루프를 구현해보세요.

public void MainLoop() 
{ 
     // Hook the application’s idle event 
     System.Windows.Forms.Application.Idle += new EventHandler(OnApplicationIdle); 
     System.Windows.Forms.Application.Run(myForm); 
}  

private void OnApplicationIdle(object sender, EventArgs e) 
{ 
    while (AppStillIdle) 
    { 
     // Render a frame during idle time (no messages are waiting) 
     UpdateEnvironment(); 
     Render3DEnvironment(); 
    } 
} 

private bool AppStillIdle 
{ 
    get 
    { 
     NativeMethods.Message msg; 
     return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0); 
    } 
} 

//And the declarations for those two native methods members:   
[StructLayout(LayoutKind.Sequential)] 
public struct Message 
{ 
    public IntPtr hWnd; 
    public WindowMessage msg; 
    public IntPtr wParam; 
    public IntPtr lParam; 
    public uint time; 
    public System.Drawing.Point p; 
} 

[System.Security.SuppressUnmanagedCodeSecurity] // We won’t use this maliciously 
[DllImport(“User32.dll”, CharSet=CharSet.Auto)] 
public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags); 

, 간단한 우아한, 효과 :

http://blogs.msdn.com/tmiller/archive/2005/05/05/415008.aspx

기본 루프 (약간 자신의 원래 버전과 독서의 용이성을위한 새로운 SDK의 버전에서 수정) . 여분의 할당, 여분의 콜렉션, 그냥 작동 .. 대기열에 메시지가 없을 때 Idle 이벤트가 발생하고 핸들러가 메시지가 나타날 때까지 계속 루핑을 계속합니다.이 경우에는 메시지가 중지됩니다. 처리하면 유휴 이벤트가 다시 시작되고 프로세스가 다시 시작됩니다.

이 링크는 응용 프로그램의 유휴 이벤트를 사용하는 링크를 설명합니다. 그것은 유용 할 수 있습니다. 약간의 시간을 테스트하거나 수면을 수행하여 필요한 fps로 느리게 테스트 할 수 있습니다. 가장 정확한 타이머는 System.Diagnostics.StopWatch 클래스를 사용해보십시오.

조금 도움이 되었기를 바랍니다.

+0

니스! 'Application.Idle'을 사용하면 1ms 타이머보다 훨씬 더 우아 해 보입니다. 나는 내일이 StopWatch를 시도해야 할 것이다. 감사! ** 편집 : ** 사실, 거짓말을했습니다. 창 크기를 조정하거나 이동할 때 유휴 상태가 중지되어 다시 그리기가 중단됩니다. 기본적으로 그리기의 우선 순위가 낮아집니다. 어쩌면 최소한의 다시 그리기 속도를 적용하기 위해 타이머를 고수 할 것입니다. 두 가지 방법 중 하나를 결합 할 수있는 이유도 없습니다. – mpen

+3

또한 StopWatch는 콜백 이벤트가 아닌 타이밍 작업에만 유용합니다. – mpen

+3

유휴 상태가 끝나면 타이머를 시작한 다음 유휴 이벤트가 발생하면 다시 중지/확인하십시오. 이런 식으로 아주 가깝게 얻을 수 있습니다. – Nanook

2

Windows Forms Timer는 궁극적으로 컴퓨터의 표준 시스템 클럭 해상도로 해상도가 제한됩니다. 이것은 컴퓨터마다 다르지만 일반적으로 1ms ~ 20ms입니다.

WinForms에서 코드에서 타이머 간격을 요청할 때마다 Windows는 이 아닌 더 자주으로 지정되고 지정된 밀리 초 수만을 보장합니다. 사용자가 지정한 밀리 초 단위로 Timer가 호출된다는 보장이 없습니다.

이것은 Windows가 실시간이 아닌 시간 공유 OS이기 때문에 가능합니다. 다른 프로세스와 스레드는 동시에 실행되도록 예약되어 스레드가 프로세서 사용을 위해 정기적으로 대기해야합니다. 따라서 정확한 간격이나 지연 시간 (밀리 초)에 의존하는 WinForms 타이머 코드를 작성하면 안됩니다.

상황에 따라 간격을 1ms로 설정하면 Windows에서 가능한 한 자주이 타이머를 트리거하도록 지시하는 것입니다. 지금까지 보았 듯이 1ms (60fps = 약 17ms)보다 훨씬 적습니다.

하드웨어가 지원하는 경우 Windows API가 고해상도/성능 타이머를 제공합니다 (How to use the high resolution timer 참조). 단점은 응용 프로그램 CPU 사용량이 더 높은 성능을 지원하기 위해 증가한다는 것입니다.

전반적으로 하드웨어/시스템에 독립적 인 게임 루프를 구현하는 것이 좋습니다. 이렇게하면 실제 프레임 속도에 관계없이 장면의 인식 된 애니메이션이 일정하게 나타납니다.자세한 내용은 these articles 좋은 배경을 제공하십시오.

3

에 따라 WPF 및 애니메이션 지원을 살펴보면 WinForms에서 자신 만의 에미넴을 작성하는 것이 더 나은 솔루션 일 수 있습니다.

DirectX를 사용할 때 .NET 래퍼가 있고 매우 빠르지 만 WPF 또는 WinForms를 사용하는 것이 더 어렵습니다.

가 지속적으로 그리기 함수를 호출하려면, 당신은 BeginInvoke에 전달 대리자에서 BeginInvoke

  • 는 다시 그리기 방법의 끝에 추가

    • 는 다시 그리기 방법에 드로잉을 수행, 무효화 당신이 그리는 컨트롤

    그러나 사용자가 자신의 컴퓨터를 죽이는 것을 좋아하지 않을 수 있습니다!

    Application.Idle도 좋은 옵션이지만, 필자는 필요한 경우 WmPaint 메시지를 보내는 속도가 느려지는 것을 좋아합니다.

  • +0

    그림을 그린 직후에 무효화하는 것이 타이머 방법보다 훨씬 높은 프레임 속도를 생성한다는 것을 알았습니다 (어떤 이유로 65fps가 타이머에서 가장 높은 값이었습니다). –

    -1

    나는 라인에 주어진 샘플 코드를 발견했습니다 :

    timer.Interval = 1000/60; 
    

    그것은 정수 나누기의 작동을 사용하므로 결과는 정수로 반올림됩니다 정확하게 16.6666ms 수 없지만 17ms . 따라서 타이머가 화면 새로 고침보다 큰 눈금을 만듭니다.

    +0

    정수 부분이 반올림되지 않고 잘립니다. 따라서 1000/60은 16입니다 ([proof] (https://repl.it/GGNW/0)). 타이머가 정확하다면 ~ 62.5 FPS가되어야합니다. – mpen

    +0

    오. 나는 그것에 대해 잊어 버렸다. 하지만 어쨌든 16ms라면 모니터와 동기화되지 않습니다. – Dennis

    +1

    모니터에 따라 다릅니다. 확실히 이것이 gsync/freesync의 문제는 아니지만, 관계없이, 나는 확실히'timer.Interval'이 대답이 아님을 확신합니다. – mpen