2013-10-07 19 views
1

什麼是使用C++在OpenGL窗口中每秒同步幀的高效且準確的方法。我曾嘗試在我的主遊戲循環中放入Sleep(17);,並將其降至每秒59幀,但效果並不準確。這是我沒有在我的主循環Sleep(17); OpenGL窗口代碼:用於OpenGL窗口的高效同步功能

#include <windows.h> 
#include <gl\gl.h> 

HDC hDC = NULL; 
HGLRC hRC = NULL; 
HWND hWnd = NULL; 
HINSTANCE hInstance; 

bool keys[256]; 
bool active = true; 

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); 

GLvoid ReSizeGLScene(GLsizei width, GLsizei height) 
{ 
    if (height == 0) 
    { 
     height = 1; 
    } 

    glViewport(0, 0, width, height); 

    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); 
    glOrtho(0, 800, 0, 600, 1, -1); 
    glMatrixMode(GL_MODELVIEW); 
} 

int InitGL(GLvoid) 
{ 
    glShadeModel(GL_SMOOTH); 
    glClearColor(0.0f, 0.0f, 0.0f, 0.5f); 
    glClearDepth(1.0f); 
    glEnable(GL_DEPTH_TEST); 
    glDepthFunc(GL_LEQUAL); 
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); 
    return 1; 
} 

int DrawGLScene(GLvoid) 
{ 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
    glLoadIdentity(); 

    return 1; 
} 

GLvoid KillGLWindow(GLvoid) 
{ 
    if (hRC) 
    { 
     if (!wglMakeCurrent(NULL,NULL)) 
     { 
      MessageBox(NULL, "Release Of DC And RC Failed." ,"SHUTDOWN ERROR" ,MB_OK | MB_ICONINFORMATION); 
     } 

     if (!wglDeleteContext(hRC)) 
     { 
      MessageBox(NULL, "Release Rendering Context Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); 
     } 
     hRC = NULL; 
    } 

    if (hDC && !ReleaseDC(hWnd, hDC)) 
    { 
     MessageBox(NULL, "Release Device Context Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); 
     hDC = NULL; 
    } 

    if (hWnd && !DestroyWindow(hWnd)) 
    { 
     MessageBox(NULL, "Could Not Release hWnd.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); 
     hWnd = NULL; 
    } 

    if (!UnregisterClass("Project2DClass",hInstance)) 
    { 
     MessageBox(NULL, "Could Not Unregister Class.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); 
     hInstance = NULL; 
    } 
} 

BOOL CreateGLWindow(char* title, int width, int height, int bits) 
{ 
    GLuint PixelFormat; 
    WNDCLASS wc; 
    DWORD dwExStyle; 
    DWORD dwStyle; 
    RECT WindowRect; 
    WindowRect.left = (long)0; 
    WindowRect.right = (long)width; 
    WindowRect.top = (long)0; 
    WindowRect.bottom = (long)height; 

    hInstance = GetModuleHandle(NULL); 
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 
    wc.lpfnWndProc = (WNDPROC) WndProc; 
    wc.cbClsExtra = 0; 
    wc.cbWndExtra = 0; 
    wc.hInstance = hInstance; 
    wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); 
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
    wc.hbrBackground = NULL; 
    wc.lpszMenuName = NULL; 
    wc.lpszClassName = "Project2DClass"; 

    if (!RegisterClass(&wc)) 
    { 
     MessageBox(NULL, "Failed To Register The Window Class.", "ERROR", MB_OK|MB_ICONEXCLAMATION); 
     return 0; 
    } 

    dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; 
    dwStyle = WS_OVERLAPPEDWINDOW; 

    AdjustWindowRectEx(&WindowRect, dwStyle, false, dwExStyle); 

    if (!(hWnd = CreateWindowEx(
      dwExStyle, 
      "Project2DClass", 
      title, 
      dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 
      0, 
      0, 
      WindowRect.right-WindowRect.left, 
      WindowRect.bottom-WindowRect.top, 
      NULL, 
      NULL, 
      hInstance, 
      NULL)) 
    ) 
    { 
     KillGLWindow(); 
     MessageBox(NULL, "Window Creation Error.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 
     return 0; 
    } 

    static PIXELFORMATDESCRIPTOR pfd = 
    { 
     sizeof(PIXELFORMATDESCRIPTOR), 
     1, 
     PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, 
     PFD_TYPE_RGBA, 
     bits, 
     0, 
     0, 
     0, 
     0, 
     0, 
     0, 
     0, 
     0, 
     0, 
     0, 
     0, 
     0, 
     0, 
     32, 
     0, 
     0, 
     PFD_MAIN_PLANE, 
     0, 
     0, 
     0, 
     0 
    }; 

    if (!(hDC = GetDC(hWnd))) 
    { 
     KillGLWindow(); 
     MessageBox(NULL, "Can't Create A GL Device Context.", "ERROR", MB_OK|MB_ICONEXCLAMATION); 
     return 0; 
    } 

    if (!(PixelFormat=ChoosePixelFormat(hDC, &pfd))) 
    { 
     KillGLWindow(); 
     MessageBox(NULL, "Can't Find A Suitable PixelFormat.", "ERROR", MB_OK|MB_ICONEXCLAMATION); 
     return 0; 
    } 

    if(!SetPixelFormat(hDC, PixelFormat, &pfd)) 
    { 
     KillGLWindow(); 
     MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION); 
     return 0; 
    } 

    if (!(hRC = wglCreateContext(hDC))) 
    { 
     KillGLWindow(); 
     MessageBox(NULL, "Can't Create A GL Rendering Context.", "ERROR", MB_OK|MB_ICONEXCLAMATION); 
     return 0; 
    } 

    if(!wglMakeCurrent(hDC, hRC)) 
    { 
     KillGLWindow(); 
     MessageBox(NULL,"Can't Activate The GL Rendering Context.", "ERROR", MB_OK|MB_ICONEXCLAMATION); 
     return 0; 
    } 

    ShowWindow(hWnd, SW_SHOW); 
    SetForegroundWindow(hWnd); 
    SetFocus(hWnd); 
    ReSizeGLScene(width, height); 

    if (!InitGL()) 
    { 
     KillGLWindow(); 
     MessageBox(NULL, "Initialization Failed.", "ERROR", MB_OK|MB_ICONEXCLAMATION); 
     return 0; 
    } 

    return 1; 
} 

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    switch (uMsg) 
    { 
     case WM_ACTIVATE: 
     { 
      if (!HIWORD(wParam)) 
      { 
       active = true; 
      } 
      else 
      { 
       active = false; 
      } 

      return 0; 
     } 

     case WM_SYSCOMMAND: 
     { 
      switch (wParam) 
      { 
       case SC_SCREENSAVE: 
       case SC_MONITORPOWER: 
       return 0; 
      } 
      break; 
     } 

     case WM_CLOSE: 
     { 
      PostQuitMessage(0); 
      return 0; 
     } 

     case WM_KEYDOWN: 
     { 
      keys[wParam] = true; 
      return 0; 
     } 

     case WM_KEYUP: 
     { 
      keys[wParam] = false; 
      return 0; 
     } 

     case WM_SIZE: 
     { 
      ReSizeGLScene(LOWORD(lParam), HIWORD(lParam)); 
      return 0; 
     } 
    } 

    return DefWindowProc(hWnd, uMsg, wParam, lParam); 
} 

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 
{ 
    MSG  msg; 
    BOOL running = true; 

    if (!CreateGLWindow("Project 2D", 800, 600, 32)) 
    { 
     return 0; 
    } 

    while(running) 
    { 
     if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
     { 
      if (msg.message == WM_QUIT) 
      { 
       running = false; 
      } 
      else 
      { 
       TranslateMessage(&msg); 
       DispatchMessage(&msg); 
      } 
     } 
     else 
     { 

      if (active) 
      { 
       if (keys[VK_ESCAPE]) 
       { 
        running = false; 
       } 
       else 
       { 
        DrawGLScene(); 
        SwapBuffers(hDC); 
       } 
      } 
     } 
    } 
    KillGLWindow(); 
    return (msg.wParam); 
} 

還有,我怎樣才能讓我的窗口,我在屏幕中間展開。

編輯:

這裏是一個java等效關閉LWJGL框架:

/* 
* Copyright (c) 2002-2012 LWJGL Project 
* All rights reserved. 
* 
* Redistribution and use in source and binary forms, with or without 
* modification, are permitted provided that the following conditions are 
* met: 
* 
* * Redistributions of source code must retain the above copyright 
* notice, this list of conditions and the following disclaimer. 
* 
* * Redistributions in binary form must reproduce the above copyright 
* notice, this list of conditions and the following disclaimer in the 
* documentation and/or other materials provided with the distribution. 
* 
* * Neither the name of 'LWJGL' nor the names of 
* its contributors may be used to endorse or promote products derived 
* from this software without specific prior written permission. 
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
*/ 
package org.lwjgl.opengl; 

import org.lwjgl.Sys; 

/** 
* A highly accurate sync method that continually adapts to the system 
* it runs on to provide reliable results. 
* 
* @author Riven 
* @author kappaOne 
*/ 
class Sync { 

    /** number of nano seconds in a second */ 
    private static final long NANOS_IN_SECOND = 1000L * 1000L * 1000L; 

    /** The time to sleep/yield until the next frame */ 
    private static long nextFrame = 0; 

    /** whether the initialisation code has run */ 
    private static boolean initialised = false; 

    /** for calculating the averages the previous sleep/yield times are stored */ 
    private static RunningAvg sleepDurations = new RunningAvg(10); 
    private static RunningAvg yieldDurations = new RunningAvg(10); 


    /** 
    * An accurate sync method that will attempt to run at a constant frame rate. 
    * It should be called once every frame. 
    * 
    * @param fps - the desired frame rate, in frames per second 
    */ 
    public static void sync(int fps) { 
     if (fps <= 0) return; 
     if (!initialised) initialise(); 

     try { 
      // sleep until the average sleep time is greater than the time remaining till nextFrame 
      for (long t0 = getTime(), t1; (nextFrame - t0) > sleepDurations.avg(); t0 = t1) { 
       Thread.sleep(1); 
       sleepDurations.add((t1 = getTime()) - t0); // update average sleep time 
      } 

      // slowly dampen sleep average if too high to avoid yielding too much 
      sleepDurations.dampenForLowResTicker(); 

      // yield until the average yield time is greater than the time remaining till nextFrame 
      for (long t0 = getTime(), t1; (nextFrame - t0) > yieldDurations.avg(); t0 = t1) { 
       Thread.yield(); 
       yieldDurations.add((t1 = getTime()) - t0); // update average yield time 
      } 
     } catch (InterruptedException e) { 

     } 

     // schedule next frame, drop frame(s) if already too late for next frame 
     nextFrame = Math.max(nextFrame + NANOS_IN_SECOND/fps, getTime()); 
    } 

    /** 
    * This method will initialise the sync method by setting initial 
    * values for sleepDurations/yieldDurations and nextFrame. 
    * 
    * If running on windows it will start the sleep timer fix. 
    */ 
    private static void initialise() { 
     initialised = true; 

     sleepDurations.init(1000 * 1000); 
     yieldDurations.init((int) (-(getTime() - getTime()) * 1.333)); 

     nextFrame = getTime(); 

     String osName = System.getProperty("os.name"); 

     if (osName.startsWith("Win")) { 
      // On windows the sleep functions can be highly inaccurate by 
      // over 10ms making in unusable. However it can be forced to 
      // be a bit more accurate by running a separate sleeping daemon 
      // thread. 
      Thread timerAccuracyThread = new Thread(new Runnable() { 
       public void run() { 
        try { 
         Thread.sleep(Long.MAX_VALUE); 
        } catch (Exception e) {} 
       } 
      }); 

      timerAccuracyThread.setName("LWJGL Timer"); 
      timerAccuracyThread.setDaemon(true); 
      timerAccuracyThread.start(); 
     } 
    } 

    /** 
    * Get the system time in nano seconds 
    * 
    * @return will return the current time in nano's 
    */ 
    private static long getTime() { 
     return (Sys.getTime() * NANOS_IN_SECOND)/Sys.getTimerResolution(); 
    } 

    private static class RunningAvg { 
     private final long[] slots; 
     private int offset; 

     private static final long DAMPEN_THRESHOLD = 10 * 1000L * 1000L; // 10ms 
     private static final float DAMPEN_FACTOR = 0.9f; // don't change: 0.9f is exactly right! 

     public RunningAvg(int slotCount) { 
      this.slots = new long[slotCount]; 
      this.offset = 0; 
     } 

     public void init(long value) { 
      while (this.offset < this.slots.length) { 
       this.slots[this.offset++] = value; 
      } 
     } 

     public void add(long value) { 
      this.slots[this.offset++ % this.slots.length] = value; 
      this.offset %= this.slots.length; 
     } 

     public long avg() { 
      long sum = 0; 
      for (int i = 0; i < this.slots.length; i++) { 
       sum += this.slots[i]; 
      } 
      return sum/this.slots.length; 
     } 

     public void dampenForLowResTicker() { 
      if (this.avg() > DAMPEN_THRESHOLD) { 
       for (int i = 0; i < this.slots.length; i++) { 
        this.slots[i] *= DAMPEN_FACTOR; 
       } 
      } 
     } 
    } 
} 

回答

5

SwapBuffers與垂直回掃同步(垂直同步)運行。除非在圖形驅動程序中禁用它,否則應該默認啓用它。您還可以使用交換間隔分機來微調SwapBuffers計時和顯示垂直回掃之間的比率。

另外,由於Windows的CPU時間計算錯誤,因此在SwapBuffers之後添加Sleep(0),這可以解決CPU負載過高的問題。

+0

我正在使用'SwapBuffers();'。我的意思是我想控制while循環每秒循環多少次(while循環處於代碼的底部),交換緩衝區和v-sync似乎並沒有這樣做。我將在我的問題的底部添加一個Java等價物。 – kzolp67

+0

@ kzolp67:如果您的閒置/繪圖循環的運行方式比您的顯示器刷新率更快,那麼您很可能會在驅動程序中禁用V-Sync。在調用SwapBuffers之後,執行繪製操作的下一個OpenGL命令將被延遲到交換實際執行之後。啓用v-sync後,只有垂直回掃後纔會發生這種情況。因此,啓用V-Sync後,它永遠不會比顯示刷新速度更快。此外,您不應該進行任何主動等待嘗試與垂直回掃同步。 – datenwolf

+0

但我特別希望循環在一秒內完成60個循環 – kzolp67