2009-07-28 21:32:10 +00:00
|
|
|
// Copyright (C) 2003 Dolphin Project.
|
2008-12-08 05:25:12 +00:00
|
|
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, version 2.0.
|
|
|
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
|
|
|
|
// Official SVN repository and contact information can be found at
|
|
|
|
// http://code.google.com/p/dolphin-emu/
|
|
|
|
|
|
|
|
#include "Globals.h"
|
2009-09-13 09:23:30 +00:00
|
|
|
#include "VideoConfig.h"
|
2008-12-08 05:25:12 +00:00
|
|
|
#include "IniFile.h"
|
2009-02-22 21:16:12 +00:00
|
|
|
#include "Setup.h"
|
2011-01-31 01:28:32 +00:00
|
|
|
#include "Core.h"
|
|
|
|
#include "Host.h"
|
|
|
|
#include "VideoBackend.h"
|
|
|
|
#include "ConfigManager.h"
|
2008-12-08 05:25:12 +00:00
|
|
|
|
|
|
|
#include "Render.h"
|
2010-12-21 23:58:25 +00:00
|
|
|
#include "VertexShaderManager.h"
|
2008-12-08 05:25:12 +00:00
|
|
|
|
2010-07-16 21:56:40 +00:00
|
|
|
#include "GLUtil.h"
|
|
|
|
|
2008-12-08 05:25:12 +00:00
|
|
|
#if defined(_WIN32)
|
2010-12-19 19:43:18 +00:00
|
|
|
#include "EmuWindow.h"
|
2010-07-16 21:56:40 +00:00
|
|
|
static HDC hDC = NULL; // Private GDI Device Context
|
|
|
|
static HGLRC hRC = NULL; // Permanent Rendering Context
|
2008-12-08 05:25:12 +00:00
|
|
|
#else
|
2010-07-16 21:56:40 +00:00
|
|
|
GLWindow GLWin;
|
2008-12-08 05:25:12 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// Handles OpenGL and the window
|
|
|
|
|
2009-02-28 16:33:59 +00:00
|
|
|
// Window dimensions.
|
|
|
|
static int s_backbuffer_width;
|
|
|
|
static int s_backbuffer_height;
|
2008-12-08 05:25:12 +00:00
|
|
|
|
|
|
|
void OpenGL_SwapBuffers()
|
|
|
|
{
|
2009-09-09 20:47:11 +00:00
|
|
|
#if defined(USE_WX) && USE_WX
|
2010-02-20 04:18:19 +00:00
|
|
|
GLWin.glCanvas->SwapBuffers();
|
2010-06-04 04:59:07 +00:00
|
|
|
#elif defined(__APPLE__)
|
2011-02-02 21:52:43 +00:00
|
|
|
[GLWin.cocoaCtx flushBuffer];
|
2008-12-08 05:25:12 +00:00
|
|
|
#elif defined(_WIN32)
|
2010-02-20 04:18:19 +00:00
|
|
|
SwapBuffers(hDC);
|
2008-12-10 23:23:05 +00:00
|
|
|
#elif defined(HAVE_X11) && HAVE_X11
|
2010-02-20 04:18:19 +00:00
|
|
|
glXSwapBuffers(GLWin.dpy, GLWin.win);
|
2008-12-08 05:25:12 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2009-04-03 14:35:49 +00:00
|
|
|
u32 OpenGL_GetBackbufferWidth()
|
|
|
|
{
|
2010-02-20 04:18:19 +00:00
|
|
|
return s_backbuffer_width;
|
2008-12-21 21:02:43 +00:00
|
|
|
}
|
|
|
|
|
2009-04-03 14:35:49 +00:00
|
|
|
u32 OpenGL_GetBackbufferHeight()
|
|
|
|
{
|
2010-02-20 04:18:19 +00:00
|
|
|
return s_backbuffer_height;
|
2008-12-21 21:02:43 +00:00
|
|
|
}
|
|
|
|
|
2009-01-15 06:48:15 +00:00
|
|
|
void OpenGL_SetWindowText(const char *text)
|
2008-12-08 05:25:12 +00:00
|
|
|
{
|
2009-09-09 20:47:11 +00:00
|
|
|
#if defined(USE_WX) && USE_WX
|
2011-01-11 04:09:11 +00:00
|
|
|
// Handled by Host_UpdateTitle()
|
2010-06-04 04:59:07 +00:00
|
|
|
#elif defined(__APPLE__)
|
2011-01-30 14:20:20 +00:00
|
|
|
[GLWin.cocoaWin setTitle: [NSString stringWithUTF8String: text]];
|
2008-12-08 05:25:12 +00:00
|
|
|
#elif defined(_WIN32)
|
2009-07-30 07:08:31 +00:00
|
|
|
// TODO convert text to unicode and change SetWindowTextA to SetWindowText
|
2010-02-20 04:18:19 +00:00
|
|
|
SetWindowTextA(EmuWindow::GetWnd(), text);
|
2010-07-16 21:56:40 +00:00
|
|
|
#elif defined(HAVE_X11) && HAVE_X11
|
|
|
|
// Tell X to ask the window manager to set the window title.
|
|
|
|
// (X itself doesn't provide window title functionality.)
|
2010-02-20 04:18:19 +00:00
|
|
|
XStoreName(GLWin.dpy, GLWin.win, text);
|
2008-12-08 05:25:12 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2011-01-31 01:28:32 +00:00
|
|
|
static void*& VideoWindowHandle()
|
|
|
|
{
|
|
|
|
return SConfig::GetInstance().m_LocalCoreStartupParameter.hMainWindow;
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace OGL
|
|
|
|
{
|
|
|
|
|
2009-02-19 06:52:01 +00:00
|
|
|
// Draw messages on top of the screen
|
2011-01-31 01:28:32 +00:00
|
|
|
unsigned int VideoBackend::PeekMessages()
|
2008-12-08 05:25:12 +00:00
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
2010-02-20 04:18:19 +00:00
|
|
|
// TODO: peekmessage
|
|
|
|
MSG msg;
|
|
|
|
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
|
|
|
|
{
|
|
|
|
if (msg.message == WM_QUIT)
|
|
|
|
return FALSE;
|
|
|
|
TranslateMessage(&msg);
|
|
|
|
DispatchMessage(&msg);
|
|
|
|
}
|
|
|
|
return TRUE;
|
2009-01-15 06:48:15 +00:00
|
|
|
#else
|
2011-02-02 04:40:27 +00:00
|
|
|
return false;
|
2008-12-08 05:25:12 +00:00
|
|
|
#endif
|
|
|
|
}
|
2009-02-28 16:33:59 +00:00
|
|
|
|
2009-02-19 06:52:01 +00:00
|
|
|
// Show the current FPS
|
2011-01-31 01:28:32 +00:00
|
|
|
void VideoBackend::UpdateFPSDisplay(const char *text)
|
2008-12-08 05:25:12 +00:00
|
|
|
{
|
2011-01-10 23:48:59 +00:00
|
|
|
char temp[100];
|
|
|
|
snprintf(temp, sizeof temp, "%s | OpenGL | %s", svn_rev_str, text);
|
2010-02-20 04:18:19 +00:00
|
|
|
OpenGL_SetWindowText(temp);
|
2008-12-08 05:25:12 +00:00
|
|
|
}
|
|
|
|
|
2011-01-31 01:28:32 +00:00
|
|
|
}
|
|
|
|
|
2010-02-16 04:59:45 +00:00
|
|
|
#if defined(HAVE_X11) && HAVE_X11
|
2011-01-27 20:47:58 +00:00
|
|
|
void XEventThread();
|
2010-03-15 23:25:11 +00:00
|
|
|
|
2010-02-16 04:59:45 +00:00
|
|
|
void CreateXWindow (void)
|
|
|
|
{
|
2010-04-12 01:33:10 +00:00
|
|
|
Atom wmProtocols[1];
|
|
|
|
|
2010-09-19 23:40:03 +00:00
|
|
|
// use evdpy to create the window, so that connection gets the events
|
|
|
|
// the colormap needs to be created on the same display, because it
|
|
|
|
// is a client side structure, as well as wmProtocols(or so it seems)
|
|
|
|
// GLWin.win is a xserver global window handle, so it can be used by both
|
|
|
|
// display connections
|
|
|
|
|
2010-04-12 01:33:10 +00:00
|
|
|
// Setup window attributes
|
2010-09-19 23:40:03 +00:00
|
|
|
GLWin.attr.colormap = XCreateColormap(GLWin.evdpy,
|
2010-04-12 01:33:10 +00:00
|
|
|
GLWin.parent, GLWin.vi->visual, AllocNone);
|
2010-08-08 00:13:05 +00:00
|
|
|
GLWin.attr.event_mask = KeyPressMask | StructureNotifyMask | FocusChangeMask;
|
2010-09-19 23:40:03 +00:00
|
|
|
GLWin.attr.background_pixel = BlackPixel(GLWin.evdpy, GLWin.screen);
|
2010-04-12 01:33:10 +00:00
|
|
|
GLWin.attr.border_pixel = 0;
|
|
|
|
|
|
|
|
// Create the window
|
2010-09-19 23:40:03 +00:00
|
|
|
GLWin.win = XCreateWindow(GLWin.evdpy, GLWin.parent,
|
2010-03-08 23:29:16 +00:00
|
|
|
GLWin.x, GLWin.y, GLWin.width, GLWin.height, 0, GLWin.vi->depth, InputOutput, GLWin.vi->visual,
|
2010-03-15 23:25:11 +00:00
|
|
|
CWBorderPixel | CWBackPixel | CWColormap | CWEventMask, &GLWin.attr);
|
2010-09-19 23:40:03 +00:00
|
|
|
wmProtocols[0] = XInternAtom(GLWin.evdpy, "WM_DELETE_WINDOW", True);
|
|
|
|
XSetWMProtocols(GLWin.evdpy, GLWin.win, wmProtocols, 1);
|
|
|
|
XSetStandardProperties(GLWin.evdpy, GLWin.win, "GPU", "GPU", None, NULL, 0, NULL);
|
|
|
|
XMapRaised(GLWin.evdpy, GLWin.win);
|
|
|
|
XSync(GLWin.evdpy, True);
|
2010-03-16 03:34:27 +00:00
|
|
|
|
2011-01-27 20:47:58 +00:00
|
|
|
GLWin.xEventThread = std::thread(XEventThread);
|
2010-02-16 04:59:45 +00:00
|
|
|
}
|
|
|
|
|
2010-04-12 01:33:10 +00:00
|
|
|
void DestroyXWindow(void)
|
2010-02-16 04:59:45 +00:00
|
|
|
{
|
2010-04-12 01:33:10 +00:00
|
|
|
XUnmapWindow(GLWin.dpy, GLWin.win);
|
|
|
|
GLWin.win = 0;
|
2011-01-27 22:48:46 +00:00
|
|
|
GLWin.xEventThread.join();
|
2010-09-19 23:40:03 +00:00
|
|
|
XFreeColormap(GLWin.evdpy, GLWin.attr.colormap);
|
2010-03-15 23:25:11 +00:00
|
|
|
}
|
|
|
|
|
2011-01-27 20:47:58 +00:00
|
|
|
void XEventThread()
|
2010-03-15 23:25:11 +00:00
|
|
|
{
|
2010-12-21 23:58:25 +00:00
|
|
|
// Free look variables
|
|
|
|
static bool mouseLookEnabled = false;
|
|
|
|
static bool mouseMoveEnabled = false;
|
|
|
|
static float lastMouse[2];
|
2010-03-15 23:25:11 +00:00
|
|
|
while (GLWin.win)
|
|
|
|
{
|
|
|
|
XEvent event;
|
|
|
|
KeySym key;
|
2010-12-21 23:58:25 +00:00
|
|
|
for (int num_events = XPending(GLWin.evdpy); num_events > 0; num_events--)
|
|
|
|
{
|
2010-09-19 23:40:03 +00:00
|
|
|
XNextEvent(GLWin.evdpy, &event);
|
2010-03-15 23:25:11 +00:00
|
|
|
switch(event.type) {
|
|
|
|
case KeyPress:
|
|
|
|
key = XLookupKeysym((XKeyEvent*)&event, 0);
|
|
|
|
switch (key)
|
|
|
|
{
|
|
|
|
case XK_3:
|
|
|
|
OSDChoice = 1;
|
|
|
|
// Toggle native resolution
|
2010-09-30 15:24:34 +00:00
|
|
|
g_Config.iEFBScale = g_Config.iEFBScale + 1;
|
|
|
|
if (g_Config.iEFBScale > 4) g_Config.iEFBScale = 0;
|
2010-03-15 23:25:11 +00:00
|
|
|
break;
|
|
|
|
case XK_4:
|
|
|
|
OSDChoice = 2;
|
|
|
|
// Toggle aspect ratio
|
|
|
|
g_Config.iAspectRatio = (g_Config.iAspectRatio + 1) & 3;
|
|
|
|
break;
|
|
|
|
case XK_5:
|
|
|
|
OSDChoice = 3;
|
|
|
|
// Toggle EFB copy
|
2010-11-18 04:01:16 +00:00
|
|
|
if (!g_Config.bEFBCopyEnable || g_Config.bCopyEFBToTexture)
|
2010-03-15 23:25:11 +00:00
|
|
|
{
|
2010-11-18 04:01:16 +00:00
|
|
|
g_Config.bEFBCopyEnable ^= true;
|
2010-03-15 23:25:11 +00:00
|
|
|
g_Config.bCopyEFBToTexture = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_Config.bCopyEFBToTexture = !g_Config.bCopyEFBToTexture;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case XK_6:
|
|
|
|
OSDChoice = 4;
|
|
|
|
g_Config.bDisableFog = !g_Config.bDisableFog;
|
|
|
|
break;
|
|
|
|
case XK_7:
|
|
|
|
OSDChoice = 5;
|
|
|
|
g_Config.bDisableLighting = !g_Config.bDisableLighting;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2010-12-21 23:58:25 +00:00
|
|
|
if (g_Config.bFreeLook)
|
|
|
|
{
|
|
|
|
static float debugSpeed = 1.0f;
|
|
|
|
switch (key)
|
|
|
|
{
|
|
|
|
case XK_parenleft:
|
|
|
|
debugSpeed /= 2.0f;
|
|
|
|
break;
|
|
|
|
case XK_parenright:
|
|
|
|
debugSpeed *= 2.0f;
|
|
|
|
break;
|
|
|
|
case XK_w:
|
|
|
|
VertexShaderManager::TranslateView(0.0f, debugSpeed);
|
|
|
|
break;
|
|
|
|
case XK_s:
|
|
|
|
VertexShaderManager::TranslateView(0.0f, -debugSpeed);
|
|
|
|
break;
|
|
|
|
case XK_a:
|
|
|
|
VertexShaderManager::TranslateView(debugSpeed, 0.0f);
|
|
|
|
break;
|
|
|
|
case XK_d:
|
|
|
|
VertexShaderManager::TranslateView(-debugSpeed, 0.0f);
|
|
|
|
break;
|
|
|
|
case XK_r:
|
|
|
|
VertexShaderManager::ResetView();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ButtonPress:
|
|
|
|
if (g_Config.bFreeLook)
|
|
|
|
{
|
|
|
|
switch (event.xbutton.button)
|
|
|
|
{
|
|
|
|
case 2: // Middle button
|
|
|
|
lastMouse[0] = event.xbutton.x;
|
|
|
|
lastMouse[1] = event.xbutton.y;
|
|
|
|
mouseMoveEnabled = true;
|
|
|
|
break;
|
|
|
|
case 3: // Right button
|
|
|
|
lastMouse[0] = event.xbutton.x;
|
|
|
|
lastMouse[1] = event.xbutton.y;
|
|
|
|
mouseLookEnabled = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ButtonRelease:
|
|
|
|
if (g_Config.bFreeLook)
|
|
|
|
{
|
|
|
|
switch (event.xbutton.button)
|
|
|
|
{
|
|
|
|
case 2: // Middle button
|
|
|
|
mouseMoveEnabled = false;
|
|
|
|
break;
|
|
|
|
case 3: // Right button
|
|
|
|
mouseLookEnabled = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MotionNotify:
|
|
|
|
if (g_Config.bFreeLook)
|
|
|
|
{
|
|
|
|
if (mouseLookEnabled)
|
|
|
|
{
|
|
|
|
VertexShaderManager::RotateView((event.xmotion.x - lastMouse[0]) / 200.0f,
|
|
|
|
(event.xmotion.y - lastMouse[1]) / 200.0f);
|
|
|
|
lastMouse[0] = event.xmotion.x;
|
|
|
|
lastMouse[1] = event.xmotion.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mouseMoveEnabled)
|
|
|
|
{
|
|
|
|
VertexShaderManager::TranslateView((event.xmotion.x - lastMouse[0]) / 50.0f,
|
|
|
|
(event.xmotion.y - lastMouse[1]) / 50.0f);
|
|
|
|
lastMouse[0] = event.xmotion.x;
|
|
|
|
lastMouse[1] = event.xmotion.y;
|
|
|
|
}
|
|
|
|
}
|
2010-03-15 23:25:11 +00:00
|
|
|
break;
|
|
|
|
case ConfigureNotify:
|
|
|
|
Window winDummy;
|
2010-04-12 01:33:10 +00:00
|
|
|
unsigned int borderDummy, depthDummy;
|
2010-09-19 23:40:03 +00:00
|
|
|
XGetGeometry(GLWin.evdpy, GLWin.win, &winDummy, &GLWin.x, &GLWin.y,
|
2010-04-12 01:33:10 +00:00
|
|
|
&GLWin.width, &GLWin.height, &borderDummy, &depthDummy);
|
2010-03-15 23:25:11 +00:00
|
|
|
s_backbuffer_width = GLWin.width;
|
|
|
|
s_backbuffer_height = GLWin.height;
|
|
|
|
break;
|
|
|
|
case ClientMessage:
|
2010-09-19 23:40:03 +00:00
|
|
|
if ((unsigned long) event.xclient.data.l[0] == XInternAtom(GLWin.evdpy, "WM_DELETE_WINDOW", False))
|
2011-01-31 01:28:32 +00:00
|
|
|
Core::Callback_CoreMessage(WM_USER_STOP);
|
2011-01-25 03:30:12 +00:00
|
|
|
if ((unsigned long) event.xclient.data.l[0] == XInternAtom(GLWin.evdpy, "RESIZE", False))
|
2010-09-19 23:40:03 +00:00
|
|
|
XMoveResizeWindow(GLWin.evdpy, GLWin.win, event.xclient.data.l[1],
|
2011-01-25 03:30:12 +00:00
|
|
|
event.xclient.data.l[2], event.xclient.data.l[3], event.xclient.data.l[4]);
|
2010-03-15 23:25:11 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Common::SleepCurrentThread(20);
|
|
|
|
}
|
2010-02-16 04:59:45 +00:00
|
|
|
}
|
|
|
|
#endif
|
2009-01-04 21:53:41 +00:00
|
|
|
|
|
|
|
// Create rendering window.
|
|
|
|
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
|
2011-01-31 01:28:32 +00:00
|
|
|
bool OpenGL_Create(int _iwidth, int _iheight)
|
2008-12-08 05:25:12 +00:00
|
|
|
{
|
2011-02-02 21:52:43 +00:00
|
|
|
int _tx, _ty, _twidth, _theight;
|
2011-01-31 01:28:32 +00:00
|
|
|
Core::Callback_VideoGetWindowSize(_tx, _ty, _twidth, _theight);
|
2009-01-04 21:53:41 +00:00
|
|
|
|
2008-12-08 05:25:12 +00:00
|
|
|
// Control window size and picture scaling
|
2010-02-20 04:18:19 +00:00
|
|
|
s_backbuffer_width = _twidth;
|
|
|
|
s_backbuffer_height = _theight;
|
2008-12-08 05:25:12 +00:00
|
|
|
|
2009-09-09 20:47:11 +00:00
|
|
|
#if defined(USE_WX) && USE_WX
|
2011-01-31 01:28:32 +00:00
|
|
|
GLWin.panel = (wxPanel *)VideoWindowHandle();
|
2011-01-10 16:18:41 +00:00
|
|
|
GLWin.glCanvas = new wxGLCanvas(GLWin.panel, wxID_ANY, NULL,
|
|
|
|
wxPoint(0, 0), wxSize(_twidth, _theight));
|
|
|
|
GLWin.glCanvas->Show(true);
|
2011-01-29 06:26:03 +00:00
|
|
|
if (GLWin.glCtxt == NULL) // XXX dirty hack
|
|
|
|
GLWin.glCtxt = new wxGLContext(GLWin.glCanvas);
|
2009-01-04 21:53:41 +00:00
|
|
|
|
2010-06-04 04:59:07 +00:00
|
|
|
#elif defined(__APPLE__)
|
2011-01-10 23:48:59 +00:00
|
|
|
NSOpenGLPixelFormatAttribute attr[2] = { NSOpenGLPFADoubleBuffer, 0 };
|
|
|
|
NSOpenGLPixelFormat *fmt = [[NSOpenGLPixelFormat alloc]
|
|
|
|
initWithAttributes: attr];
|
|
|
|
if (fmt == nil) {
|
2011-02-02 21:52:43 +00:00
|
|
|
ERROR_LOG(VIDEO, "failed to create pixel format");
|
2011-01-31 01:28:32 +00:00
|
|
|
return NULL;
|
2011-01-10 23:48:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GLWin.cocoaCtx = [[NSOpenGLContext alloc]
|
|
|
|
initWithFormat: fmt shareContext: nil];
|
|
|
|
[fmt release];
|
|
|
|
if (GLWin.cocoaCtx == nil) {
|
2011-02-02 21:52:43 +00:00
|
|
|
ERROR_LOG(VIDEO, "failed to create context");
|
2011-01-31 01:28:32 +00:00
|
|
|
return NULL;
|
2011-01-10 23:48:59 +00:00
|
|
|
}
|
|
|
|
|
2011-02-02 21:52:43 +00:00
|
|
|
(void)VideoWindowHandle;
|
|
|
|
CGDisplayCapture(CGMainDisplayID());
|
|
|
|
GLWin.cocoaWin = [[NSWindow alloc]
|
|
|
|
initWithContentRect: [[NSScreen mainScreen] frame]
|
|
|
|
styleMask: NSBorderlessWindowMask
|
|
|
|
backing: NSBackingStoreBuffered defer: NO];
|
|
|
|
[GLWin.cocoaWin makeKeyAndOrderFront: nil];
|
|
|
|
[GLWin.cocoaWin setLevel: CGShieldingWindowLevel()];
|
2011-01-10 23:48:59 +00:00
|
|
|
[GLWin.cocoaCtx setView: [GLWin.cocoaWin contentView]];
|
2010-06-04 04:59:07 +00:00
|
|
|
|
2008-12-08 05:25:12 +00:00
|
|
|
#elif defined(_WIN32)
|
2011-01-31 01:28:32 +00:00
|
|
|
VideoWindowHandle() = (void*)EmuWindow::Create((HWND)VideoWindowHandle(), GetModuleHandle(0), _T("Please wait..."));
|
|
|
|
if (VideoWindowHandle() == NULL)
|
2008-12-08 05:25:12 +00:00
|
|
|
{
|
2011-01-31 01:28:32 +00:00
|
|
|
Host_SysMessage("failed to create window");
|
2008-12-08 05:25:12 +00:00
|
|
|
return false;
|
|
|
|
}
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2010-01-20 19:51:13 +00:00
|
|
|
// Show the window
|
|
|
|
EmuWindow::Show();
|
2008-12-08 05:25:12 +00:00
|
|
|
|
2010-02-20 04:18:19 +00:00
|
|
|
PIXELFORMATDESCRIPTOR pfd = // pfd Tells Windows How We Want Things To Be
|
|
|
|
{
|
|
|
|
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
|
|
|
|
1, // Version Number
|
|
|
|
PFD_DRAW_TO_WINDOW | // Format Must Support Window
|
|
|
|
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
|
|
|
|
PFD_DOUBLEBUFFER, // Must Support Double Buffering
|
|
|
|
PFD_TYPE_RGBA, // Request An RGBA Format
|
|
|
|
32, // Select Our Color Depth
|
|
|
|
0, 0, 0, 0, 0, 0, // Color Bits Ignored
|
|
|
|
0, // 8bit Alpha Buffer
|
|
|
|
0, // Shift Bit Ignored
|
|
|
|
0, // No Accumulation Buffer
|
|
|
|
0, 0, 0, 0, // Accumulation Bits Ignored
|
|
|
|
24, // 24Bit Z-Buffer (Depth Buffer)
|
|
|
|
8, // 8bit Stencil Buffer
|
|
|
|
0, // No Auxiliary Buffer
|
|
|
|
PFD_MAIN_PLANE, // Main Drawing Layer
|
|
|
|
0, // Reserved
|
|
|
|
0, 0, 0 // Layer Masks Ignored
|
|
|
|
};
|
2010-01-20 19:51:13 +00:00
|
|
|
|
|
|
|
GLuint PixelFormat; // Holds The Results After Searching For A Match
|
|
|
|
|
2010-02-20 04:18:19 +00:00
|
|
|
if (!(hDC=GetDC(EmuWindow::GetWnd()))) {
|
2009-03-22 11:21:44 +00:00
|
|
|
PanicAlert("(1) Can't create an OpenGL Device context. Fail.");
|
2010-02-20 04:18:19 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!(PixelFormat = ChoosePixelFormat(hDC,&pfd))) {
|
|
|
|
PanicAlert("(2) Can't find a suitable PixelFormat.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!SetPixelFormat(hDC, PixelFormat, &pfd)) {
|
2009-03-22 11:21:44 +00:00
|
|
|
PanicAlert("(3) Can't set the PixelFormat.");
|
2010-02-20 04:18:19 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!(hRC = wglCreateContext(hDC))) {
|
2009-03-22 11:21:44 +00:00
|
|
|
PanicAlert("(4) Can't create an OpenGL rendering context.");
|
2010-02-20 04:18:19 +00:00
|
|
|
return false;
|
|
|
|
}
|
2009-01-04 21:53:41 +00:00
|
|
|
// --------------------------------------
|
2008-12-08 05:25:12 +00:00
|
|
|
|
|
|
|
#elif defined(HAVE_X11) && HAVE_X11
|
2010-02-20 04:18:19 +00:00
|
|
|
int glxMajorVersion, glxMinorVersion;
|
|
|
|
|
|
|
|
// attributes for a single buffered visual in RGBA format with at least
|
|
|
|
// 8 bits per color and a 24 bit depth buffer
|
|
|
|
int attrListSgl[] = {GLX_RGBA, GLX_RED_SIZE, 8,
|
|
|
|
GLX_GREEN_SIZE, 8,
|
|
|
|
GLX_BLUE_SIZE, 8,
|
|
|
|
GLX_DEPTH_SIZE, 24,
|
|
|
|
None};
|
|
|
|
|
|
|
|
// attributes for a double buffered visual in RGBA format with at least
|
|
|
|
// 8 bits per color and a 24 bit depth buffer
|
|
|
|
int attrListDbl[] = {GLX_RGBA, GLX_DOUBLEBUFFER,
|
|
|
|
GLX_RED_SIZE, 8,
|
|
|
|
GLX_GREEN_SIZE, 8,
|
|
|
|
GLX_BLUE_SIZE, 8,
|
|
|
|
GLX_DEPTH_SIZE, 24,
|
2010-06-01 21:05:00 +00:00
|
|
|
GLX_SAMPLE_BUFFERS_ARB, g_Config.iMultisampleMode != MULTISAMPLE_OFF?1:0,
|
|
|
|
GLX_SAMPLES_ARB, g_Config.iMultisampleMode != MULTISAMPLE_OFF?1:0,
|
|
|
|
None };
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2010-05-16 07:37:22 +00:00
|
|
|
int attrListDefault[] = {
|
|
|
|
GLX_RGBA,
|
|
|
|
GLX_RED_SIZE, 1,
|
|
|
|
GLX_GREEN_SIZE, 1,
|
|
|
|
GLX_BLUE_SIZE, 1,
|
|
|
|
GLX_DOUBLEBUFFER,
|
|
|
|
GLX_DEPTH_SIZE, 1,
|
|
|
|
None };
|
2010-02-20 04:18:19 +00:00
|
|
|
|
|
|
|
GLWin.dpy = XOpenDisplay(0);
|
2010-09-19 23:40:03 +00:00
|
|
|
GLWin.evdpy = XOpenDisplay(0);
|
2011-01-31 01:28:32 +00:00
|
|
|
GLWin.parent = (Window)VideoWindowHandle();
|
2010-02-20 04:18:19 +00:00
|
|
|
GLWin.screen = DefaultScreen(GLWin.dpy);
|
2010-04-12 01:33:10 +00:00
|
|
|
if (GLWin.parent == 0)
|
|
|
|
GLWin.parent = RootWindow(GLWin.dpy, GLWin.screen);
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2010-04-12 01:33:10 +00:00
|
|
|
glXQueryVersion(GLWin.dpy, &glxMajorVersion, &glxMinorVersion);
|
|
|
|
NOTICE_LOG(VIDEO, "glX-Version %d.%d", glxMajorVersion, glxMinorVersion);
|
2010-02-20 04:18:19 +00:00
|
|
|
|
2010-07-16 21:56:40 +00:00
|
|
|
// Get an appropriate visual
|
2010-02-20 04:18:19 +00:00
|
|
|
GLWin.vi = glXChooseVisual(GLWin.dpy, GLWin.screen, attrListDbl);
|
2010-05-16 07:37:22 +00:00
|
|
|
if (GLWin.vi == NULL)
|
|
|
|
{
|
2010-02-20 04:18:19 +00:00
|
|
|
GLWin.vi = glXChooseVisual(GLWin.dpy, GLWin.screen, attrListSgl);
|
2010-05-16 07:37:22 +00:00
|
|
|
if (GLWin.vi != NULL)
|
|
|
|
{
|
|
|
|
ERROR_LOG(VIDEO, "Only Singlebuffered Visual!");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GLWin.vi = glXChooseVisual(GLWin.dpy, GLWin.screen, attrListDefault);
|
|
|
|
if (GLWin.vi == NULL)
|
|
|
|
{
|
|
|
|
ERROR_LOG(VIDEO, "Could not choose visual (glXChooseVisual)");
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
}
|
2010-02-20 04:18:19 +00:00
|
|
|
}
|
2010-04-12 01:33:10 +00:00
|
|
|
else
|
2010-02-20 04:18:19 +00:00
|
|
|
NOTICE_LOG(VIDEO, "Got Doublebuffered Visual!");
|
2008-12-08 05:25:12 +00:00
|
|
|
|
2009-03-22 11:21:44 +00:00
|
|
|
// Create a GLX context.
|
2010-02-16 04:59:45 +00:00
|
|
|
GLWin.ctx = glXCreateContext(GLWin.dpy, GLWin.vi, 0, GL_TRUE);
|
2010-04-12 01:33:10 +00:00
|
|
|
if (!GLWin.ctx)
|
2009-03-22 11:21:44 +00:00
|
|
|
{
|
|
|
|
PanicAlert("Couldn't Create GLX context.Quit");
|
|
|
|
exit(0); // TODO: Don't bring down entire Emu
|
|
|
|
}
|
2010-03-15 23:25:11 +00:00
|
|
|
|
2010-04-22 04:28:34 +00:00
|
|
|
GLWin.x = _tx;
|
|
|
|
GLWin.y = _ty;
|
|
|
|
GLWin.width = _twidth;
|
|
|
|
GLWin.height = _theight;
|
|
|
|
|
2010-02-20 04:18:19 +00:00
|
|
|
CreateXWindow();
|
2011-01-31 01:28:32 +00:00
|
|
|
VideoWindowHandle() = (void *)GLWin.win;
|
2008-12-08 05:25:12 +00:00
|
|
|
#endif
|
2011-02-02 04:40:27 +00:00
|
|
|
return true;
|
2008-12-08 05:25:12 +00:00
|
|
|
}
|
|
|
|
|
2010-04-01 23:13:26 +00:00
|
|
|
bool OpenGL_MakeCurrent()
|
|
|
|
{
|
|
|
|
// connect the glx-context to the window
|
|
|
|
#if defined(USE_WX) && USE_WX
|
2011-01-21 02:56:54 +00:00
|
|
|
return GLWin.glCanvas->SetCurrent(*GLWin.glCtxt);
|
2010-06-04 04:59:07 +00:00
|
|
|
#elif defined(__APPLE__)
|
2011-01-10 23:48:59 +00:00
|
|
|
[GLWin.cocoaCtx makeCurrentContext];
|
2010-04-01 23:13:26 +00:00
|
|
|
#elif defined(_WIN32)
|
2010-06-04 20:03:03 +00:00
|
|
|
return wglMakeCurrent(hDC,hRC) ? true : false;
|
2010-04-01 23:13:26 +00:00
|
|
|
#elif defined(HAVE_X11) && HAVE_X11
|
2010-08-08 00:13:05 +00:00
|
|
|
#if defined(HAVE_WX) && (HAVE_WX)
|
2011-01-31 01:28:32 +00:00
|
|
|
Core::Callback_VideoGetWindowSize(GLWin.x, GLWin.y, (int&)GLWin.width, (int&)GLWin.height);
|
2010-04-22 04:28:34 +00:00
|
|
|
XMoveResizeWindow(GLWin.dpy, GLWin.win, GLWin.x, GLWin.y, GLWin.width, GLWin.height);
|
2010-08-08 00:13:05 +00:00
|
|
|
#endif
|
2010-04-01 23:13:26 +00:00
|
|
|
return glXMakeCurrent(GLWin.dpy, GLWin.win, GLWin.ctx);
|
2008-12-08 05:25:12 +00:00
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update window width, size and etc. Called from Render.cpp
|
|
|
|
void OpenGL_Update()
|
|
|
|
{
|
2009-09-09 20:47:11 +00:00
|
|
|
#if defined(USE_WX) && USE_WX
|
2011-01-10 16:18:41 +00:00
|
|
|
int width, height;
|
|
|
|
|
|
|
|
GLWin.panel->GetSize(&width, &height);
|
|
|
|
if (width == s_backbuffer_width && height == s_backbuffer_height)
|
|
|
|
return;
|
|
|
|
|
2011-01-11 05:48:22 +00:00
|
|
|
GLWin.glCanvas->SetFocus();
|
2011-01-10 16:18:41 +00:00
|
|
|
GLWin.glCanvas->SetSize(0, 0, width, height);
|
|
|
|
GLWin.glCtxt->SetCurrent(*GLWin.glCanvas);
|
|
|
|
s_backbuffer_width = width;
|
|
|
|
s_backbuffer_height = height;
|
|
|
|
|
2010-06-04 04:59:07 +00:00
|
|
|
#elif defined(__APPLE__)
|
2011-01-10 23:48:59 +00:00
|
|
|
int width, height;
|
2010-07-16 21:56:40 +00:00
|
|
|
|
2011-01-10 23:48:59 +00:00
|
|
|
width = [[GLWin.cocoaWin contentView] frame].size.width;
|
|
|
|
height = [[GLWin.cocoaWin contentView] frame].size.height;
|
|
|
|
if (width == s_backbuffer_width && height == s_backbuffer_height)
|
|
|
|
return;
|
|
|
|
|
|
|
|
[GLWin.cocoaCtx setView: [GLWin.cocoaWin contentView]];
|
|
|
|
[GLWin.cocoaCtx update];
|
|
|
|
[GLWin.cocoaCtx makeCurrentContext];
|
|
|
|
s_backbuffer_width = width;
|
|
|
|
s_backbuffer_height = height;
|
2010-06-04 04:59:07 +00:00
|
|
|
|
2008-12-08 05:25:12 +00:00
|
|
|
#elif defined(_WIN32)
|
|
|
|
RECT rcWindow;
|
2009-02-19 06:52:01 +00:00
|
|
|
if (!EmuWindow::GetParentWnd())
|
|
|
|
{
|
2009-02-28 16:33:59 +00:00
|
|
|
// We are not rendering to a child window - use client size.
|
|
|
|
GetClientRect(EmuWindow::GetWnd(), &rcWindow);
|
2008-12-08 05:25:12 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-02-28 16:33:59 +00:00
|
|
|
// We are rendering to a child window - use parent size.
|
2008-12-08 05:25:12 +00:00
|
|
|
GetWindowRect(EmuWindow::GetParentWnd(), &rcWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the new window width and height
|
|
|
|
// See below for documentation
|
2010-02-20 04:18:19 +00:00
|
|
|
int width = rcWindow.right - rcWindow.left;
|
|
|
|
int height = rcWindow.bottom - rcWindow.top;
|
2008-12-08 05:25:12 +00:00
|
|
|
|
2009-02-19 06:52:01 +00:00
|
|
|
// If we are rendering to a child window
|
fixed fps limiting when using using virtual xfb, now fps = vps, in fact now real xfb is as fast as no using xfb, i'm thinking now that the correct thing is leave it enabled as default, and even remove the option.
the problem is one strange behavior i found, in opengl when xfb is enable, frame limit causes the frame rate to be limited exact half the correct speed, so if you choose auto and the game uses 30 fps you get 15 fps
so in opengl, you have to limit to the exact double of the game speed, 100 to pal games and 120 to ntsc.
in d3d this not happened every time, it just happen when you change some time consuming setting like changing the ssaa or resizing the window, in that case you have to disable and re enable frame limit to get the correct fps
to all the devs please if you can help me debug this, will give you a lot of thanks as i'm short in time to debug this error and is driving me crazy not to find the source of the problem.
git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5249 8ced0084-cf51-0410-be5f-012b33b47a6e
2010-03-28 23:51:32 +00:00
|
|
|
if (EmuWindow::GetParentWnd() != 0 && (s_backbuffer_width != width || s_backbuffer_height != height) && width >= 4 && height >= 4)
|
|
|
|
{
|
2008-12-25 15:56:36 +00:00
|
|
|
::MoveWindow(EmuWindow::GetWnd(), 0, 0, width, height, FALSE);
|
fixed fps limiting when using using virtual xfb, now fps = vps, in fact now real xfb is as fast as no using xfb, i'm thinking now that the correct thing is leave it enabled as default, and even remove the option.
the problem is one strange behavior i found, in opengl when xfb is enable, frame limit causes the frame rate to be limited exact half the correct speed, so if you choose auto and the game uses 30 fps you get 15 fps
so in opengl, you have to limit to the exact double of the game speed, 100 to pal games and 120 to ntsc.
in d3d this not happened every time, it just happen when you change some time consuming setting like changing the ssaa or resizing the window, in that case you have to disable and re enable frame limit to get the correct fps
to all the devs please if you can help me debug this, will give you a lot of thanks as i'm short in time to debug this error and is driving me crazy not to find the source of the problem.
git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5249 8ced0084-cf51-0410-be5f-012b33b47a6e
2010-03-28 23:51:32 +00:00
|
|
|
s_backbuffer_width = width;
|
|
|
|
s_backbuffer_height = height;
|
|
|
|
}
|
2008-12-08 05:25:12 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Close plugin
|
|
|
|
void OpenGL_Shutdown()
|
|
|
|
{
|
2009-09-09 20:47:11 +00:00
|
|
|
#if defined(USE_WX) && USE_WX
|
2011-01-21 02:56:54 +00:00
|
|
|
GLWin.glCanvas->Hide();
|
2011-01-29 06:26:03 +00:00
|
|
|
// XXX GLWin.glCanvas->Destroy();
|
|
|
|
// XXX delete GLWin.glCtxt;
|
2010-06-04 04:59:07 +00:00
|
|
|
#elif defined(__APPLE__)
|
2011-02-02 21:52:43 +00:00
|
|
|
[GLWin.cocoaWin close];
|
|
|
|
[GLWin.cocoaCtx clearDrawable];
|
|
|
|
[GLWin.cocoaCtx release];
|
2008-12-08 05:25:12 +00:00
|
|
|
#elif defined(_WIN32)
|
2009-03-22 11:21:44 +00:00
|
|
|
if (hRC) // Do We Have A Rendering Context?
|
|
|
|
{
|
|
|
|
if (!wglMakeCurrent(NULL,NULL)) // Are We Able To Release The DC And RC Contexts?
|
|
|
|
{
|
2008-12-08 05:25:12 +00:00
|
|
|
// [F|RES]: if this fails i dont see the message box and
|
|
|
|
// cant get out of the modal state so i disable it.
|
|
|
|
// This function fails only if i render to main window
|
2009-03-22 11:21:44 +00:00
|
|
|
// MessageBox(NULL,"Release Of DC And RC Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
|
|
|
|
}
|
2008-12-08 05:25:12 +00:00
|
|
|
|
2009-03-22 11:21:44 +00:00
|
|
|
if (!wglDeleteContext(hRC)) // Are We Able To Delete The RC?
|
|
|
|
{
|
|
|
|
ERROR_LOG(VIDEO, "Release Rendering Context Failed.");
|
|
|
|
}
|
|
|
|
hRC = NULL; // Set RC To NULL
|
|
|
|
}
|
2008-12-08 05:25:12 +00:00
|
|
|
|
2009-03-22 11:21:44 +00:00
|
|
|
if (hDC && !ReleaseDC(EmuWindow::GetWnd(), hDC)) // Are We Able To Release The DC
|
|
|
|
{
|
|
|
|
ERROR_LOG(VIDEO, "Release Device Context Failed.");
|
|
|
|
hDC = NULL; // Set DC To NULL
|
|
|
|
}
|
2010-04-12 01:33:10 +00:00
|
|
|
EmuWindow::Close();
|
2008-12-08 05:25:12 +00:00
|
|
|
#elif defined(HAVE_X11) && HAVE_X11
|
2010-02-20 04:18:19 +00:00
|
|
|
DestroyXWindow();
|
2010-04-12 01:33:10 +00:00
|
|
|
if (GLWin.ctx && !glXMakeCurrent(GLWin.dpy, None, NULL))
|
|
|
|
NOTICE_LOG(VIDEO, "Could not release drawing context.");
|
2009-03-22 11:21:44 +00:00
|
|
|
if (GLWin.ctx)
|
|
|
|
{
|
|
|
|
glXDestroyContext(GLWin.dpy, GLWin.ctx);
|
|
|
|
XCloseDisplay(GLWin.dpy);
|
2010-09-19 23:40:03 +00:00
|
|
|
XCloseDisplay(GLWin.evdpy);
|
2009-03-22 11:21:44 +00:00
|
|
|
GLWin.ctx = NULL;
|
|
|
|
}
|
2008-12-08 09:58:02 +00:00
|
|
|
#endif
|
2008-12-08 05:25:12 +00:00
|
|
|
}
|
2009-02-21 13:11:49 +00:00
|
|
|
|
2009-03-22 11:21:44 +00:00
|
|
|
GLuint OpenGL_ReportGLError(const char *function, const char *file, int line)
|
|
|
|
{
|
|
|
|
GLint err = glGetError();
|
|
|
|
if (err != GL_NO_ERROR)
|
|
|
|
{
|
|
|
|
ERROR_LOG(VIDEO, "%s:%d: (%s) OpenGL error 0x%x - %s\n", file, line, function, err, gluErrorString(err));
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpenGL_ReportARBProgramError()
|
2009-02-21 13:11:49 +00:00
|
|
|
{
|
|
|
|
const GLubyte* pstr = glGetString(GL_PROGRAM_ERROR_STRING_ARB);
|
|
|
|
if (pstr != NULL && pstr[0] != 0)
|
|
|
|
{
|
|
|
|
GLint loc = 0;
|
|
|
|
glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &loc);
|
2009-02-28 01:26:56 +00:00
|
|
|
ERROR_LOG(VIDEO, "program error at %d: ", loc);
|
2010-12-05 09:04:34 +00:00
|
|
|
ERROR_LOG(VIDEO, "%s", (char*)pstr);
|
|
|
|
ERROR_LOG(VIDEO, "\n");
|
2009-02-21 13:11:49 +00:00
|
|
|
}
|
2009-03-22 11:21:44 +00:00
|
|
|
}
|
2009-02-21 13:11:49 +00:00
|
|
|
|
2009-03-22 11:21:44 +00:00
|
|
|
bool OpenGL_ReportFBOError(const char *function, const char *file, int line)
|
|
|
|
{
|
|
|
|
unsigned int fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
|
|
|
|
if (fbo_status != GL_FRAMEBUFFER_COMPLETE_EXT)
|
2009-02-21 13:11:49 +00:00
|
|
|
{
|
2009-03-22 11:21:44 +00:00
|
|
|
const char *error = "-";
|
|
|
|
switch (fbo_status)
|
|
|
|
{
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: error = "INCOMPLETE_ATTACHMENT_EXT"; break;
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error = "INCOMPLETE_MISSING_ATTACHMENT_EXT"; break;
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error = "INCOMPLETE_DIMENSIONS_EXT"; break;
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error = "INCOMPLETE_FORMATS_EXT"; break;
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error = "INCOMPLETE_DRAW_BUFFER_EXT"; break;
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error = "INCOMPLETE_READ_BUFFER_EXT"; break;
|
|
|
|
case GL_FRAMEBUFFER_UNSUPPORTED_EXT: error = "UNSUPPORTED_EXT"; break;
|
|
|
|
}
|
|
|
|
ERROR_LOG(VIDEO, "%s:%d: (%s) OpenGL FBO error - %s\n", file, line, function, error);
|
|
|
|
return false;
|
2009-02-21 13:11:49 +00:00
|
|
|
}
|
2009-03-22 11:21:44 +00:00
|
|
|
return true;
|
2009-02-21 13:11:49 +00:00
|
|
|
}
|
2009-09-09 19:52:45 +00:00
|
|
|
|