gzdoom-gles/src/win32/win32gliface.cpp
Christoph Oelckers 12a2dede7e - added CVAR to disable WGL_EXT_swap_control_tear.
At least on faster NVidia hardware, setting this to false and gl_finishbeforeswap to true gives a better experience because it reduces screen tearing - but the same setting will reduce frame rate quite dramatically on Intel and can cause bad stalls on some older GPUs when rendering camera textures.

(cherry picked from commit cc65490062)
2018-05-23 10:13:35 +02:00

1296 lines
No EOL
31 KiB
C++

/*
**
**
**---------------------------------------------------------------------------
** Copyright 2003-2005 Tim Stump
** Copyright 2005-2016 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. 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.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
**---------------------------------------------------------------------------
**
*/
//#include "gl/system/gl_system.h"
#define _WIN32_WINNT 0x0501
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <GL/gl.h>
#include "wglext.h"
#include "win32iface.h"
#include "win32gliface.h"
//#include "gl/gl_intern.h"
#include "x86.h"
#include "templates.h"
#include "version.h"
#include "c_console.h"
#include "hardware.h"
#include "v_video.h"
#include "i_input.h"
#include "i_system.h"
#include "doomstat.h"
#include "v_text.h"
#include "m_argv.h"
#include "doomerrors.h"
#include "optwin32.h"
//#include "gl_defs.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/system/gl_framebuffer.h"
#include "gl/system/gl_swframebuffer.h"
extern HWND Window;
extern "C" {
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}
void gl_CalculateCPUSpeed();
extern int NewWidth, NewHeight, NewBits, DisplayBits;
// these get used before GLEW is initialized so we have to use separate pointers with different names
PFNWGLCHOOSEPIXELFORMATARBPROC myWglChoosePixelFormatARB; // = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
PFNWGLCREATECONTEXTATTRIBSARBPROC myWglCreateContextAttribsARB;
PFNWGLSWAPINTERVALEXTPROC myWglSwapIntervalExtProc;
CUSTOM_CVAR(Bool, gl_debug, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
Printf("This won't take effect until " GAMENAME " is restarted.\n");
}
EXTERN_CVAR(Bool, vr_enable_quadbuffered)
EXTERN_CVAR(Int, vid_refreshrate)
//==========================================================================
//
//
//
//==========================================================================
class Win32GLVideo : public IVideo
{
public:
Win32GLVideo(int parm);
virtual ~Win32GLVideo();
EDisplayType GetDisplayType() { return DISPLAY_Both; }
void SetWindowedScale(float scale);
void StartModeIterator(int bits, bool fs);
bool NextMode(int *width, int *height, bool *letterbox);
bool GoFullscreen(bool yes);
DFrameBuffer *CreateFrameBuffer (int width, int height, bool bgra, bool fs, DFrameBuffer *old);
virtual bool SetResolution(int width, int height, int bits);
void DumpAdapters();
bool InitHardware(HWND Window, int multisample);
void Shutdown();
bool SetFullscreen(const char *devicename, int w, int h, int bits, int hz);
HDC m_hDC;
protected:
struct ModeInfo
{
ModeInfo(int inX, int inY, int inBits, int inRealY, int inRefresh)
: next(NULL),
width(inX),
height(inY),
bits(inBits),
refreshHz(inRefresh),
realheight(inRealY)
{}
ModeInfo *next;
int width, height, bits, refreshHz, realheight;
} *m_Modes;
ModeInfo *m_IteratorMode;
int m_IteratorBits;
bool m_IteratorFS;
bool m_IsFullscreen;
int m_trueHeight;
int m_DisplayWidth, m_DisplayHeight, m_DisplayBits, m_DisplayHz;
HMODULE hmRender;
char m_DisplayDeviceBuffer[CCHDEVICENAME];
char *m_DisplayDeviceName;
HMONITOR m_hMonitor;
HWND m_Window;
HGLRC m_hRC;
HWND InitDummy();
void ShutdownDummy(HWND dummy);
bool SetPixelFormat();
bool SetupPixelFormat(int multisample);
void GetDisplayDeviceName();
void MakeModesList();
void AddMode(int x, int y, int bits, int baseHeight, int refreshHz);
void FreeModes();
public:
int GetTrueHeight() { return m_trueHeight; }
};
//==========================================================================
//
//
//
//==========================================================================
Win32GLVideo::Win32GLVideo(int parm) : m_Modes(NULL), m_IsFullscreen(false)
{
#ifdef _WIN32
gl_CalculateCPUSpeed();
#endif
I_SetWndProc();
m_DisplayWidth = vid_defwidth;
m_DisplayHeight = vid_defheight;
m_DisplayBits = 32;
m_DisplayHz = 60;
GetDisplayDeviceName();
MakeModesList();
SetPixelFormat();
}
//==========================================================================
//
//
//
//==========================================================================
Win32GLVideo::~Win32GLVideo()
{
FreeModes();
if (GLRenderer != NULL) GLRenderer->FlushTextures();
}
//==========================================================================
//
//
//
//==========================================================================
void Win32GLVideo::SetWindowedScale(float scale)
{
}
//==========================================================================
//
//
//
//==========================================================================
struct MonitorEnumState
{
int curIdx;
HMONITOR hFoundMonitor;
};
static BOOL CALLBACK GetDisplayDeviceNameMonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
{
using OptWin32::GetMonitorInfoA;
MonitorEnumState *state = reinterpret_cast<MonitorEnumState *>(dwData);
MONITORINFOEX mi;
mi.cbSize = sizeof mi;
assert (GetMonitorInfo);
GetMonitorInfo(hMonitor, &mi);
// This assumes the monitors are returned by EnumDisplayMonitors in the
// order they're found in the Direct3D9 adapters list. Fingers crossed...
if (state->curIdx == vid_adapter)
{
state->hFoundMonitor = hMonitor;
// Don't stop enumeration; this makes EnumDisplayMonitors fail. I like
// proper fails.
}
++state->curIdx;
return TRUE;
}
//==========================================================================
//
//
//
//==========================================================================
void Win32GLVideo::GetDisplayDeviceName()
{
using OptWin32::GetMonitorInfoA;
// If anything goes wrong, anything at all, everything uses the primary
// monitor.
m_DisplayDeviceName = 0;
m_hMonitor = 0;
MonitorEnumState mes;
mes.curIdx = 1;
mes.hFoundMonitor = 0;
// Could also use EnumDisplayDevices, I guess. That might work.
if (EnumDisplayMonitors(0, 0, &GetDisplayDeviceNameMonitorEnumProc, LPARAM(&mes)))
{
if (mes.hFoundMonitor)
{
MONITORINFOEX mi;
mi.cbSize = sizeof mi;
assert (GetMonitorInfo);
if (GetMonitorInfo(mes.hFoundMonitor, &mi))
{
strcpy(m_DisplayDeviceBuffer, mi.szDevice);
m_DisplayDeviceName = m_DisplayDeviceBuffer;
m_hMonitor = mes.hFoundMonitor;
}
}
}
}
//==========================================================================
//
//
//
//==========================================================================
void Win32GLVideo::MakeModesList()
{
ModeInfo *pMode, *nextmode;
DEVMODE dm;
int mode = 0;
memset(&dm, 0, sizeof(DEVMODE));
dm.dmSize = sizeof(DEVMODE);
while (EnumDisplaySettings(m_DisplayDeviceName, mode, &dm))
{
this->AddMode(dm.dmPelsWidth, dm.dmPelsHeight, dm.dmBitsPerPel, dm.dmPelsHeight, dm.dmDisplayFrequency);
++mode;
}
for (pMode = m_Modes; pMode != NULL; pMode = nextmode)
{
nextmode = pMode->next;
if (pMode->realheight == pMode->height && pMode->height * 4/3 == pMode->width)
{
if (pMode->width >= 360)
{
AddMode (pMode->width, pMode->width * 9/16, pMode->bits, pMode->height, pMode->refreshHz);
}
if (pMode->width > 640)
{
AddMode (pMode->width, pMode->width * 10/16, pMode->bits, pMode->height, pMode->refreshHz);
}
}
}
}
//==========================================================================
//
//
//
//==========================================================================
void Win32GLVideo::StartModeIterator(int bits, bool fs)
{
m_IteratorMode = m_Modes;
// I think it's better to ignore the game-side settings of bit depth.
// The GL renderer will always default to 32 bits because 16 bit modes cannot have a stencil buffer.
m_IteratorBits = 32;
m_IteratorFS = fs;
}
//==========================================================================
//
//
//
//==========================================================================
bool Win32GLVideo::NextMode(int *width, int *height, bool *letterbox)
{
if (m_IteratorMode)
{
while (m_IteratorMode && m_IteratorMode->bits != m_IteratorBits)
{
m_IteratorMode = m_IteratorMode->next;
}
if (m_IteratorMode)
{
*width = m_IteratorMode->width;
*height = m_IteratorMode->height;
if (letterbox != NULL) *letterbox = m_IteratorMode->realheight != m_IteratorMode->height;
m_IteratorMode = m_IteratorMode->next;
return true;
}
}
return false;
}
//==========================================================================
//
//
//
//==========================================================================
void Win32GLVideo::AddMode(int x, int y, int bits, int baseHeight, int refreshHz)
{
ModeInfo **probep = &m_Modes;
ModeInfo *probe = m_Modes;
// This mode may have been already added to the list because it is
// enumerated multiple times at different refresh rates. If it's
// not present, add it to the right spot in the list; otherwise, do nothing.
// Modes are sorted first by width, then by height, then by depth. In each
// case the order is ascending.
if (bits < 32) return;
for (; probe != 0; probep = &probe->next, probe = probe->next)
{
if (probe->width != x) continue;
// Width is equal
if (probe->height != y) continue;
// Width is equal
if (probe->realheight != baseHeight) continue;
// Height is equal
if (probe->bits != bits) continue;
// Bits is equal
if (probe->refreshHz > refreshHz) return;
probe->refreshHz = refreshHz;
return;
}
*probep = new ModeInfo (x, y, bits, baseHeight, refreshHz);
(*probep)->next = probe;
}
//==========================================================================
//
//
//
//==========================================================================
void Win32GLVideo::FreeModes()
{
ModeInfo *mode = m_Modes;
while (mode)
{
ModeInfo *tempmode = mode;
mode = mode->next;
delete tempmode;
}
m_Modes = NULL;
}
//==========================================================================
//
//
//
//==========================================================================
bool Win32GLVideo::GoFullscreen(bool yes)
{
m_IsFullscreen = yes;
m_trueHeight = m_DisplayHeight;
if (yes)
{
// If in windowed mode, any height is good.
for (ModeInfo *mode = m_Modes; mode != NULL; mode = mode->next)
{
if (mode->width == m_DisplayWidth && mode->height == m_DisplayHeight)
{
m_trueHeight = mode->realheight;
break;
}
}
}
if (yes)
{
SetFullscreen(m_DisplayDeviceName, m_DisplayWidth, m_trueHeight, m_DisplayBits, m_DisplayHz);
}
else
{
SetFullscreen(m_DisplayDeviceName, 0,0,0,0);
}
return yes;
}
//==========================================================================
//
//
//
//==========================================================================
DFrameBuffer *Win32GLVideo::CreateFrameBuffer(int width, int height, bool bgra, bool fs, DFrameBuffer *old)
{
Win32GLFrameBuffer *fb;
if (fs)
{
I_ClosestResolution(&width, &height, 32);
}
m_DisplayWidth = width;
m_DisplayHeight = height;
m_DisplayBits = 32;
m_DisplayHz = 60;
if (vid_refreshrate == 0)
{
for (ModeInfo *mode = m_Modes; mode != NULL; mode = mode->next)
{
if (mode->width == m_DisplayWidth && mode->height == m_DisplayHeight && mode->bits == m_DisplayBits)
{
m_DisplayHz = MAX<int>(m_DisplayHz, mode->refreshHz);
}
}
}
else
{
m_DisplayHz = vid_refreshrate;
}
if (old != NULL)
{ // Reuse the old framebuffer if its attributes are the same
fb = static_cast<Win32GLFrameBuffer *> (old);
if (fb->m_Width == m_DisplayWidth &&
fb->m_Height == m_DisplayHeight &&
fb->m_Bits == m_DisplayBits &&
fb->m_RefreshHz == m_DisplayHz &&
fb->m_Fullscreen == fs &&
fb->m_Bgra == bgra)
{
return old;
}
//old->GetFlash(flashColor, flashAmount);
delete old;
}
if (vid_renderer == 1)
fb = new OpenGLFrameBuffer(m_hMonitor, m_DisplayWidth, m_DisplayHeight, m_DisplayBits, m_DisplayHz, fs);
else
fb = new OpenGLSWFrameBuffer(m_hMonitor, m_DisplayWidth, m_DisplayHeight, m_DisplayBits, m_DisplayHz, fs, bgra);
return fb;
}
//==========================================================================
//
//
//
//==========================================================================
bool Win32GLVideo::SetResolution (int width, int height, int bits)
{
if (GLRenderer != NULL) GLRenderer->FlushTextures();
I_ShutdownGraphics();
Video = new Win32GLVideo(0);
if (Video == NULL) I_FatalError ("Failed to initialize display");
bits=32;
V_DoModeSetup(width, height, bits);
return true; // We must return true because the old video context no longer exists.
}
//==========================================================================
//
//
//
//==========================================================================
struct DumpAdaptersState
{
unsigned index;
char *displayDeviceName;
};
static BOOL CALLBACK DumpAdaptersMonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
{
using OptWin32::GetMonitorInfoA;
DumpAdaptersState *state = reinterpret_cast<DumpAdaptersState *>(dwData);
MONITORINFOEX mi;
mi.cbSize=sizeof mi;
char moreinfo[64] = "";
bool active = true;
assert (GetMonitorInfo);
if (GetMonitorInfo(hMonitor, &mi))
{
bool primary = !!(mi.dwFlags & MONITORINFOF_PRIMARY);
mysnprintf(moreinfo, countof(moreinfo), " [%ldx%ld @ (%ld,%ld)]%s",
mi.rcMonitor.right - mi.rcMonitor.left,
mi.rcMonitor.bottom - mi.rcMonitor.top,
mi.rcMonitor.left, mi.rcMonitor.top,
primary ? " (Primary)" : "");
if (!state->displayDeviceName && !primary)
active = false;//primary selected, but this ain't primary
else if (state->displayDeviceName && strcmp(state->displayDeviceName, mi.szDevice) != 0)
active = false;//this isn't the selected one
}
Printf("%s%u. %s\n",
active ? TEXTCOLOR_BOLD : "",
state->index,
moreinfo);
++state->index;
return TRUE;
}
//==========================================================================
//
//
//
//==========================================================================
void Win32GLVideo::DumpAdapters()
{
DumpAdaptersState das;
das.index = 1;
das.displayDeviceName = m_DisplayDeviceName;
EnumDisplayMonitors(0, 0, DumpAdaptersMonitorEnumProc, LPARAM(&das));
}
//==========================================================================
//
//
//
//==========================================================================
HWND Win32GLVideo::InitDummy()
{
HMODULE g_hInst = GetModuleHandle(NULL);
HWND dummy;
//Create a rect structure for the size/position of the window
RECT windowRect;
windowRect.left = 0;
windowRect.right = 64;
windowRect.top = 0;
windowRect.bottom = 64;
//Window class structure
WNDCLASS wc;
//Fill in window class struct
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = (WNDPROC) DefWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_hInst;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = "GZDoomOpenGLDummyWindow";
//Register window class
if(!RegisterClass(&wc))
{
return 0;
}
//Set window style & extended style
DWORD style, exStyle;
exStyle = WS_EX_CLIENTEDGE;
style = WS_SYSMENU | WS_BORDER | WS_CAPTION;// | WS_VISIBLE;
//Adjust the window size so that client area is the size requested
AdjustWindowRectEx(&windowRect, style, false, exStyle);
//Create Window
if(!(dummy = CreateWindowEx(exStyle,
"GZDoomOpenGLDummyWindow",
"GZDOOM",
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | style,
0, 0,
windowRect.right-windowRect.left,
windowRect.bottom-windowRect.top,
NULL, NULL,
g_hInst,
NULL)))
{
UnregisterClass("GZDoomOpenGLDummyWindow", g_hInst);
return 0;
}
ShowWindow(dummy, SW_HIDE);
return dummy;
}
//==========================================================================
//
//
//
//==========================================================================
void Win32GLVideo::ShutdownDummy(HWND dummy)
{
DestroyWindow(dummy);
UnregisterClass("GZDoomOpenGLDummyWindow", GetModuleHandle(NULL));
}
//==========================================================================
//
//
//
//==========================================================================
bool Win32GLVideo::SetPixelFormat()
{
HDC hDC;
HGLRC hRC;
HWND dummy;
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
32, // color depth
0, 0, 0, 0, 0, 0,
0,
0,
0,
0, 0, 0, 0,
16, // z depth
0, // stencil buffer
0,
PFD_MAIN_PLANE,
0,
0, 0, 0
};
int pixelFormat;
// we have to create a dummy window to init stuff from or the full init stuff fails
dummy = InitDummy();
hDC = GetDC(dummy);
pixelFormat = ChoosePixelFormat(hDC, &pfd);
DescribePixelFormat(hDC, pixelFormat, sizeof(pfd), &pfd);
::SetPixelFormat(hDC, pixelFormat, &pfd);
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
myWglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
myWglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
// any extra stuff here?
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hRC);
ReleaseDC(dummy, hDC);
ShutdownDummy(dummy);
return true;
}
//==========================================================================
//
//
//
//==========================================================================
bool Win32GLVideo::SetupPixelFormat(int multisample)
{
int i;
int colorDepth;
HDC deskDC;
int attributes[28];
int pixelFormat;
unsigned int numFormats;
float attribsFloat[] = {0.0f, 0.0f};
deskDC = GetDC(GetDesktopWindow());
colorDepth = GetDeviceCaps(deskDC, BITSPIXEL);
ReleaseDC(GetDesktopWindow(), deskDC);
if (myWglChoosePixelFormatARB)
{
again:
attributes[0] = WGL_RED_BITS_ARB; //bits
attributes[1] = 8;
attributes[2] = WGL_GREEN_BITS_ARB; //bits
attributes[3] = 8;
attributes[4] = WGL_BLUE_BITS_ARB; //bits
attributes[5] = 8;
attributes[6] = WGL_ALPHA_BITS_ARB;
attributes[7] = 8;
attributes[8] = WGL_DEPTH_BITS_ARB;
attributes[9] = 24;
attributes[10] = WGL_STENCIL_BITS_ARB;
attributes[11] = 8;
attributes[12] = WGL_DRAW_TO_WINDOW_ARB; //required to be true
attributes[13] = true;
attributes[14] = WGL_SUPPORT_OPENGL_ARB;
attributes[15] = true;
attributes[16] = WGL_DOUBLE_BUFFER_ARB;
attributes[17] = true;
if (multisample > 0)
{
attributes[18] = WGL_SAMPLE_BUFFERS_ARB;
attributes[19] = true;
attributes[20] = WGL_SAMPLES_ARB;
attributes[21] = multisample;
i = 22;
}
else
{
i = 18;
}
attributes[i++] = WGL_ACCELERATION_ARB; //required to be FULL_ACCELERATION_ARB
attributes[i++] = WGL_FULL_ACCELERATION_ARB;
if (vr_enable_quadbuffered)
{
// [BB] Starting with driver version 314.07, NVIDIA GeForce cards support OpenGL quad buffered
// stereo rendering with 3D Vision hardware. Select the corresponding attribute here.
attributes[i++] = WGL_STEREO_ARB;
attributes[i++] = true;
}
attributes[i++] = 0;
attributes[i++] = 0;
if (!myWglChoosePixelFormatARB(m_hDC, attributes, attribsFloat, 1, &pixelFormat, &numFormats))
{
Printf("R_OPENGL: Couldn't choose pixel format. Retrying in compatibility mode\n");
goto oldmethod;
}
if (numFormats == 0)
{
if (vr_enable_quadbuffered)
{
Printf("R_OPENGL: No valid pixel formats found for VR quadbuffering. Retrying without this feature\n");
vr_enable_quadbuffered = false;
goto again;
}
Printf("R_OPENGL: No valid pixel formats found. Retrying in compatibility mode\n");
goto oldmethod;
}
}
else
{
oldmethod:
// If wglChoosePixelFormatARB is not found we have to do it the old fashioned way.
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
32, // color depth
0, 0, 0, 0, 0, 0,
0,
0,
0,
0, 0, 0, 0,
32, // z depth
8, // stencil buffer
0,
PFD_MAIN_PLANE,
0,
0, 0, 0
};
pixelFormat = ChoosePixelFormat(m_hDC, &pfd);
DescribePixelFormat(m_hDC, pixelFormat, sizeof(pfd), &pfd);
if (pfd.dwFlags & PFD_GENERIC_FORMAT)
{
I_Error("R_OPENGL: OpenGL driver not accelerated!");
return false;
}
}
if (!::SetPixelFormat(m_hDC, pixelFormat, NULL))
{
I_Error("R_OPENGL: Couldn't set pixel format.\n");
return false;
}
return true;
}
//==========================================================================
//
//
//
//==========================================================================
bool Win32GLVideo::InitHardware (HWND Window, int multisample)
{
m_Window=Window;
m_hDC = GetDC(Window);
if (!SetupPixelFormat(multisample))
{
return false;
}
int prof = WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
const char *version = Args->CheckValue("-glversion");
if (version != nullptr && strtod(version, nullptr) < 3.0) prof = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
for (; prof <= WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; prof++)
{
m_hRC = NULL;
if (myWglCreateContextAttribsARB != NULL)
{
// let's try to get the best version possible. Some drivers only give us the version we request
// which breaks all version checks for feature support. The highest used features we use are from version 4.4, and 3.0 is a requirement.
static int versions[] = { 46, 45, 44, 43, 42, 41, 40, 33, 32, 31, 30, -1 };
for (int i = 0; versions[i] > 0; i++)
{
int ctxAttribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, versions[i] / 10,
WGL_CONTEXT_MINOR_VERSION_ARB, versions[i] % 10,
WGL_CONTEXT_FLAGS_ARB, gl_debug ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
WGL_CONTEXT_PROFILE_MASK_ARB, prof,
0
};
m_hRC = myWglCreateContextAttribsARB(m_hDC, 0, ctxAttribs);
if (m_hRC != NULL) break;
}
}
if (m_hRC == NULL && prof == WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB)
{
m_hRC = wglCreateContext(m_hDC);
if (m_hRC == NULL)
{
I_Error("R_OPENGL: Unable to create an OpenGL render context.\n");
return false;
}
}
if (m_hRC != NULL)
{
wglMakeCurrent(m_hDC, m_hRC);
return true;
}
}
// We get here if the driver doesn't support the modern context creation API which always means an old driver.
I_Error ("R_OPENGL: Unable to create an OpenGL render context. Insufficient driver support for context creation\n");
return false;
}
//==========================================================================
//
//
//
//==========================================================================
void Win32GLVideo::Shutdown()
{
if (m_hRC)
{
wglMakeCurrent(0, 0);
wglDeleteContext(m_hRC);
}
if (m_hDC) ReleaseDC(m_Window, m_hDC);
}
//==========================================================================
//
//
//
//==========================================================================
bool Win32GLVideo::SetFullscreen(const char *devicename, int w, int h, int bits, int hz)
{
DEVMODE dm;
if (w==0)
{
ChangeDisplaySettingsEx(devicename, 0, 0, 0, 0);
}
else
{
dm.dmSize = sizeof(DEVMODE);
dm.dmSpecVersion = DM_SPECVERSION;//Somebody owes me...
dm.dmDriverExtra = 0;//...1 hour of my life back
dm.dmPelsWidth = w;
dm.dmPelsHeight = h;
dm.dmBitsPerPel = bits;
dm.dmDisplayFrequency = hz;
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
if (DISP_CHANGE_SUCCESSFUL != ChangeDisplaySettingsEx(devicename, &dm, 0, CDS_FULLSCREEN, 0))
{
dm.dmFields &= ~DM_DISPLAYFREQUENCY;
return DISP_CHANGE_SUCCESSFUL == ChangeDisplaySettingsEx(devicename, &dm, 0, CDS_FULLSCREEN, 0);
}
}
return true;
}
//==========================================================================
//
//
//
//==========================================================================
//==========================================================================
//
//
//
//==========================================================================
Win32GLFrameBuffer::Win32GLFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen, bool bgra) : BaseWinFB(width, height, bgra)
{
using OptWin32::GetMonitorInfoA;
m_Width = width;
m_Height = height;
m_Bits = bits;
m_RefreshHz = refreshHz;
m_Fullscreen = fullscreen;
m_Bgra = bgra;
m_Lock=0;
RECT r;
LONG style, exStyle;
static_cast<Win32GLVideo *>(Video)->GoFullscreen(fullscreen);
m_displayDeviceName = 0;
int monX = 0, monY = 0;
if (hMonitor)
{
MONITORINFOEX mi;
mi.cbSize = sizeof mi;
assert (GetMonitorInfo);
if (GetMonitorInfo(HMONITOR(hMonitor), &mi))
{
strcpy(m_displayDeviceNameBuffer, mi.szDevice);
m_displayDeviceName = m_displayDeviceNameBuffer;
monX = int(mi.rcMonitor.left);
monY = int(mi.rcMonitor.top);
}
}
ShowWindow (Window, SW_SHOW);
GetWindowRect(Window, &r);
style = WS_VISIBLE | WS_CLIPSIBLINGS;
exStyle = 0;
if (fullscreen)
style |= WS_POPUP;
else
{
style |= WS_OVERLAPPEDWINDOW;
exStyle |= WS_EX_WINDOWEDGE;
}
SetWindowLong(Window, GWL_STYLE, style);
SetWindowLong(Window, GWL_EXSTYLE, exStyle);
SetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
if (fullscreen)
{
MoveWindow(Window, monX, monY, width, GetTrueHeight(), FALSE);
// And now, seriously, it IS in the right place. Promise.
}
else
{
RECT windowRect;
windowRect.left = r.left;
windowRect.top = r.top;
windowRect.right = windowRect.left + width;
windowRect.bottom = windowRect.top + height;
AdjustWindowRectEx(&windowRect, style, FALSE, exStyle);
MoveWindow(Window, windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, FALSE);
I_RestoreWindowedPos();
}
if (!static_cast<Win32GLVideo *>(Video)->InitHardware(Window, 0))
{
vid_renderer = 0;
return;
}
HDC hDC = GetDC(Window);
const char *wglext = nullptr;
myWglSwapIntervalExtProc = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
auto myWglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
if (myWglGetExtensionsStringARB)
{
wglext = myWglGetExtensionsStringARB(hDC);
}
else
{
auto myWglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT");
if (myWglGetExtensionsStringEXT)
{
wglext = myWglGetExtensionsStringEXT();
}
}
SwapInterval = 1;
if (wglext != nullptr)
{
if (strstr(wglext, "WGL_EXT_swap_control_tear"))
{
SwapInterval = -1;
}
}
m_supportsGamma = !!GetDeviceGammaRamp(hDC, (void *)m_origGamma);
ReleaseDC(Window, hDC);
}
//==========================================================================
//
//
//
//==========================================================================
Win32GLFrameBuffer::~Win32GLFrameBuffer()
{
ResetGammaTable();
I_SaveWindowedPos();
static_cast<Win32GLVideo *>(Video)->SetFullscreen(m_displayDeviceName, 0,0,0,0);
ShowWindow (Window, SW_SHOW);
SetWindowLong(Window, GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW);
SetWindowLong(Window, GWL_EXSTYLE, WS_EX_WINDOWEDGE);
SetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
I_GetEvent();
static_cast<Win32GLVideo *>(Video)->Shutdown();
}
//==========================================================================
//
//
//
//==========================================================================
void Win32GLFrameBuffer::InitializeState()
{
}
//==========================================================================
//
//
//
//==========================================================================
void Win32GLFrameBuffer::ResetGammaTable()
{
if (m_supportsGamma)
{
HDC hDC = GetDC(Window);
SetDeviceGammaRamp(hDC, (void *)m_origGamma);
ReleaseDC(Window, hDC);
}
}
void Win32GLFrameBuffer::SetGammaTable(uint16_t *tbl)
{
if (m_supportsGamma)
{
HDC hDC = GetDC(Window);
SetDeviceGammaRamp(hDC, (void *)tbl);
ReleaseDC(Window, hDC);
}
}
//==========================================================================
//
//
//
//==========================================================================
bool Win32GLFrameBuffer::Lock(bool buffered)
{
m_Lock++;
Buffer = MemBuffer;
return true;
}
bool Win32GLFrameBuffer::Lock ()
{
return Lock(false);
}
void Win32GLFrameBuffer::Unlock ()
{
m_Lock--;
}
bool Win32GLFrameBuffer::IsLocked ()
{
return m_Lock > 0;
}
//==========================================================================
//
//
//
//==========================================================================
bool Win32GLFrameBuffer::IsFullscreen()
{
return m_Fullscreen;
}
void Win32GLFrameBuffer::PaletteChanged()
{
}
int Win32GLFrameBuffer::QueryNewPalette()
{
return 0;
}
HRESULT Win32GLFrameBuffer::GetHR()
{
return 0;
}
void Win32GLFrameBuffer::Blank ()
{
}
bool Win32GLFrameBuffer::PaintToWindow ()
{
return false;
}
bool Win32GLFrameBuffer::CreateResources ()
{
return false;
}
void Win32GLFrameBuffer::ReleaseResources ()
{
}
//==========================================================================
//
//
//
//==========================================================================
EXTERN_CVAR(Bool, vid_vsync);
CUSTOM_CVAR(Bool, gl_control_tear, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
vid_vsync.Callback();
}
void Win32GLFrameBuffer::SetVSync (bool vsync)
{
if (myWglSwapIntervalExtProc != NULL) myWglSwapIntervalExtProc(vsync ? (gl_control_tear? SwapInterval : 1) : 0);
}
void Win32GLFrameBuffer::SwapBuffers()
{
// Limiting the frame rate is as simple as waiting for the timer to signal this event.
I_FPSLimit();
::SwapBuffers(static_cast<Win32GLVideo *>(Video)->m_hDC);
}
//==========================================================================
//
//
//
//==========================================================================
void Win32GLFrameBuffer::NewRefreshRate ()
{
if (m_Fullscreen)
{
setmodeneeded = true;
NewWidth = screen->VideoWidth;
NewHeight = screen->VideoHeight;
NewBits = DisplayBits;
}
}
int Win32GLFrameBuffer::GetClientWidth()
{
RECT rect = { 0 };
GetClientRect(Window, &rect);
return rect.right - rect.left;
}
int Win32GLFrameBuffer::GetClientHeight()
{
RECT rect = { 0 };
GetClientRect(Window, &rect);
return rect.bottom - rect.top;
}
int Win32GLFrameBuffer::GetTrueHeight()
{
return static_cast<Win32GLVideo *>(Video)->GetTrueHeight();
}
IVideo *gl_CreateVideo()
{
return new Win32GLVideo(0);
}