main.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. #include <QCoreApplication>
  2. #include <QDateTime>
  3. #include <QDebug>
  4. #include <QDir>
  5. #include <QFile>
  6. #include <QGuiApplication>
  7. #include <QOffscreenSurface>
  8. #include <QOpenGLContext>
  9. #include <QOpenGLFunctions>
  10. #include <QQmlApplicationEngine>
  11. #include <QQmlContext>
  12. #include <QQuickWindow>
  13. #include <QSGRendererInterface>
  14. #include <QSurfaceFormat>
  15. #include <QTextStream>
  16. #include <QUrl>
  17. #include <cstdio>
  18. #include <qglobal.h>
  19. #include <qguiapplication.h>
  20. #include <qnamespace.h>
  21. #include <qobject.h>
  22. #include <qqml.h>
  23. #include <qqmlapplicationengine.h>
  24. #include <qsgrendererinterface.h>
  25. #include <qstringliteral.h>
  26. #include <qstringview.h>
  27. #include <qsurfaceformat.h>
  28. #include <qurl.h>
  29. #ifdef Q_OS_WIN
  30. #include <QProcess>
  31. #include <gl/gl.h>
  32. #include <windows.h>
  33. #pragma comment(lib, "opengl32.lib")
  34. #endif
  35. #include "app/core/game_engine.h"
  36. #include "app/core/language_manager.h"
  37. #include "ui/gl_view.h"
  38. #include "ui/theme.h"
  39. // Constants replacing magic numbers
  40. constexpr int k_depth_buffer_bits = 24;
  41. constexpr int k_stencil_buffer_bits = 8;
  42. #ifdef Q_OS_WIN
  43. // Test OpenGL using native Win32 API (before any Qt initialization)
  44. // Returns true if OpenGL is available, false otherwise
  45. static bool testNativeOpenGL() {
  46. WNDCLASSA wc = {};
  47. wc.lpfnWndProc = DefWindowProcA;
  48. wc.hInstance = GetModuleHandle(nullptr);
  49. wc.lpszClassName = "OpenGLTest";
  50. if (!RegisterClassA(&wc)) {
  51. return false;
  52. }
  53. HWND hwnd = CreateWindowExA(0, "OpenGLTest", "", WS_OVERLAPPEDWINDOW, 0, 0, 1,
  54. 1, nullptr, nullptr, wc.hInstance, nullptr);
  55. if (!hwnd) {
  56. UnregisterClassA("OpenGLTest", wc.hInstance);
  57. return false;
  58. }
  59. HDC hdc = GetDC(hwnd);
  60. if (!hdc) {
  61. DestroyWindow(hwnd);
  62. UnregisterClassA("OpenGLTest", wc.hInstance);
  63. return false;
  64. }
  65. PIXELFORMATDESCRIPTOR pfd = {};
  66. pfd.nSize = sizeof(pfd);
  67. pfd.nVersion = 1;
  68. pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
  69. pfd.iPixelType = PFD_TYPE_RGBA;
  70. pfd.cColorBits = 24;
  71. pfd.cDepthBits = 24;
  72. pfd.cStencilBits = 8;
  73. pfd.iLayerType = PFD_MAIN_PLANE;
  74. int pixelFormat = ChoosePixelFormat(hdc, &pfd);
  75. bool success = false;
  76. if (pixelFormat != 0 && SetPixelFormat(hdc, pixelFormat, &pfd)) {
  77. HGLRC hglrc = wglCreateContext(hdc);
  78. if (hglrc) {
  79. if (wglMakeCurrent(hdc, hglrc)) {
  80. // Successfully created OpenGL context
  81. const char *vendor = (const char *)glGetString(GL_VENDOR);
  82. const char *renderer = (const char *)glGetString(GL_RENDERER);
  83. const char *version = (const char *)glGetString(GL_VERSION);
  84. if (vendor && renderer && version) {
  85. fprintf(stderr,
  86. "[OpenGL Test] Native context created successfully\n");
  87. fprintf(stderr, "[OpenGL Test] Vendor: %s\n", vendor);
  88. fprintf(stderr, "[OpenGL Test] Renderer: %s\n", renderer);
  89. fprintf(stderr, "[OpenGL Test] Version: %s\n", version);
  90. success = true;
  91. }
  92. wglMakeCurrent(nullptr, nullptr);
  93. }
  94. wglDeleteContext(hglrc);
  95. }
  96. }
  97. ReleaseDC(hwnd, hdc);
  98. DestroyWindow(hwnd);
  99. UnregisterClassA("OpenGLTest", wc.hInstance);
  100. return success;
  101. }
  102. // Windows crash handler to detect OpenGL failures and suggest fallback
  103. static bool g_opengl_crashed = false;
  104. static LONG WINAPI crashHandler(EXCEPTION_POINTERS *exceptionInfo) {
  105. if (exceptionInfo->ExceptionRecord->ExceptionCode ==
  106. EXCEPTION_ACCESS_VIOLATION) {
  107. // Log crash
  108. FILE *crash_log = fopen("opengl_crash.txt", "w");
  109. if (crash_log) {
  110. fprintf(crash_log,
  111. "OpenGL/Qt rendering crash detected (Access Violation)\n");
  112. fprintf(crash_log, "Try running with: run_debug_softwaregl.cmd\n");
  113. fprintf(crash_log,
  114. "Or set environment variable: QT_QUICK_BACKEND=software\n");
  115. fclose(crash_log);
  116. }
  117. qCritical() << "=== CRASH DETECTED ===";
  118. qCritical() << "OpenGL rendering failed. This usually means:";
  119. qCritical() << "1. Graphics drivers are outdated";
  120. qCritical() << "2. Running in a VM with incomplete OpenGL support";
  121. qCritical() << "3. GPU doesn't support required OpenGL version";
  122. qCritical() << "";
  123. qCritical() << "To fix: Run run_debug_softwaregl.cmd instead";
  124. qCritical() << "Or set: set QT_QUICK_BACKEND=software";
  125. g_opengl_crashed = true;
  126. }
  127. return EXCEPTION_CONTINUE_SEARCH;
  128. }
  129. #endif
  130. auto main(int argc, char *argv[]) -> int {
  131. #ifdef Q_OS_WIN
  132. // Install crash handler to detect OpenGL failures
  133. SetUnhandledExceptionFilter(crashHandler);
  134. // Test OpenGL BEFORE any Qt initialization (using native Win32 API)
  135. fprintf(stderr, "[Pre-Init] Testing native OpenGL availability...\n");
  136. bool opengl_available = testNativeOpenGL();
  137. if (!opengl_available) {
  138. fprintf(stderr, "[Pre-Init] WARNING: OpenGL test failed!\n");
  139. fprintf(stderr, "[Pre-Init] Forcing software rendering mode\n");
  140. _putenv("QT_QUICK_BACKEND=software");
  141. _putenv("QT_OPENGL=software");
  142. } else {
  143. fprintf(stderr, "[Pre-Init] OpenGL test passed\n");
  144. }
  145. // Check if we should use software rendering
  146. bool use_software = qEnvironmentVariableIsSet("QT_QUICK_BACKEND") &&
  147. qEnvironmentVariable("QT_QUICK_BACKEND") == "software";
  148. if (use_software) {
  149. qInfo() << "=== SOFTWARE RENDERING MODE ===";
  150. qInfo() << "Using Qt Quick Software renderer (CPU-based)";
  151. qInfo() << "Performance will be limited but should work on all systems";
  152. }
  153. #endif
  154. // Setup message handler for debugging
  155. qInstallMessageHandler([](QtMsgType type, const QMessageLogContext &context,
  156. const QString &msg) {
  157. QByteArray const localMsg = msg.toLocal8Bit();
  158. const char *file = (context.file != nullptr) ? context.file : "";
  159. const char *function =
  160. (context.function != nullptr) ? context.function : "";
  161. FILE *out = stderr;
  162. switch (type) {
  163. case QtDebugMsg:
  164. fprintf(out, "[DEBUG] %s (%s:%u, %s)\n", localMsg.constData(), file,
  165. context.line, function);
  166. break;
  167. case QtInfoMsg:
  168. fprintf(out, "[INFO] %s\n", localMsg.constData());
  169. break;
  170. case QtWarningMsg:
  171. fprintf(out, "[WARNING] %s (%s:%u, %s)\n", localMsg.constData(), file,
  172. context.line, function);
  173. // Check for critical OpenGL warnings
  174. if (msg.contains("OpenGL", Qt::CaseInsensitive) ||
  175. msg.contains("scene graph", Qt::CaseInsensitive) ||
  176. msg.contains("RHI", Qt::CaseInsensitive)) {
  177. fprintf(out, "[HINT] If you see crashes, try software rendering: set "
  178. "QT_QUICK_BACKEND=software\n");
  179. }
  180. break;
  181. case QtCriticalMsg:
  182. fprintf(out, "[CRITICAL] %s (%s:%u, %s)\n", localMsg.constData(), file,
  183. context.line, function);
  184. fprintf(
  185. out,
  186. "[CRITICAL] Try running with software rendering if this persists\n");
  187. break;
  188. case QtFatalMsg:
  189. fprintf(out, "[FATAL] %s (%s:%u, %s)\n", localMsg.constData(), file,
  190. context.line, function);
  191. fprintf(out, "[FATAL] === RECOVERY SUGGESTION ===\n");
  192. fprintf(out, "[FATAL] Run: run_debug_softwaregl.cmd\n");
  193. fprintf(out, "[FATAL] Or set: QT_QUICK_BACKEND=software\n");
  194. abort();
  195. }
  196. fflush(out);
  197. });
  198. qInfo() << "=== Standard of Iron - Starting ===";
  199. qInfo() << "Qt version:" << QT_VERSION_STR;
  200. // Linux-specific: prefer X11 over Wayland for better OpenGL compatibility
  201. #ifndef Q_OS_WIN
  202. if (qEnvironmentVariableIsSet("WAYLAND_DISPLAY") &&
  203. qEnvironmentVariableIsSet("DISPLAY")) {
  204. qputenv("QT_QPA_PLATFORM", "xcb");
  205. qInfo() << "Linux: Using X11 (xcb) platform";
  206. }
  207. #endif
  208. qInfo() << "Setting OpenGL environment...";
  209. qputenv("QT_OPENGL", "desktop");
  210. qputenv("QSG_RHI_BACKEND", "opengl");
  211. #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
  212. qInfo() << "Setting graphics API to OpenGLRhi...";
  213. QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi);
  214. #endif
  215. qInfo() << "Configuring OpenGL surface format...";
  216. QSurfaceFormat fmt;
  217. fmt.setVersion(3, 3);
  218. fmt.setProfile(QSurfaceFormat::CoreProfile);
  219. fmt.setDepthBufferSize(k_depth_buffer_bits);
  220. fmt.setStencilBufferSize(k_stencil_buffer_bits);
  221. fmt.setSamples(0);
  222. #ifdef Q_OS_WIN
  223. // Windows: Request compatibility profile for better driver support
  224. // Some Windows drivers have issues with Core profile on older hardware
  225. fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
  226. qInfo() << "Windows detected: Using OpenGL Compatibility Profile";
  227. #endif
  228. QSurfaceFormat::setDefaultFormat(fmt);
  229. qInfo() << "Surface format configured: OpenGL" << fmt.majorVersion() << "."
  230. << fmt.minorVersion();
  231. qInfo() << "Creating QGuiApplication...";
  232. QGuiApplication app(argc, argv);
  233. qInfo() << "QGuiApplication created successfully";
  234. qInfo() << "Creating LanguageManager...";
  235. auto *language_manager = new LanguageManager();
  236. qInfo() << "LanguageManager created";
  237. qInfo() << "Creating GameEngine...";
  238. auto *game_engine = new GameEngine();
  239. qInfo() << "GameEngine created";
  240. qInfo() << "Setting up QML engine...";
  241. QQmlApplicationEngine engine;
  242. qInfo() << "Adding context properties...";
  243. engine.rootContext()->setContextProperty("language_manager",
  244. language_manager);
  245. engine.rootContext()->setContextProperty("game", game_engine);
  246. qInfo() << "Adding import path...";
  247. engine.addImportPath("qrc:/StandardOfIron/ui/qml");
  248. qInfo() << "Registering QML types...";
  249. qmlRegisterType<GLView>("StandardOfIron", 1, 0, "GLView");
  250. // Register Theme singleton
  251. qmlRegisterSingletonType<Theme>("StandardOfIron.UI", 1, 0, "Theme",
  252. &Theme::create);
  253. qInfo() << "Loading Main.qml...";
  254. qInfo() << "Loading Main.qml...";
  255. engine.load(QUrl(QStringLiteral("qrc:/StandardOfIron/ui/qml/Main.qml")));
  256. qInfo() << "Checking if QML loaded...";
  257. if (engine.rootObjects().isEmpty()) {
  258. qWarning() << "Failed to load QML file";
  259. return -1;
  260. }
  261. qInfo() << "QML loaded successfully, root objects count:"
  262. << engine.rootObjects().size();
  263. qInfo() << "Finding QQuickWindow...";
  264. auto *root_obj = engine.rootObjects().first();
  265. auto *window = qobject_cast<QQuickWindow *>(root_obj);
  266. if (window == nullptr) {
  267. qInfo() << "Root object is not a window, searching children...";
  268. window = root_obj->findChild<QQuickWindow *>();
  269. }
  270. if (window == nullptr) {
  271. qWarning() << "No QQuickWindow found for OpenGL initialization.";
  272. return -2;
  273. }
  274. qInfo() << "QQuickWindow found";
  275. qInfo() << "Setting window in GameEngine...";
  276. game_engine->setWindow(window);
  277. qInfo() << "Window set successfully";
  278. qInfo() << "Connecting scene graph signals...";
  279. qInfo() << "Connecting scene graph signals...";
  280. QObject::connect(
  281. window, &QQuickWindow::sceneGraphInitialized, window, [window]() {
  282. qInfo() << "Scene graph initialized!";
  283. if (auto *renderer_interface = window->rendererInterface()) {
  284. const auto api = renderer_interface->graphicsApi();
  285. QString name;
  286. switch (api) {
  287. case QSGRendererInterface::OpenGLRhi:
  288. name = "OpenGLRhi";
  289. break;
  290. case QSGRendererInterface::VulkanRhi:
  291. name = "VulkanRhi";
  292. break;
  293. case QSGRendererInterface::Direct3D11Rhi:
  294. name = "D3D11Rhi";
  295. break;
  296. case QSGRendererInterface::MetalRhi:
  297. name = "MetalRhi";
  298. break;
  299. case QSGRendererInterface::Software:
  300. name = "Software";
  301. break;
  302. default:
  303. name = "Unknown";
  304. break;
  305. }
  306. qInfo() << "QSG graphicsApi:" << name;
  307. }
  308. });
  309. QObject::connect(window, &QQuickWindow::sceneGraphError, &app,
  310. [&](QQuickWindow::SceneGraphError, const QString &msg) {
  311. qCritical()
  312. << "Failed to initialize OpenGL scene graph:" << msg;
  313. QGuiApplication::exit(3);
  314. });
  315. qInfo() << "Starting event loop...";
  316. int const result = QGuiApplication::exec();
  317. #ifdef Q_OS_WIN
  318. // Check if we crashed during OpenGL initialization
  319. if (g_opengl_crashed) {
  320. qCritical() << "";
  321. qCritical() << "========================================";
  322. qCritical() << "OPENGL CRASH RECOVERY";
  323. qCritical() << "========================================";
  324. qCritical() << "";
  325. qCritical() << "The application crashed during OpenGL initialization.";
  326. qCritical()
  327. << "This is a known issue with Qt + some Windows graphics drivers.";
  328. qCritical() << "";
  329. qCritical() << "SOLUTION: Set environment variable before running:";
  330. qCritical() << " set QT_QUICK_BACKEND=software";
  331. qCritical() << "";
  332. qCritical() << "Or use the provided launcher:";
  333. qCritical() << " run_debug_softwaregl.cmd";
  334. qCritical() << "";
  335. return -1;
  336. }
  337. #endif
  338. return result;
  339. }