c++ - Efficient sync function for an OpenGL window -
what efficient , accurate way of syncing frames per second in opengl window using c++. have tried putting sleep(17);
in main game loop , gets down 59 frames per second not accurate , efficient. here opengl window code without sleep(17);
in main loop:
#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 , 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 register 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 gl device context.", "error", mb_ok|mb_iconexclamation); return 0; } if (!(pixelformat=choosepixelformat(hdc, &pfd))) { killglwindow(); messagebox(null, "can't find suitable pixelformat.", "error", mb_ok|mb_iconexclamation); return 0; } if(!setpixelformat(hdc, pixelformat, &pfd)) { killglwindow(); messagebox(null,"can't set pixelformat.","error",mb_ok|mb_iconexclamation); return 0; } if (!(hrc = wglcreatecontext(hdc))) { killglwindow(); messagebox(null, "can't create gl rendering context.", "error", mb_ok|mb_iconexclamation); return 0; } if(!wglmakecurrent(hdc, hrc)) { killglwindow(); messagebox(null,"can't activate 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); }
and also, how can window launch in middle of screen.
edit:
here java equivalent off lwjgl framework:
/* * copyright (c) 2002-2012 lwjgl project * rights reserved. * * redistribution , use in source , binary forms, or without * modification, permitted provided following conditions * met: * * * redistributions of source code must retain above copyright * notice, list of conditions , following disclaimer. * * * redistributions in binary form must reproduce above copyright * notice, list of conditions , following disclaimer in * documentation and/or other materials provided distribution. * * * neither name of 'lwjgl' nor names of * contributors may used endorse or promote products derived * software without specific prior written permission. * * software provided copyright holders , contributors * "as is" , express or implied warranties, including, not limited * to, implied warranties of merchantability , fitness particular * purpose disclaimed. in no event shall copyright owner or * contributors liable direct, indirect, incidental, special, * exemplary, or consequential damages (including, not limited to, * procurement of substitute goods or services; loss of use, data, or * profits; or business interruption) caused , on theory of * liability, whether in contract, strict liability, or tort (including * negligence or otherwise) arising in way out of use of * software, if advised of possibility of such damage. */ package org.lwjgl.opengl; import org.lwjgl.sys; /** * highly accurate sync method continually adapts system * runs on provide reliable results. * * @author riven * @author kappaone */ class sync { /** number of nano seconds in second */ private static final long nanos_in_second = 1000l * 1000l * 1000l; /** time sleep/yield until next frame */ private static long nextframe = 0; /** whether initialisation code has run */ private static boolean initialised = false; /** calculating averages previous sleep/yield times stored */ private static runningavg sleepdurations = new runningavg(10); private static runningavg yielddurations = new runningavg(10); /** * accurate sync method attempt run @ constant frame rate. * should called once every frame. * * @param fps - desired frame rate, in frames per second */ public static void sync(int fps) { if (fps <= 0) return; if (!initialised) initialise(); try { // sleep until average sleep time greater time remaining till nextframe (long t0 = gettime(), t1; (nextframe - t0) > sleepdurations.avg(); t0 = t1) { thread.sleep(1); sleepdurations.add((t1 = gettime()) - t0); // update average sleep time } // dampen sleep average if high avoid yielding sleepdurations.dampenforlowresticker(); // yield until average yield time greater time remaining till nextframe (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 late next frame nextframe = math.max(nextframe + nanos_in_second / fps, gettime()); } /** * method initialise sync method setting initial * values sleepdurations/yielddurations , nextframe. * * if running on windows start 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 sleep functions can highly inaccurate // on 10ms making in unusable. can forced // bit more accurate running 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(); } } /** * system time in nano seconds * * @return return 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 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; (int = 0; < this.slots.length; i++) { sum += this.slots[i]; } return sum / this.slots.length; } public void dampenforlowresticker() { if (this.avg() > dampen_threshold) { (int = 0; < this.slots.length; i++) { this.slots[i] *= dampen_factor; } } } } }
swapbuffers
vertical retrace synchronization (v-sync) enabled. unless disabled in graphics driver should enabled default. can use swap interval extension fine tune ratio between swapbuffers
timing , display vertical retrace.
also, since windows it's cpu time consumption calculation wrongly, add sleep(0)
after swapbuffers
, fixes issue of high indicated cpu load.
Comments
Post a Comment