gpt4 book ai didi

c++ - OpenGL 窗口的高效同步功能

转载 作者:行者123 更新时间:2023-11-30 01:20:33 25 4
gpt4 key购买 nike

什么是使用 C++ 在 OpenGL 窗口中每秒同步帧数的有效且准确的方法。我试过将 Sleep(17); 放在我的主游戏循环中,这将它降低到每秒 59 帧,但它并不准确和高效。这是我的主循环中没有 Sleep(17); 的 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);
}

另外,我怎样才能让我的窗口在屏幕中间启动。

编辑:

这是 LWJGL 框架的 java 等价物:

/*
* 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;
}
}
}
}
}

最佳答案

SwapBuffers 启用了垂直回扫同步 (V-Sync)。除非你在图形驱动程序中禁用它,否则它应该默认启用。您还可以使用 Swap Interval 扩展来微调 SwapBuffers 计时和显示垂直回扫之间的比率。

此外,由于 Windows 错误地计算了 CPU 时间消耗,因此在 SwapBuffers 之后添加一个 Sleep(0),这解决了指示 CPU 负载过高的问题。

关于c++ - OpenGL 窗口的高效同步功能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19222194/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com