327 lines
9.9 KiB
C++
327 lines
9.9 KiB
C++
#include "global.h"
|
|
#include "LowLevelWindow_Win32.h"
|
|
#include "archutils/Win32/DirectXHelpers.h"
|
|
#include "archutils/Win32/ErrorStrings.h"
|
|
#include "archutils/Win32/GraphicsWindow.h"
|
|
#include "RageUtil.h"
|
|
#include "RageLog.h"
|
|
#include "RageDisplay.h"
|
|
#include "LocalizedString.h"
|
|
|
|
#include <GL/gl.h>
|
|
|
|
static PIXELFORMATDESCRIPTOR g_CurrentPixelFormat;
|
|
static HGLRC g_HGLRC = NULL;
|
|
static HGLRC g_HGLRC_Background = NULL;
|
|
|
|
static void DestroyGraphicsWindowAndOpenGLContext()
|
|
{
|
|
if( g_HGLRC != NULL )
|
|
{
|
|
wglMakeCurrent( NULL, NULL );
|
|
wglDeleteContext( g_HGLRC );
|
|
g_HGLRC = NULL;
|
|
}
|
|
|
|
if( g_HGLRC_Background != NULL )
|
|
{
|
|
wglDeleteContext( g_HGLRC_Background );
|
|
g_HGLRC_Background = NULL;
|
|
}
|
|
|
|
ZERO( g_CurrentPixelFormat );
|
|
|
|
GraphicsWindow::DestroyGraphicsWindow();
|
|
}
|
|
|
|
void *LowLevelWindow_Win32::GetProcAddress( RString s )
|
|
{
|
|
void *pRet = wglGetProcAddress( s );
|
|
if( pRet != NULL )
|
|
return pRet;
|
|
|
|
return ::GetProcAddress( GetModuleHandle(NULL), s );
|
|
}
|
|
|
|
LowLevelWindow_Win32::LowLevelWindow_Win32()
|
|
{
|
|
ASSERT( g_HGLRC == NULL );
|
|
ASSERT( g_HGLRC_Background == NULL );
|
|
|
|
GraphicsWindow::Initialize( false );
|
|
}
|
|
|
|
LowLevelWindow_Win32::~LowLevelWindow_Win32()
|
|
{
|
|
DestroyGraphicsWindowAndOpenGLContext();
|
|
GraphicsWindow::Shutdown();
|
|
}
|
|
|
|
void LowLevelWindow_Win32::GetDisplayResolutions( DisplayResolutions &out ) const
|
|
{
|
|
GraphicsWindow::GetDisplayResolutions( out );
|
|
}
|
|
|
|
float LowLevelWindow_Win32::GetMonitorAspectRatio() const
|
|
{
|
|
return GraphicsWindow::GetMonitorAspectRatio();
|
|
}
|
|
|
|
int ChooseWindowPixelFormat( const VideoModeParams &p, PIXELFORMATDESCRIPTOR *PixelFormat )
|
|
{
|
|
ASSERT( GraphicsWindow::GetHwnd() != NULL );
|
|
ASSERT( GraphicsWindow::GetHDC() != NULL );
|
|
|
|
ZERO( *PixelFormat );
|
|
PixelFormat->nSize = sizeof(PIXELFORMATDESCRIPTOR);
|
|
PixelFormat->nVersion = 1;
|
|
PixelFormat->dwFlags = PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL;
|
|
PixelFormat->iPixelType = PFD_TYPE_RGBA;
|
|
PixelFormat->cColorBits = p.bpp == 16? 16:24;
|
|
PixelFormat->cDepthBits = 16;
|
|
|
|
return ChoosePixelFormat( GraphicsWindow::GetHDC(), PixelFormat );
|
|
}
|
|
|
|
void DumpPixelFormat( const PIXELFORMATDESCRIPTOR &pfd )
|
|
{
|
|
RString str = ssprintf( "Mode: " );
|
|
bool bInvalidFormat = false;
|
|
|
|
if( pfd.dwFlags & PFD_GENERIC_FORMAT )
|
|
{
|
|
if( pfd.dwFlags & PFD_GENERIC_ACCELERATED ) str += "MCD ";
|
|
else { str += "software "; bInvalidFormat = true; }
|
|
}
|
|
else
|
|
{
|
|
str += "ICD ";
|
|
}
|
|
|
|
if( pfd.iPixelType != PFD_TYPE_RGBA ) { str += "indexed "; bInvalidFormat = true; }
|
|
if( !(pfd.dwFlags & PFD_SUPPORT_OPENGL) ) { str += "!OPENGL "; bInvalidFormat = true; }
|
|
if( !(pfd.dwFlags & PFD_DRAW_TO_WINDOW) ) { str += "!window "; bInvalidFormat = true; }
|
|
if( !(pfd.dwFlags & PFD_DOUBLEBUFFER) ) { str += "!dbuff "; bInvalidFormat = true; }
|
|
|
|
str += ssprintf( "%i (%i%i%i) ", pfd.cColorBits, pfd.cRedBits, pfd.cGreenBits, pfd.cBlueBits );
|
|
if( pfd.cAlphaBits ) str += ssprintf( "%i alpha ", pfd.cAlphaBits );
|
|
if( pfd.cDepthBits ) str += ssprintf( "%i depth ", pfd.cDepthBits );
|
|
if( pfd.cStencilBits ) str += ssprintf( "%i stencil ", pfd.cStencilBits );
|
|
if( pfd.cAccumBits ) str += ssprintf( "%i accum ", pfd.cAccumBits );
|
|
|
|
if( bInvalidFormat )
|
|
LOG->Warn( "Invalid format: %s", str.c_str() );
|
|
else
|
|
LOG->Info( "%s", str.c_str() );
|
|
}
|
|
|
|
/* This function does not reset the video mode if it fails, because we might be trying
|
|
* yet another video mode, so we'd just thrash the display. On fatal error,
|
|
* LowLevelWindow_Win32::~LowLevelWindow_Win32 will call GraphicsWindow::Shutdown(). */
|
|
RString LowLevelWindow_Win32::TryVideoMode( const VideoModeParams &p, bool &bNewDeviceOut )
|
|
{
|
|
//LOG->Warn( "LowLevelWindow_Win32::TryVideoMode" );
|
|
|
|
ASSERT_M( p.bpp == 16 || p.bpp == 32, ssprintf("%i", p.bpp) );
|
|
|
|
bNewDeviceOut = false;
|
|
|
|
/* We're only allowed to change the pixel format of a window exactly once. */
|
|
bool bCanSetPixelFormat = true;
|
|
|
|
/* Do we have an old window? */
|
|
if( GraphicsWindow::GetHwnd() == NULL )
|
|
{
|
|
/* No. Always create and show the window before changing the video mode.
|
|
* Otherwise, some other window may have focus, and changing the video mode will
|
|
* cause that window to be resized. */
|
|
bNewDeviceOut = true;
|
|
GraphicsWindow::CreateGraphicsWindow( p );
|
|
} else {
|
|
/* We already have a window. Assume that it's pixel format has already been
|
|
* set. */
|
|
bCanSetPixelFormat = false;
|
|
}
|
|
|
|
ASSERT( GraphicsWindow::GetHwnd() );
|
|
|
|
/* Set the display mode: switch to a fullscreen mode or revert to windowed mode. */
|
|
LOG->Trace("SetScreenMode ...");
|
|
RString sErr = GraphicsWindow::SetScreenMode( p );
|
|
if( !sErr.empty() )
|
|
return sErr;
|
|
|
|
PIXELFORMATDESCRIPTOR PixelFormat;
|
|
int iPixelFormat = ChooseWindowPixelFormat( p, &PixelFormat );
|
|
if( iPixelFormat == 0 )
|
|
{
|
|
/* Destroy the window. */
|
|
DestroyGraphicsWindowAndOpenGLContext();
|
|
return "Pixel format not found";
|
|
}
|
|
|
|
bool bNeedToSetPixelFormat = false;
|
|
{
|
|
/* We'll need to recreate it if the pixel format is going to change. We
|
|
* aren't allowed to change the pixel format twice. */
|
|
PIXELFORMATDESCRIPTOR DestPixelFormat;
|
|
ZERO( DestPixelFormat );
|
|
DescribePixelFormat( GraphicsWindow::GetHDC(), iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &DestPixelFormat );
|
|
if( memcmp( &DestPixelFormat, &g_CurrentPixelFormat, sizeof(PIXELFORMATDESCRIPTOR) ) )
|
|
{
|
|
LOG->Trace("Reset: pixel format changing" );
|
|
bNeedToSetPixelFormat = true;
|
|
}
|
|
}
|
|
|
|
if( bNeedToSetPixelFormat && !bCanSetPixelFormat )
|
|
{
|
|
/*
|
|
* The screen mode has changed, so we need to set the pixel format. If we're
|
|
* not allowed to do so, destroy the window and make a new one.
|
|
*
|
|
* For some reason, if we destroy the old window before creating the new one,
|
|
* the "maximized apps go under the taskbar" glitch will happen when we quit.
|
|
* We have to create the new window first.
|
|
*/
|
|
LOG->Trace( "Mode requires new pixel format, and we've already set one; resetting OpenGL context" );
|
|
if( g_HGLRC != NULL )
|
|
{
|
|
wglMakeCurrent( NULL, NULL );
|
|
wglDeleteContext( g_HGLRC );
|
|
g_HGLRC = NULL;
|
|
wglDeleteContext( g_HGLRC_Background );
|
|
g_HGLRC_Background = NULL;
|
|
}
|
|
|
|
bNewDeviceOut = true;
|
|
}
|
|
|
|
/* If we deleted the OpenGL context above, also recreate the window. Otherwise, just
|
|
* reconfigure it. */
|
|
GraphicsWindow::CreateGraphicsWindow( p, bNewDeviceOut );
|
|
|
|
if( bNeedToSetPixelFormat )
|
|
{
|
|
/* Set the pixel format. */
|
|
if( !SetPixelFormat(GraphicsWindow::GetHDC(), iPixelFormat, &PixelFormat) )
|
|
{
|
|
/* Destroy the window. */
|
|
DestroyGraphicsWindowAndOpenGLContext();
|
|
|
|
return werr_ssprintf( GetLastError(), "Pixel format failed" );
|
|
}
|
|
|
|
DescribePixelFormat( GraphicsWindow::GetHDC(), iPixelFormat, sizeof(g_CurrentPixelFormat), &g_CurrentPixelFormat );
|
|
|
|
DumpPixelFormat( g_CurrentPixelFormat );
|
|
}
|
|
|
|
if( g_HGLRC == NULL )
|
|
{
|
|
g_HGLRC = wglCreateContext( GraphicsWindow::GetHDC() );
|
|
if ( g_HGLRC == NULL )
|
|
{
|
|
DestroyGraphicsWindowAndOpenGLContext();
|
|
return hr_ssprintf( GetLastError(), "wglCreateContext" );
|
|
}
|
|
|
|
g_HGLRC_Background = wglCreateContext( GraphicsWindow::GetHDC() );
|
|
if( g_HGLRC_Background == NULL )
|
|
{
|
|
DestroyGraphicsWindowAndOpenGLContext();
|
|
return hr_ssprintf( GetLastError(), "wglCreateContext" );
|
|
}
|
|
|
|
if( !wglShareLists(g_HGLRC, g_HGLRC_Background) )
|
|
{
|
|
LOG->Warn( werr_ssprintf(GetLastError(), "wglShareLists failed") );
|
|
wglDeleteContext( g_HGLRC_Background );
|
|
g_HGLRC_Background = NULL;
|
|
}
|
|
|
|
if( !wglMakeCurrent( GraphicsWindow::GetHDC(), g_HGLRC ) )
|
|
{
|
|
DestroyGraphicsWindowAndOpenGLContext();
|
|
return hr_ssprintf( GetLastError(), "wglCreateContext" );
|
|
}
|
|
}
|
|
return RString(); // we set the video mode successfully
|
|
}
|
|
|
|
bool LowLevelWindow_Win32::SupportsThreadedRendering()
|
|
{
|
|
return g_HGLRC_Background != NULL;
|
|
}
|
|
|
|
void LowLevelWindow_Win32::BeginConcurrentRendering()
|
|
{
|
|
if( !wglMakeCurrent( GraphicsWindow::GetHDC(), g_HGLRC_Background ) )
|
|
{
|
|
LOG->Warn( hr_ssprintf(GetLastError(), "wglMakeCurrent") );
|
|
FAIL_M( hr_ssprintf(GetLastError(), "wglMakeCurrent") );
|
|
}
|
|
}
|
|
|
|
void LowLevelWindow_Win32::EndConcurrentRendering()
|
|
{
|
|
wglMakeCurrent( NULL, NULL );
|
|
}
|
|
|
|
static LocalizedString OPENGL_NOT_AVAILABLE( "LowLevelWindow_Win32", "OpenGL hardware acceleration is not available." );
|
|
bool LowLevelWindow_Win32::IsSoftwareRenderer( RString &sError )
|
|
{
|
|
RString sVendor = (const char*)glGetString(GL_VENDOR);
|
|
RString sRenderer = (const char*)glGetString(GL_RENDERER);
|
|
|
|
LOG->Trace( "LowLevelWindow_Win32::IsSoftwareRenderer '%s', '%s'", sVendor.c_str(), sRenderer.c_str() );
|
|
|
|
if( sVendor == "Microsoft Corporation" && sRenderer == "GDI Generic" )
|
|
{
|
|
sError = OPENGL_NOT_AVAILABLE;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void LowLevelWindow_Win32::SwapBuffers()
|
|
{
|
|
::SwapBuffers( GraphicsWindow::GetHDC() );
|
|
}
|
|
|
|
void LowLevelWindow_Win32::Update()
|
|
{
|
|
GraphicsWindow::Update();
|
|
}
|
|
|
|
const VideoModeParams &LowLevelWindow_Win32::GetActualVideoModeParams() const
|
|
{
|
|
return GraphicsWindow::GetParams();
|
|
}
|
|
|
|
/*
|
|
* (c) 2004 Glenn Maynard
|
|
* All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, and/or sell copies of the Software, and to permit persons to
|
|
* whom the Software is furnished to do so, provided that the above
|
|
* copyright notice(s) and this permission notice appear in all copies of
|
|
* the Software and that both the above copyright notice(s) and this
|
|
* permission notice appear in supporting documentation.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|