2017-02-27 21:57:46 +01:00
# include <mutex>
2013-03-29 18:50:08 +01:00
# include "base/timeutil.h"
# include "base/NativeApp.h"
2014-09-26 21:16:56 -07:00
# include "i18n/i18n.h"
2016-01-01 12:50:38 +01:00
# include "input/input_state.h"
2014-06-29 23:29:49 +02:00
# include "util/text/utf8.h"
2016-01-01 12:50:38 +01:00
2013-08-13 08:09:36 -07:00
# include "Common/Log.h"
# include "Common/StringUtils.h"
2012-11-01 16:19:01 +01:00
# include "../Globals.h"
2013-08-13 08:09:36 -07:00
# include "Windows/EmuThread.h"
2014-09-26 21:16:56 -07:00
# include "Windows/W32Util/Misc.h"
2015-09-19 13:14:05 +02:00
# include "Windows/MainWindow.h"
2013-08-13 08:09:36 -07:00
# include "Windows/resource.h"
# include "Core/Reporting.h"
# include "Core/MemMap.h"
# include "Core/Core.h"
# include "Core/Host.h"
# include "Core/System.h"
# include "Core/Config.h"
2013-04-13 21:24:07 +02:00
# include "thread/threadutil.h"
2012-11-01 16:19:01 +01:00
2012-11-17 22:44:29 +00:00
# include <tchar.h>
# include <process.h>
2013-06-08 08:32:07 +08:00
# include <intrin.h>
2017-02-27 11:32:40 +01:00
2013-06-08 08:32:07 +08:00
# pragma intrinsic(_InterlockedExchange)
2012-11-17 22:44:29 +00:00
2017-02-27 21:57:46 +01:00
static std : : mutex emuThreadLock ;
2012-11-01 16:19:01 +01:00
static HANDLE emuThread ;
2013-08-13 08:09:36 -07:00
static volatile long emuThreadReady ;
2013-06-08 08:32:07 +08:00
2014-08-31 03:05:59 -04:00
extern std : : vector < std : : wstring > GetWideCmdLine ( ) ;
2013-08-13 08:09:36 -07:00
enum EmuThreadStatus : long
2013-06-08 08:32:07 +08:00
{
THREAD_NONE = 0 ,
THREAD_INIT ,
THREAD_CORE_LOOP ,
THREAD_SHUTDOWN ,
THREAD_END ,
} ;
2012-11-01 16:19:01 +01:00
HANDLE EmuThread_GetThreadHandle ( )
{
2017-02-27 21:57:46 +01:00
std : : lock_guard < std : : mutex > guard ( emuThreadLock ) ;
2012-11-01 16:19:01 +01:00
return emuThread ;
}
2013-06-08 08:32:07 +08:00
unsigned int WINAPI TheThread ( void * ) ;
2012-11-01 16:19:01 +01:00
2013-03-29 18:50:08 +01:00
void EmuThread_Start ( )
2012-11-01 16:19:01 +01:00
{
2017-02-27 21:57:46 +01:00
std : : lock_guard < std : : mutex > guard ( emuThreadLock ) ;
2013-06-08 08:32:07 +08:00
emuThread = ( HANDLE ) _beginthreadex ( 0 , 0 , & TheThread , 0 , 0 , 0 ) ;
2012-11-01 16:19:01 +01:00
}
void EmuThread_Stop ( )
{
2013-08-13 08:03:13 -07:00
// Already stopped?
2013-08-13 08:09:36 -07:00
{
2017-02-27 21:57:46 +01:00
std : : lock_guard < std : : mutex > guard ( emuThreadLock ) ;
2014-02-09 17:35:43 -08:00
if ( emuThread = = NULL | | emuThreadReady = = THREAD_END )
2013-08-13 08:09:36 -07:00
return ;
}
2013-08-13 08:03:13 -07:00
2014-06-22 09:38:46 +02:00
UpdateUIState ( UISTATE_EXIT ) ;
2012-11-01 16:19:01 +01:00
Core_Stop ( ) ;
2013-06-08 08:32:07 +08:00
Core_WaitInactive ( 800 ) ;
if ( WAIT_TIMEOUT = = WaitForSingleObject ( emuThread , 800 ) )
{
2013-06-30 22:50:58 -07:00
_dbg_assert_msg_ ( COMMON , false , " Wait for EmuThread timed out. " ) ;
2013-06-08 08:32:07 +08:00
}
2012-11-01 16:19:01 +01:00
{
2017-02-27 21:57:46 +01:00
std : : lock_guard < std : : mutex > guard ( emuThreadLock ) ;
2013-06-08 08:32:07 +08:00
CloseHandle ( emuThread ) ;
emuThread = 0 ;
2012-11-01 16:19:01 +01:00
}
host - > UpdateUI ( ) ;
}
2013-06-08 08:32:07 +08:00
bool EmuThread_Ready ( )
{
return emuThreadReady = = THREAD_CORE_LOOP ;
}
unsigned int WINAPI TheThread ( void * )
{
_InterlockedExchange ( & emuThreadReady , THREAD_INIT ) ;
2014-06-29 12:53:03 +02:00
setCurrentThreadName ( " Emu " ) ; // And graphics...
2012-11-01 16:19:01 +01:00
2013-03-29 18:50:08 +01:00
// Native overwrites host. Can't allow that.
Host * oldHost = host ;
2014-08-31 01:17:25 -04:00
// Convert the command-line arguments to Unicode, then to proper UTF-8
// (the benefit being that we don't have to pollute the UI project with win32 ifdefs and lots of Convert<whatever>To<whatever>).
// This avoids issues with PPSSPP inadvertently destroying paths with Unicode glyphs
// (using the ANSI args resulted in Japanese/Chinese glyphs being turned into question marks, at least for me..).
// -TheDax
std : : vector < std : : wstring > wideArgs = GetWideCmdLine ( ) ;
std : : vector < std : : string > argsUTF8 ;
2014-08-31 04:16:22 -04:00
for ( auto & string : wideArgs ) {
2014-08-31 03:05:59 -04:00
argsUTF8 . push_back ( ConvertWStringToUTF8 ( string ) ) ;
2014-08-31 01:17:25 -04:00
}
std : : vector < const char * > args ;
2014-08-31 04:16:22 -04:00
for ( auto & string : argsUTF8 ) {
2014-08-31 01:17:25 -04:00
args . push_back ( string . c_str ( ) ) ;
}
2017-04-15 16:33:30 -07:00
bool performingRestart = NativeIsRestarting ( ) ;
2016-01-17 22:11:28 +01:00
NativeInit ( static_cast < int > ( args . size ( ) ) , & args [ 0 ] , " 1234 " , " 1234 " , nullptr ) ;
2014-08-31 01:17:25 -04:00
2013-03-29 18:50:08 +01:00
Host * nativeHost = host ;
host = oldHost ;
2012-11-01 16:19:01 +01:00
host - > UpdateUI ( ) ;
2014-01-27 07:36:39 +08:00
2015-12-31 16:59:40 +01:00
GraphicsContext * graphicsContext ;
2013-03-10 23:08:57 +01:00
std : : string error_string ;
2015-12-31 16:59:40 +01:00
if ( ! host - > InitGraphics ( & error_string , & graphicsContext ) ) {
2017-04-15 16:33:30 -07:00
// Before anything: are we restarting right now?
if ( performingRestart ) {
// Okay, switching graphics didn't work out. Probably a driver bug - fallback to restart.
// This happens on NVIDIA when switching OpenGL -> Vulkan.
g_Config . Save ( ) ;
W32Util : : ExitAndRestart ( ) ;
}
2014-09-26 21:16:56 -07:00
I18NCategory * err = GetI18NCategory ( " Error " ) ;
2014-08-17 16:07:14 +02:00
Reporting : : ReportMessage ( " Graphics init error: %s " , error_string . c_str ( ) ) ;
2014-09-26 21:16:56 -07:00
2016-03-13 09:33:39 -07:00
const char * defaultErrorVulkan = " Failed initializing graphics. Try upgrading your graphics drivers. \n \n Would you like to try switching to OpenGL? \n \n Error message: " ;
2014-09-26 21:16:56 -07:00
const char * defaultErrorOpenGL = " Failed initializing graphics. Try upgrading your graphics drivers. \n \n Would you like to try switching to DirectX 9? \n \n Error message: " ;
2014-09-28 15:55:16 +08:00
const char * defaultErrorDirect3D9 = " Failed initializing graphics. Try upgrading your graphics drivers and directx 9 runtime. \n \n Would you like to try switching to OpenGL? \n \n Error message: " ;
2014-09-26 21:16:56 -07:00
const char * genericError ;
int nextBackend = GPU_BACKEND_DIRECT3D9 ;
switch ( g_Config . iGPUBackend ) {
case GPU_BACKEND_DIRECT3D9 :
nextBackend = GPU_BACKEND_OPENGL ;
genericError = err - > T ( " GenericDirect3D9Error " , defaultErrorDirect3D9 ) ;
break ;
2016-03-13 09:33:39 -07:00
case GPU_BACKEND_VULKAN :
nextBackend = GPU_BACKEND_OPENGL ;
2016-03-24 17:03:04 -07:00
genericError = err - > T ( " GenericVulkanError " , defaultErrorVulkan ) ;
2016-03-13 09:33:39 -07:00
break ;
2014-09-26 21:16:56 -07:00
case GPU_BACKEND_OPENGL :
default :
nextBackend = GPU_BACKEND_DIRECT3D9 ;
genericError = err - > T ( " GenericOpenGLError " , defaultErrorOpenGL ) ;
break ;
}
std : : string full_error = StringFromFormat ( " %s \n \n %s " , genericError , error_string . c_str ( ) ) ;
std : : wstring title = ConvertUTF8ToWString ( err - > T ( " GenericGraphicsError " , " Graphics Error " ) ) ;
bool yes = IDYES = = MessageBox ( 0 , ConvertUTF8ToWString ( full_error ) . c_str ( ) , title . c_str ( ) , MB_ICONERROR | MB_YESNO ) ;
2013-03-10 23:08:57 +01:00
ERROR_LOG ( BOOT , full_error . c_str ( ) ) ;
2013-09-11 21:35:04 +02:00
2014-09-26 21:16:56 -07:00
if ( yes ) {
// Change the config to the alternative and restart.
g_Config . iGPUBackend = nextBackend ;
g_Config . Save ( ) ;
W32Util : : ExitAndRestart ( ) ;
}
// No safe way out without graphics.
2013-09-11 21:35:04 +02:00
ExitProcess ( 1 ) ;
2013-03-10 23:08:57 +01:00
}
2012-11-01 16:19:01 +01:00
2015-12-31 16:59:40 +01:00
NativeInitGraphics ( graphicsContext ) ;
2014-02-11 07:45:13 -08:00
NativeResized ( ) ;
2012-11-01 16:19:01 +01:00
INFO_LOG ( BOOT , " Done. " ) ;
_dbg_update_ ( ) ;
2013-06-08 08:32:07 +08:00
if ( coreState = = CORE_POWERDOWN ) {
INFO_LOG ( BOOT , " Exit before core loop. " ) ;
goto shutdown ;
}
_InterlockedExchange ( & emuThreadReady , THREAD_CORE_LOOP ) ;
if ( g_Config . bBrowse )
PostMessage ( MainWindow : : GetHWND ( ) , WM_COMMAND , ID_FILE_LOAD , 0 ) ;
2013-03-29 18:50:08 +01:00
Core_EnableStepping ( FALSE ) ;
2013-06-08 08:32:07 +08:00
2014-06-22 09:38:46 +02:00
while ( GetUIState ( ) ! = UISTATE_EXIT )
2013-06-18 00:31:15 -07:00
{
// We're here again, so the game quit. Restart Core_Run() which controls the UI.
// This way they can load a new game.
2013-10-12 01:40:33 -07:00
if ( ! Core_IsActive ( ) )
UpdateUIState ( UISTATE_MENU ) ;
2017-03-14 22:01:18 -07:00
Core_Run ( graphicsContext ) ;
2013-06-18 00:31:15 -07:00
}
2012-11-01 16:19:01 +01:00
2012-11-05 13:42:33 +01:00
shutdown :
2013-06-08 08:32:07 +08:00
_InterlockedExchange ( & emuThreadReady , THREAD_SHUTDOWN ) ;
2013-08-16 00:57:04 +02:00
2013-03-29 18:50:08 +01:00
NativeShutdownGraphics ( ) ;
2013-09-11 21:35:04 +02:00
2013-08-18 23:25:14 -07:00
host - > ShutdownSound ( ) ;
2013-06-20 23:13:00 -07:00
host = nativeHost ;
2013-03-29 18:50:08 +01:00
NativeShutdown ( ) ;
host = oldHost ;
2014-09-20 21:55:58 +02:00
host - > ShutdownGraphics ( ) ;
2012-11-01 16:19:01 +01:00
2013-06-08 08:32:07 +08:00
_InterlockedExchange ( & emuThreadReady , THREAD_END ) ;
2012-11-01 16:19:01 +01:00
return 0 ;
}