diff --git a/binaries/data/mods/public/hwdetect/hwdetect.js b/binaries/data/mods/public/hwdetect/hwdetect.js new file mode 100644 index 0000000000..06dd5ba98f --- /dev/null +++ b/binaries/data/mods/public/hwdetect/hwdetect.js @@ -0,0 +1,107 @@ +/* Copyright (c) 2010 Wildfire Games + * + * 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, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * 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. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + +This script is for adjusting the game's default settings based on the +user's system configuration details. + +The game engine itself does some detection of capabilities, so it will +enable certain graphical features only when they are supported. +This script is for the messier task of avoiding performance problems +and driver bugs based on experience of particular system configurations. + +*/ + +function RunDetection(settings) +{ + // List of warning strings to display to the user + var warnings = []; + + // TODO: add some mechanism for setting config values + // (overriding default.cfg, but overridden by local.cfg) + + + // Extract all the settings we might use from the argument: + // (This is less error-prone than referring to "settings.foo" directly + // since typos will be caught) + + // OS flags (0 or 1) + var os_unix = settings.os_unix; + var os_linux = settings.os_linux; + var os_macosx = settings.os_macosx; + var os_win = settings.os_win; + + // Should avoid using these, since they're disabled in quickstart mode + var gfx_card = settings.gfx_card; + var gfx_drv_ver = settings.gfx_drv_ver; + var gfx_mem = settings.gfx_mem; + + // Values from glGetString + var gl_vendor = settings.gl_vendor; + var gl_renderer = settings.gl_renderer; + var gl_version = settings.gl_version; + var gl_extensions = settings.gl_extensions.split(" "); // split on spaces + + var video_xres = settings.video_xres; + var video_yres = settings.video_yres; + var video_bpp = settings.video_bpp; + + var uname_sysname = settings.uname_sysname; + var uname_release = settings.uname_release; + var uname_version = settings.uname_version; + var uname_machine = settings.uname_machine; + + var cpu_identifier = settings.cpu_identifier; + var cpu_frequency = settings.cpu_frequency; // -1 if unknown + + var ram_total = settings.ram_total; // megabytes + var ram_free = settings.ram_free; // megabytes + + + // NVIDIA 260.19.* UNIX drivers cause random crashes soon after startup. + // http://www.wildfiregames.com/forum/index.php?showtopic=13668 + // Fixed in 260.19.21: + // "Fixed a race condition in OpenGL that could cause crashes with multithreaded applications." + if (os_unix && gl_version.match(/NVIDIA 260\.19\.(0[0-9]|1[0-9]|20)$/)) + { + warnings.push("You are using 260.19.* series NVIDIA drivers, which may crash the game. Please upgrade to 260.19.21 or later."); + } + + + return { "warnings": warnings }; +} + +global.RunHardwareDetection = function(settings) +{ + //print(uneval(settings)+"\n"); + + var output = RunDetection(settings); + + //print(uneval(output)+"\n"); + + if (output.warnings.length) + { + var msg = output.warnings.join("\n\n"); + Engine.DisplayErrorDialog(msg); + } +}; diff --git a/source/gui/scripting/ScriptFunctions.cpp b/source/gui/scripting/ScriptFunctions.cpp index 51747b3f13..48824e8966 100644 --- a/source/gui/scripting/ScriptFunctions.cpp +++ b/source/gui/scripting/ScriptFunctions.cpp @@ -328,6 +328,12 @@ bool HotkeyIsPressed_(void* UNUSED(cbdata), std::string hotkeyName) return HotkeyIsPressed(hotkeyName); } +void DisplayErrorDialog(void* UNUSED(cbdata), std::wstring msg) +{ + debug_DisplayError(msg.c_str(), DE_NO_DEBUG_INFO, NULL, NULL, NULL, 0, NULL, NULL); +} + + void SetSimRate(void* UNUSED(cbdata), float rate) { g_Game->SetSimRate(rate); @@ -415,6 +421,7 @@ void GuiScriptingInit(ScriptInterface& scriptInterface) scriptInterface.RegisterFunction("CameraFollow"); scriptInterface.RegisterFunction("CameraFollowFPS"); scriptInterface.RegisterFunction("HotkeyIsPressed"); + scriptInterface.RegisterFunction("DisplayErrorDialog"); // Development/debugging functions scriptInterface.RegisterFunction("SetSimRate"); diff --git a/source/lib/debug.cpp b/source/lib/debug.cpp index b186614af7..d0109be35d 100644 --- a/source/lib/debug.cpp +++ b/source/lib/debug.cpp @@ -429,6 +429,15 @@ ErrorReaction debug_DisplayError(const wchar_t* description, // the error display implementation enables the Suppress option. if(suppress) flags |= DE_ALLOW_SUPPRESS; + + if(flags & DE_NO_DEBUG_INFO) + { + // in non-debug-info mode, simply display the given description + // and then return immediately + ErrorReaction er = CallDisplayError(description, flags); + return PerformErrorReaction(er, flags, suppress); + } + // .. deal with incomplete file/line info if(!pathname || pathname[0] == '\0') pathname = L"unknown"; diff --git a/source/lib/debug.h b/source/lib/debug.h index 66e4dc8fe4..b16c789faa 100644 --- a/source/lib/debug.h +++ b/source/lib/debug.h @@ -102,7 +102,13 @@ enum DebugDisplayErrorFlags * will take care of this if ER_BREAK is returned. this is so that the * debugger can jump directly into the offending function. **/ - DE_MANUAL_BREAK = 4 + DE_MANUAL_BREAK = 4, + + /** + * display just the given message; do not add any information about the + * call stack, do not write crashlogs, etc. + */ + DE_NO_DEBUG_INFO = 8 }; /** diff --git a/source/lib/sysdep/os/unix/unix.cpp b/source/lib/sysdep/os/unix/unix.cpp index e3b9f03215..cac16fbd36 100644 --- a/source/lib/sysdep/os/unix/unix.cpp +++ b/source/lib/sysdep/os/unix/unix.cpp @@ -72,6 +72,10 @@ static ErrorReaction try_gui_display_error(const wchar_t* text, bool manual_brea // Replace CRLF->LF boost::algorithm::replace_all(message, "\r\n", "\n"); + // TODO: we ought to wrap the text if it's very long, + // since xmessage doesn't do that and it'll get clamped + // to the screen width + const char* cmd = "/usr/bin/xmessage"; char buttons[256] = ""; @@ -92,6 +96,8 @@ static ErrorReaction try_gui_display_error(const wchar_t* text, bool manual_brea // and don't care about the memory leak char* const argv[] = { strdup(cmd), + strdup("-geometry"), strdup("x500"), // set height so the box will always be very visible + strdup("-title"), strdup("0 A.D. message"), // TODO: maybe shouldn't hard-code app name strdup("-buttons"), buttons, strdup("-default"), strdup(defaultButton), strdup(message.c_str()), diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index 4497339e7a..9ccb694eef 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -94,6 +94,7 @@ #include "ps/GameSetup/Paths.h" #include "ps/GameSetup/Config.h" #include "ps/GameSetup/CmdLineArgs.h" +#include "ps/GameSetup/HWDetect.h" #if !(OS_WIN || OS_MACOSX) // assume all other platforms use X11 for wxWidgets #define MUST_INIT_X11 1 @@ -798,16 +799,18 @@ void InitGraphics(const CmdLineArgs& args, int flags) SDL_WM_SetCaption("0 A.D.", "0 A.D."); } - tex_codec_register_all(); - - const int quality = SANE_TEX_QUALITY_DEFAULT; // TODO: set value from config file - SetTextureQuality(quality); - // needed by ogl_tex to detect broken gfx card/driver combos, // but takes a while due to WMI startup, so make it optional. if(!g_Quickstart) gfx_detect(); + RunHardwareDetection(); + + tex_codec_register_all(); + + const int quality = SANE_TEX_QUALITY_DEFAULT; // TODO: set value from config file + SetTextureQuality(quality); + ogl_WarnIfError(); if(!g_Quickstart) diff --git a/source/ps/GameSetup/HWDetect.cpp b/source/ps/GameSetup/HWDetect.cpp new file mode 100644 index 0000000000..c7237bcabd --- /dev/null +++ b/source/ps/GameSetup/HWDetect.cpp @@ -0,0 +1,93 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. 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, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +#include "precompiled.h" + +#include "scripting/ScriptingHost.h" +#include "scriptinterface/ScriptInterface.h" + +#include "lib/ogl.h" +#include "lib/utf8.h" +#include "lib/sysdep/cpu.h" +#include "lib/sysdep/gfx.h" +#include "lib/sysdep/os_cpu.h" +#include "ps/CLogger.h" +#include "ps/Filesystem.h" +#include "ps/VideoMode.h" + +void RunHardwareDetection() +{ + ScriptInterface& scriptInterface = g_ScriptingHost.GetScriptInterface(); + + // Load the detection script: + + const wchar_t* scriptName = L"hwdetect/hwdetect.js"; + CVFSFile file; + if (file.Load(g_VFS, scriptName) != PSRETURN_OK) + { + LOGERROR(L"Failed to load hardware detection script"); + return; + } + + LibError err; // ignore encoding errors + std::wstring code = wstring_from_utf8(file.GetAsString(), &err); + + scriptInterface.LoadScript(scriptName, code); + + // Collect all the settings we'll pass to the script: + + CScriptValRooted settings; + scriptInterface.Eval("({})", settings); + + scriptInterface.SetProperty(settings.get(), "os_unix", OS_UNIX, false); + scriptInterface.SetProperty(settings.get(), "os_linux", OS_LINUX, false); + scriptInterface.SetProperty(settings.get(), "os_macosx", OS_MACOSX, false); + scriptInterface.SetProperty(settings.get(), "os_win", OS_WIN, false); + + scriptInterface.SetProperty(settings.get(), "gfx_card", std::wstring(gfx_card), false); + scriptInterface.SetProperty(settings.get(), "gfx_drv_ver", std::wstring(gfx_drv_ver), false); + scriptInterface.SetProperty(settings.get(), "gfx_mem", gfx_mem, false); + + scriptInterface.SetProperty(settings.get(), "gl_vendor", std::string((const char*)glGetString(GL_VENDOR)), false); + scriptInterface.SetProperty(settings.get(), "gl_renderer", std::string((const char*)glGetString(GL_RENDERER)), false); + scriptInterface.SetProperty(settings.get(), "gl_version", std::string((const char*)glGetString(GL_VERSION)), false); + + const char* exts = ogl_ExtensionString(); + if (!exts) exts = ""; + scriptInterface.SetProperty(settings.get(), "gl_extensions", std::string(exts), false); + + scriptInterface.SetProperty(settings.get(), "video_xres", g_VideoMode.GetXRes(), false); + scriptInterface.SetProperty(settings.get(), "video_yres", g_VideoMode.GetYRes(), false); + scriptInterface.SetProperty(settings.get(), "video_bpp", g_VideoMode.GetBPP(), false); + + struct utsname un; + uname(&un); + scriptInterface.SetProperty(settings.get(), "uname_sysname", std::string(un.sysname), false); + scriptInterface.SetProperty(settings.get(), "uname_release", std::string(un.release), false); + scriptInterface.SetProperty(settings.get(), "uname_version", std::string(un.version), false); + scriptInterface.SetProperty(settings.get(), "uname_machine", std::string(un.machine), false); + + scriptInterface.SetProperty(settings.get(), "cpu_identifier", std::string(cpu_IdentifierString()), false); + scriptInterface.SetProperty(settings.get(), "cpu_frequency", os_cpu_ClockFrequency(), false); + + scriptInterface.SetProperty(settings.get(), "ram_total", (int)os_cpu_MemorySize(), false); + scriptInterface.SetProperty(settings.get(), "ram_free", (int)os_cpu_MemoryAvailable(), false); + + // Run the detection script: + + scriptInterface.CallFunctionVoid(scriptInterface.GetGlobalObject(), "RunHardwareDetection", settings); +} diff --git a/source/ps/GameSetup/HWDetect.h b/source/ps/GameSetup/HWDetect.h new file mode 100644 index 0000000000..7b0069bd4d --- /dev/null +++ b/source/ps/GameSetup/HWDetect.h @@ -0,0 +1,28 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. 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, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +#ifndef INCLUDED_HWDETECT +#define INCLUDED_HWDETECT + +/** + * Runs hardware-detection script to adjust default config settings + * and/or emit warnings depending on the user's system configuration. + * This must only be called after ogl_Init. + */ +void RunHardwareDetection(); + +#endif // INCLUDED_HWDETECT