llappviewerwin32.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857
  1. /**
  2. * @file llappviewerwin32.cpp
  3. * @brief The LLAppViewerWin32 class definitions
  4. *
  5. * $LicenseInfo:firstyear=2007&license=viewergpl$
  6. *
  7. * Copyright (c) 2007-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "llviewerprecompiledheaders.h"
  33. // Unreferenced formal parameter:
  34. #pragma warning(disable: 4100)
  35. // Non-standard extension used (zero-sized array in struct/union):
  36. #pragma warning(disable: 4200)
  37. // printf format string
  38. #pragma warning(disable: 4477)
  39. #include <fcntl.h> // For _O_APPEND
  40. #include <io.h> // For _open_osfhandle()
  41. #include <werapi.h> // For WerAddExcludedApplication()
  42. #include <process.h> // For _spawnl()
  43. #include <tchar.h> // For TCHAR support
  44. #include <tlhelp32.h>
  45. #include <dbghelp.h>
  46. #include <malloc.h>
  47. #include "res/resource.h" // *FIX: for setting gIconResource.
  48. #include "llappviewerwin32.h"
  49. #include "llapp.h"
  50. #include "llcommandlineparser.h"
  51. #include "lldir.h"
  52. #include "lldxhardware.h"
  53. #include "llfindlocale.h"
  54. #include "llmd5.h"
  55. #include "llsdserialize.h"
  56. #include "llsys.h"
  57. #include "llwindowwin32.h"
  58. #include "llgridmanager.h"
  59. #include "llviewercontrol.h"
  60. #include "llweb.h"
  61. ///////////////////////////////////////////////////////////////////////////////
  62. // LLWinDebug class (used to be held in llwindebug.h and llwindebug.cpp)
  63. ///////////////////////////////////////////////////////////////////////////////
  64. static const std::string sDumpFilename = "CoolVLViewer.dmp";
  65. class LLWinDebug
  66. {
  67. protected:
  68. LOG_CLASS(LLWinDebug);
  69. public:
  70. // Initializes the LLWinDebug exception filter callback. Hands a windows
  71. // unhandled exception filter to LLWinDebug. This method should only be
  72. // called to change the exception filter used by LLWinDebug. Setting
  73. // filter_func to NULL will clear any custom filters.
  74. static void initExceptionHandler(LPTOP_LEVEL_EXCEPTION_FILTER filter_func);
  75. // Checks the status of the exception filter. Resets unhandled exception
  76. // filter to the filter specified with initExceptionFilter. Returns false
  77. // if the exception filter was modified.
  78. static bool checkExceptionHandler();
  79. static void generateCrashStacks(struct _EXCEPTION_POINTERS* ex = NULL);
  80. static void clearCrashStacks(); // Deletes the crash stack file(s).
  81. };
  82. // Based on dbghelp.h
  83. typedef BOOL (WINAPI* MINIDUMPWRITEDUMP)(HANDLE, DWORD, HANDLE, MINIDUMP_TYPE,
  84. CONST PMINIDUMP_EXCEPTION_INFORMATION,
  85. CONST PMINIDUMP_USER_STREAM_INFORMATION,
  86. CONST PMINIDUMP_CALLBACK_INFORMATION);
  87. static MINIDUMPWRITEDUMP sMiniDumpProcAdrp = NULL;
  88. static LPTOP_LEVEL_EXCEPTION_FILTER sFilterFunc = NULL;
  89. class LLMemoryReserve
  90. {
  91. public:
  92. LLMemoryReserve()
  93. : mReserved(NULL)
  94. {
  95. }
  96. ~LLMemoryReserve()
  97. {
  98. release();
  99. }
  100. void reserve()
  101. {
  102. if (!mReserved)
  103. {
  104. constexpr size_t MEMORY_RESERVATION_SIZE = 8 * 1024 * 1024;
  105. mReserved = malloc(MEMORY_RESERVATION_SIZE);
  106. }
  107. }
  108. void release()
  109. {
  110. if (mReserved)
  111. {
  112. free(mReserved);
  113. mReserved = NULL;
  114. }
  115. }
  116. private:
  117. void* mReserved;
  118. };
  119. static LLMemoryReserve sEmergencyMemoryReserve;
  120. //static
  121. void LLWinDebug::initExceptionHandler(LPTOP_LEVEL_EXCEPTION_FILTER filter_func)
  122. {
  123. static bool s_first_run = true;
  124. // Load the dbghelp dll now, instead of waiting for the crash. Less
  125. // potential for stack mangling.
  126. if (s_first_run)
  127. {
  128. s_first_run = false;
  129. // First, try loading from the directory that the app resides in.
  130. std::string local_dll_name =
  131. gDirUtil.findFile("dbghelp.dll", gDirUtil.getWorkingDir(),
  132. gDirUtil.getExecutableDir());
  133. HMODULE dll_handle = LoadLibraryA(local_dll_name.c_str());
  134. if (!dll_handle)
  135. {
  136. dll_handle = LoadLibrary(L"dbghelp.dll");
  137. }
  138. if (!dll_handle)
  139. {
  140. llwarns << "Could not find dbghelp.dll !" << llendl;
  141. }
  142. else
  143. {
  144. sMiniDumpProcAdrp =
  145. (MINIDUMPWRITEDUMP)GetProcAddress(dll_handle,
  146. "MiniDumpWriteDump");
  147. if (!sMiniDumpProcAdrp)
  148. {
  149. llwarns << "Could not find the MiniDumpWriteDump() function !"
  150. << llendl;
  151. FreeLibrary(dll_handle);
  152. dll_handle = NULL;
  153. }
  154. }
  155. sEmergencyMemoryReserve.reserve();
  156. }
  157. LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
  158. prev_filter = SetUnhandledExceptionFilter(filter_func);
  159. if (prev_filter != sFilterFunc)
  160. {
  161. llwarns << "Replacing unknown exception (" << (void*)prev_filter
  162. << ") with (" << (void *)filter_func << ") !" << llendl;
  163. }
  164. sFilterFunc = filter_func;
  165. }
  166. bool LLWinDebug::checkExceptionHandler()
  167. {
  168. bool ok = true;
  169. LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
  170. prev_filter = SetUnhandledExceptionFilter(sFilterFunc);
  171. if (prev_filter != sFilterFunc)
  172. {
  173. llwarns << "Our exception handler (" << (void*)sFilterFunc
  174. << ") replaced with " << (void*)prev_filter << "!" << llendl;
  175. ok = false;
  176. }
  177. if (!prev_filter)
  178. {
  179. ok = false;
  180. if (sFilterFunc)
  181. {
  182. llwarns << "Our exception handler (" << (void*)sFilterFunc
  183. << ") replaced with NULL!" << llendl;
  184. }
  185. else
  186. {
  187. llwarns << "Exception handler uninitialized." << llendl;
  188. }
  189. }
  190. return ok;
  191. }
  192. // *NOTE: Mani - This method is no longer the exception handler. It is called
  193. // from viewer_windows_exception_handler() and other places.
  194. //static
  195. void LLWinDebug::generateCrashStacks(struct _EXCEPTION_POINTERS* ex_infop)
  196. {
  197. // Since there is exception info... Release the hounds.
  198. sEmergencyMemoryReserve.release();
  199. if (!ex_infop || !sMiniDumpProcAdrp)
  200. {
  201. return;
  202. }
  203. _MINIDUMP_EXCEPTION_INFORMATION info;
  204. info.ThreadId = ::GetCurrentThreadId();
  205. info.ExceptionPointers = ex_infop;
  206. info.ClientPointers = NULL;
  207. std::string dump_path = gDirUtil.getFullPath(LL_PATH_LOGS, sDumpFilename);
  208. HANDLE fhandle = CreateFileA(dump_path.c_str(), GENERIC_WRITE,
  209. FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
  210. FILE_ATTRIBUTE_NORMAL, NULL);
  211. if (fhandle != INVALID_HANDLE_VALUE)
  212. {
  213. // Write the dump, ignoring the return value
  214. sMiniDumpProcAdrp(GetCurrentProcess(), GetCurrentProcessId(),
  215. fhandle, MiniDumpNormal, &info, NULL, NULL);
  216. CloseHandle(fhandle);
  217. }
  218. }
  219. void LLWinDebug::clearCrashStacks()
  220. {
  221. LLFile::remove(gDirUtil.getFullPath(LL_PATH_LOGS, sDumpFilename));
  222. }
  223. ///////////////////////////////////////////////////////////////////////////////
  224. // LLAppViewerWin32 stuff proper
  225. ///////////////////////////////////////////////////////////////////////////////
  226. static bool sErrorReportingDisabled = false;
  227. LONG WINAPI viewer_windows_exception_handler(struct _EXCEPTION_POINTERS* ex_infop)
  228. {
  229. // Guard against possible reentry of the error handler... HB
  230. static bool reentry = false;
  231. if (reentry)
  232. {
  233. _tprintf(_T("Got another fatal signal while in the exception handler, die now !\n"));
  234. llwarns << "Got another fatal signal while in the exception handler, die now !"
  235. << llendl;
  236. return EXCEPTION_EXECUTE_HANDLER;
  237. }
  238. reentry = true;
  239. // *NOTE:Mani - this code is stolen from LLApp, where it is never actually
  240. // used. Translate the signals/exceptions into cross-platform stuff Windows
  241. // implementation
  242. _tprintf(_T("Entering Windows exception handler...\n"));
  243. llinfos << "Entering Windows exception handler..." << llendl;
  244. // Generate a minidump if we can.
  245. LLWinDebug::generateCrashStacks(ex_infop);
  246. if (!LLApp::isError())
  247. {
  248. // Flag status to error
  249. LLApp::setError();
  250. // Block in the exception handler until the app has stopped; this is
  251. // pretty sketchy, but appears to work just fine.
  252. while (!LLApp::isStopped())
  253. {
  254. ms_sleep(10);
  255. }
  256. }
  257. // At this point, we always want to exit the app. There is no graceful
  258. // recovery for an unhandled exception. Just kill the process.
  259. return EXCEPTION_EXECUTE_HANDLER;
  260. }
  261. // MSVC 2022 clang does not like the appearance following which hMutex is "not
  262. // used" (it is, but only to verify its creation does not trigger an error)...
  263. // So, let's silence that warning. HB
  264. #if LL_CLANG
  265. # pragma clang diagnostic push
  266. # pragma clang diagnostic ignored "-Wunused-variable"
  267. #endif
  268. // Creates a unique global windows object. If the object can be created it
  269. // returns true, otherwise it returns false. The false result can be used to
  270. // determine if another instance of a Second Life app (this version or later)
  271. // is running.
  272. // NOTE: Do not use this method to run a single instance of the app. This is
  273. // intended to help debug problems with the cross-platform locked file method
  274. // used for that purpose.
  275. bool create_app_mutex()
  276. {
  277. bool result = true;
  278. LPCWSTR unique_mutex_name = L"SecondLifeAppMutex";
  279. HANDLE hMutex = CreateMutex(NULL, TRUE, unique_mutex_name);
  280. if (GetLastError() == ERROR_ALREADY_EXISTS)
  281. {
  282. result = false;
  283. }
  284. return result;
  285. }
  286. #if LL_CLANG
  287. # pragma clang diagnostic pop
  288. #endif
  289. int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR cmd_line, int)
  290. {
  291. LLWindowWin32::setDPIAwareness();
  292. // Enable the low fragmentation heap; this results in a 2-3x improvement in
  293. // opening a new Inventory window (which uses a large numebr of
  294. // allocations).
  295. _CrtSetDbgFlag(0); // Default, just making explicit
  296. ULONG enable_heap = 2;
  297. constexpr S32 MAX_HEAPS = 255;
  298. DWORD heap_enable_lfh_error[MAX_HEAPS];
  299. HANDLE* heap_handles = new HANDLE[MAX_HEAPS];
  300. S32 num_heaps = GetProcessHeaps(MAX_HEAPS, heap_handles);
  301. for (S32 i = 0; i < num_heaps; ++i)
  302. {
  303. if (HeapSetInformation(heap_handles[i],
  304. HeapCompatibilityInformation,
  305. &enable_heap, sizeof(enable_heap)))
  306. {
  307. heap_enable_lfh_error[i] = 0;
  308. }
  309. else
  310. {
  311. heap_enable_lfh_error[i] = GetLastError();
  312. }
  313. }
  314. // *FIX: global
  315. gIconResource = MAKEINTRESOURCE(IDI_LL_ICON);
  316. LLAppViewerWin32* viewer_app_ptr = new LLAppViewerWin32(cmd_line);
  317. LLWinDebug::initExceptionHandler(viewer_windows_exception_handler);
  318. LLApp::setErrorHandler(LLAppViewer::handleViewerCrash);
  319. // Set a debug info flag to indicate if multiple instances are running.
  320. bool found_other_instance = !create_app_mutex();
  321. gDebugInfo["FoundOtherInstanceAtStartup"] = LLSD::Boolean(found_other_instance);
  322. LLApp::InitState state = viewer_app_ptr->init();
  323. if (state != LLApp::INIT_OK)
  324. {
  325. if (state != LLApp::INIT_OK_EXIT)
  326. {
  327. llwarns << "Application init failed." << llendl;
  328. return LLAppViewer::EXIT_INIT_FAILED;
  329. }
  330. return LLAppViewer::EXIT_OK; // No error, just exiting immediately.
  331. }
  332. // We have to wait until after logging is initialized to be able write to
  333. // the log file...
  334. if (sErrorReportingDisabled)
  335. {
  336. llinfos << "Windows error reporting disabled successfully." << llendl;
  337. }
  338. else
  339. {
  340. llinfos << "Windows error reporting disabling failed." << llendl;
  341. }
  342. if (num_heaps > 0)
  343. {
  344. llinfos << "Attempting to enable the Low Fragmentation Heap feature for "
  345. << num_heaps << " heaps:" << llendl;
  346. for (S32 i = 0; i < num_heaps; ++i)
  347. {
  348. if (heap_enable_lfh_error[i])
  349. {
  350. LL_DEBUGS("AppInit") << " -> LFH enabling failed for heap "
  351. << i << " with error: "
  352. << heap_enable_lfh_error[i] << LL_ENDL;
  353. }
  354. else
  355. {
  356. llinfos << " -> LFH enabled for heap " << i << llendl;
  357. }
  358. }
  359. }
  360. // Run the application main loop
  361. if (!LLApp::isQuitting())
  362. {
  363. viewer_app_ptr->mainLoop();
  364. }
  365. // We do not want to do cleanup here if the error handler got called; the
  366. // assumption is that the error handler is responsible for doing app
  367. // cleanup if there was a problem.
  368. if (LLApp::isError())
  369. {
  370. delete viewer_app_ptr;
  371. }
  372. else
  373. {
  374. viewer_app_ptr->cleanup();
  375. delete viewer_app_ptr;
  376. #if 1
  377. // *HACK: force-kill the viewer process to avoid getting a zombie
  378. // (background, never-ending) process.
  379. // *TODO: find out why the f*cking hell the use of libepoxy causes the
  380. // viewer to never exit otherwise under Windoze... HB
  381. TerminateProcess(GetCurrentProcess(), gExitCode);
  382. #endif
  383. }
  384. return gExitCode;
  385. }
  386. // SL-13528: This code used to be based on:
  387. // http://dslweb.nwnexus.com/~ast/dload/guicon.htm
  388. // (referenced in https://stackoverflow.com/a/191880).
  389. // But one of the comments on that StackOverflow answer points out that
  390. // assigning to *stdout or *stderr "probably doesn't even work with the
  391. // Universal CRT that was introduced in 2015," suggesting freopen_s() instead.
  392. // Code below is based on https://stackoverflow.com/a/55875595.
  393. static bool set_stream(FILE* fp, DWORD handle_id, const char* name,
  394. const std::string& mode)
  395. {
  396. HANDLE l_std_handle = GetStdHandle(handle_id);
  397. if (l_std_handle == INVALID_HANDLE_VALUE)
  398. {
  399. return false;
  400. }
  401. if (mode.find('w') != std::string::npos)
  402. {
  403. // Enable color processing for output streams
  404. DWORD dw_mode = 0;
  405. GetConsoleMode(l_std_handle, &dw_mode);
  406. dw_mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
  407. SetConsoleMode(l_std_handle, dw_mode);
  408. }
  409. // Redirect the passed fp to the console.
  410. FILE* ignore;
  411. if (freopen_s(&ignore, name, mode.c_str(), fp) == 0)
  412. {
  413. // Use unbuffered I/O
  414. setvbuf(fp, NULL, _IONBF, 0);
  415. }
  416. return true;
  417. }
  418. LLAppViewerWin32::LLAppViewerWin32(const char* cmd_line)
  419. : mCmdLine(cmd_line),
  420. mIsConsoleAllocated(false)
  421. {
  422. }
  423. // Platform specific initialization.
  424. LLApp::InitState LLAppViewerWin32::init()
  425. {
  426. // Turn off Windows error reporting (do not send our data to Microsoft)
  427. std::string executable_name = gDirUtil.getExecutableFilename();
  428. std::wstring utf16_exec_name = ll_convert_string_to_wide(executable_name);
  429. sErrorReportingDisabled =
  430. WerAddExcludedApplication(utf16_exec_name.c_str(), FALSE) == S_OK;
  431. // Also exclude SLVoice.exe, SLPlugin.exe and dullahan_host.exe. HB
  432. executable_name = gDirUtil.getExecutableDir() + "\\SLVoice.exe";
  433. utf16_exec_name = ll_convert_string_to_wide(executable_name);
  434. sErrorReportingDisabled &=
  435. WerAddExcludedApplication(utf16_exec_name.c_str(), FALSE) == S_OK;
  436. executable_name = gDirUtil.getExecutableDir() + "\\SLPlugin.exe";
  437. utf16_exec_name = ll_convert_string_to_wide(executable_name);
  438. sErrorReportingDisabled &=
  439. WerAddExcludedApplication(utf16_exec_name.c_str(), FALSE) == S_OK;
  440. executable_name = gDirUtil.getLLPluginDir() + "\\dullahan_host.exe";
  441. utf16_exec_name = ll_convert_string_to_wide(executable_name);
  442. sErrorReportingDisabled &=
  443. WerAddExcludedApplication(utf16_exec_name.c_str(), FALSE) == S_OK;
  444. // Initialize the viewer app proper.
  445. return LLAppViewer::init();
  446. }
  447. bool LLAppViewerWin32::cleanup()
  448. {
  449. bool result = LLAppViewer::cleanup();
  450. if (mIsConsoleAllocated)
  451. {
  452. FreeConsole();
  453. mIsConsoleAllocated = false;
  454. }
  455. return result;
  456. }
  457. void LLAppViewerWin32::initLogging()
  458. {
  459. // Remove the crash stack log from previous executions.
  460. // Since we have started logging a new instance of the app, we can assume
  461. // NOTE: This should happen before the we send a 'previous instance froze'
  462. // crash report, but it must happen after we initialize the DirUtil.
  463. LLWinDebug::clearCrashStacks();
  464. LLAppViewer::initLogging();
  465. }
  466. // Pops up the log console
  467. void LLAppViewerWin32::initConsole()
  468. {
  469. // Allocate a console for this app
  470. mIsConsoleAllocated = AllocConsole();
  471. if (mIsConsoleAllocated)
  472. {
  473. // Set the screen buffer to be big enough to let us scroll text
  474. CONSOLE_SCREEN_BUFFER_INFO coninfo;
  475. GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
  476. constexpr S32 MAX_CONSOLE_LINES = 8000;
  477. coninfo.dwSize.Y = MAX_CONSOLE_LINES;
  478. SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),
  479. coninfo.dwSize);
  480. // Redirect unbuffered STDOUT to the console
  481. if (!set_stream(stdout, STD_OUTPUT_HANDLE, "CONOUT$", "w"))
  482. {
  483. llwarns << "Failed to redirect stdout to the console." << llendl;
  484. }
  485. // Redirect unbuffered STDERR to the console
  486. if (!set_stream(stderr, STD_ERROR_HANDLE, "CONOUT$", "w"))
  487. {
  488. llwarns << "Failed to redirect stderr to the console." << llendl;
  489. }
  490. // Redirect unbuffered STDIN to the console
  491. if (!set_stream(stdout, STD_OUTPUT_HANDLE, "CONIN$", "r"))
  492. {
  493. llwarns << "Failed to redirect stdin to the console." << llendl;
  494. }
  495. }
  496. return LLAppViewer::initConsole();
  497. }
  498. void write_debug_dx(const char* str)
  499. {
  500. gDebugInfo["DXInfo"] = LLSD::String(gDebugInfo["DXInfo"].asString() + str);
  501. }
  502. void write_debug_dx(const std::string& str)
  503. {
  504. write_debug_dx(str.c_str());
  505. }
  506. // Driver verification and initialization based on DirectX hardware polling and
  507. // driver versions.
  508. bool LLAppViewerWin32::initHardwareTest()
  509. {
  510. if (!gSavedSettings.getBool("NoHardwareProbe"))
  511. {
  512. LLSplashScreen::update("Detecting hardware...");
  513. LL_DEBUGS("AppInit") << "Attempting to poll DirectX for hardware info"
  514. << LL_ENDL;
  515. LLDXHardware::setWriteDebugFunc(write_debug_dx);
  516. LLSD info = gDXHardware.getDisplayInfo();
  517. if (!info.size() && gSavedSettings.getWarning("AboutDirectX"))
  518. {
  519. llwarns << "DirectX probe failed, alerting user." << llendl;
  520. // Warn them that running without DirectX 9 will not allow us to
  521. // tell them about driver issues.
  522. std::ostringstream msg;
  523. msg << gSecondLife
  524. << " is unable to detect your graphics hardware via DirectX.\n\n"
  525. << "This may be due to unsupported or outdated graphics drivers or\n"
  526. << "hardware, and in these conditions the viewer might fail to run.\n"
  527. << "\nDo you wish to continue ?\n";
  528. S32 button = OSMessageBox(msg.str(), "Warning", OSMB_YESNO);
  529. if (button == OSBTN_NO)
  530. {
  531. llinfos << "User quitting after failed DirectX detection"
  532. << llendl;
  533. LLWeb::loadURLExternal(SUPPORT_URL, false);
  534. return false;
  535. }
  536. gSavedSettings.setWarning("AboutDirectX", false);
  537. }
  538. LL_DEBUGS("AppInit") << "Done polling DirectX for hardware info"
  539. << LL_ENDL;
  540. std::ostringstream splash_msg;
  541. splash_msg << "Loading " << gSecondLife << "...";
  542. LLSplashScreen::update(splash_msg.str());
  543. }
  544. if (!gGLManager.mVRAM)
  545. {
  546. llinfos << "Detecting available VRAM via DXGI..." << llendl;
  547. gGLManager.mVRAM = LLDXHardware::getMBVideoMemoryViaDXGI();
  548. }
  549. llinfos << "Detected VRAM: " << gGLManager.mVRAM << llendl;
  550. if (!restoreErrorTrap())
  551. {
  552. llwarns << "Someone took over my exception handler (post hardware probe) !"
  553. << llendl;
  554. }
  555. return true;
  556. }
  557. bool LLAppViewerWin32::initParseCommandLine(LLCommandLineParser& clp)
  558. {
  559. if (!clp.parseCommandLineString(mCmdLine))
  560. {
  561. return false;
  562. }
  563. // Find the system language.
  564. FL_Locale* locale = NULL;
  565. FL_Success success = FL_FindLocale(&locale, FL_MESSAGES);
  566. if (success)
  567. {
  568. if (success >= 2 && locale->lang) // Confident !
  569. {
  570. llinfos << "Language: " << ll_safe_string(locale->lang) << llendl;
  571. llinfos << "Location: " << ll_safe_string(locale->country)
  572. << llendl;
  573. llinfos << "Variant: " << ll_safe_string(locale->variant)
  574. << llendl;
  575. LLControlVariable* c = gSavedSettings.getControl("SystemLanguage");
  576. if (c)
  577. {
  578. c->setValue(std::string(locale->lang), false);
  579. }
  580. }
  581. }
  582. FL_FreeLocale(&locale);
  583. return true;
  584. }
  585. bool LLAppViewerWin32::beingDebugged()
  586. {
  587. bool debugged = LLError::Log::sIsBeingDebugged = IsDebuggerPresent();
  588. return debugged;
  589. }
  590. bool LLAppViewerWin32::restoreErrorTrap()
  591. {
  592. static F32 last_check = 0.f;
  593. if (gFrameTimeSeconds - last_check < 2.f)
  594. {
  595. // Do not waste time every frame on this: checking the exception
  596. // handler is costly. Kuddos to Kathrine Jansma for pointing this
  597. // hot spot in the VS profiler. HB
  598. return true;
  599. }
  600. last_check = gFrameTimeSeconds;
  601. return LLWinDebug::checkExceptionHandler();
  602. }
  603. void LLAppViewerWin32::handleSyncCrashTrace()
  604. {
  605. // Free our reserved memory space before dumping the stack trace (it should
  606. // already be freed at this point, but it does not hurt calling this method
  607. // twice).
  608. LLMemory::cleanupClass();
  609. }
  610. //virtual
  611. bool LLAppViewerWin32::sendURLToOtherInstance(const std::string& url)
  612. {
  613. wchar_t window_class[256]; // Assume max length < 255 chars.
  614. // Use the default window class name to for all Second Life viewers to
  615. // find any running session of any viewer.
  616. mbstowcs(window_class, "Second Life", 255);
  617. window_class[255] = 0;
  618. HWND other_window = FindWindow(window_class, NULL);
  619. if (other_window)
  620. {
  621. LL_DEBUGS("AppInit") << "Found other window with the class name 'Second Life'"
  622. << LL_ENDL;
  623. COPYDATASTRUCT cds;
  624. constexpr S32 SLURL_MESSAGE_TYPE = 0;
  625. cds.dwData = SLURL_MESSAGE_TYPE;
  626. cds.cbData = url.length() + 1;
  627. cds.lpData = (void*)url.c_str();
  628. LRESULT msg_result = SendMessage(other_window, WM_COPYDATA, NULL,
  629. (LPARAM)&cds);
  630. LL_DEBUGS("AppInit") << "SendMessage(WM_COPYDATA) to other window 'Second Life' returned "
  631. << msg_result << LL_ENDL;
  632. return true;
  633. }
  634. return false;
  635. }
  636. std::string LLAppViewerWin32::generateSerialNumber()
  637. {
  638. char serial_md5[MD5HEX_STR_SIZE];
  639. serial_md5[0] = 0;
  640. DWORD serial = 0;
  641. DWORD flags = 0;
  642. if (GetVolumeInformation(L"C:\\",
  643. NULL, // Volume name buffer
  644. 0, // Volume name buffer size
  645. &serial, // Volume serial
  646. NULL, // Max component length
  647. &flags, // File system flags
  648. NULL, // File system name buffer
  649. 0)) // File system name buffer size
  650. {
  651. LLMD5 md5;
  652. md5.update((unsigned char*)&serial, sizeof(DWORD));
  653. md5.finalize();
  654. md5.hex_digest(serial_md5);
  655. }
  656. else
  657. {
  658. llwarns << "GetVolumeInformation failed" << llendl;
  659. }
  660. return serial_md5;
  661. }
  662. ///////////////////////////////////////////////////////////////////////////////
  663. // Vulkan detection used by llviewerstats.cpp
  664. ///////////////////////////////////////////////////////////////////////////////
  665. // Minimal Vulkan API defines to avoid having to #include <vulkan/vulkan.h>
  666. #if defined(_WIN32)
  667. # define VKAPI_ATTR
  668. # define VKAPI_CALL __stdcall
  669. # define VKAPI_PTR VKAPI_CALL
  670. #else
  671. # define VKAPI_ATTR
  672. # define VKAPI_CALL
  673. # define VKAPI_PTR
  674. #endif
  675. #define VK_API_VERSION_MAJOR(v) ((U32(v) >> 22) & 0x07FU) // 7 bits
  676. #define VK_API_VERSION_MINOR(v) ((U32(v) >> 12) & 0x3FFU) // 10 bits
  677. #define VK_API_VERSION_PATCH(v) (U32(v) & 0xFFFU) // 12 bits
  678. #define VK_API_VERSION_VARIANT(v) ((U32(v) >> 29) & 0x007U) // 3 bits
  679. // NOTE: variant is first parameter ! This is to match vulkan/vulkan_core.h
  680. #define VK_MAKE_API_VERSION(variant, major, minor, patch) (0 | \
  681. ((U32(major) & 0x07FU) << 22) | ((U32(minor) & 0x3FFU) << 12) | \
  682. (U32(patch) << 12) | ((U32(variant) & 0x007U) << 29))
  683. #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object;
  684. // Types
  685. VK_DEFINE_HANDLE(VkInstance);
  686. typedef enum VkResult
  687. {
  688. VK_SUCCESS = 0,
  689. VK_RESULT_MAX_ENUM = 0x7FFFFFFF
  690. } VkResult;
  691. // Prototypes
  692. typedef void (VKAPI_PTR* PFN_vkVoidFunction)(void);
  693. typedef PFN_vkVoidFunction (VKAPI_PTR* PFN_vkGetInstanceProcAddr)(VkInstance instance,
  694. const char* namep);
  695. typedef VkResult (VKAPI_PTR* PFN_vkEnumerateInstanceVersion)(U32* versionp);
  696. //virtual
  697. bool LLAppViewerWin32::probeVulkan(std::string& version)
  698. {
  699. static std::string vk_api_version;
  700. static S32 has_vulkan = -1; // -1 = not yet probed
  701. if (has_vulkan == -1)
  702. {
  703. has_vulkan = 0; // Default to no Vulkan support
  704. // Probe for Vulkan capability (Dave Houlton 05/2020)
  705. // Check for presense of a Vulkan loader DLL, as a proxy for a
  706. // Vulkan-capable GPU. Gives a good approximation of Vulkan capability
  707. // within current user systems from this.
  708. if (HMODULE vulkan_loader = LoadLibraryA("vulkan-1.dll"))
  709. {
  710. has_vulkan = 1;
  711. // We have at least 1.0.
  712. // See the note about vkEnumerateInstanceVersion() below.
  713. vk_api_version = "1.0";
  714. PFN_vkGetInstanceProcAddr pGetInstanceProcAddr =
  715. (PFN_vkGetInstanceProcAddr)GetProcAddress(vulkan_loader,
  716. "vkGetInstanceProcAddr");
  717. if (pGetInstanceProcAddr)
  718. {
  719. // Check for vkEnumerateInstanceVersion. If it exists then we
  720. // have at least 1.1 and can query the max API version.
  721. // NOTE: Each VkPhysicalDevice that supports Vulkan has its own
  722. // VkPhysicalDeviceProperties.apiVersion which is separate from
  723. // the max API version ! See:
  724. // https://www.lunarg.com/wp-content/uploads/2019/02/
  725. // Vulkan-1.1-Compatibility-Statement_01_19.pdf
  726. PFN_vkEnumerateInstanceVersion pEnumerateInstanceVersion =
  727. (PFN_vkEnumerateInstanceVersion)pGetInstanceProcAddr(NULL,
  728. "vkEnumerateInstanceVersion");
  729. if (pEnumerateInstanceVersion)
  730. {
  731. U32 version = VK_MAKE_API_VERSION(0, 1, 1, 0);
  732. VkResult status = pEnumerateInstanceVersion(&version);
  733. if (status != VK_SUCCESS)
  734. {
  735. llinfos << "Failed to get Vulkan version. Assuming v1.0."
  736. << llendl;
  737. }
  738. else
  739. {
  740. S32 major = VK_API_VERSION_MAJOR(version);
  741. S32 minor = VK_API_VERSION_MINOR(version);
  742. S32 patch = VK_API_VERSION_PATCH(version);
  743. S32 variant = VK_API_VERSION_VARIANT(version);
  744. vk_api_version = llformat("%d.%d.%d.%d", major, minor,
  745. patch, variant);
  746. }
  747. }
  748. }
  749. else
  750. {
  751. llwarns << "Failed to get Vulkan vkGetInstanceProcAddr()"
  752. << llendl;
  753. }
  754. FreeLibrary(vulkan_loader);
  755. }
  756. }
  757. version = vk_api_version;
  758. return has_vulkan == 1;
  759. }