diff --git a/Source/System/GLCheck.cpp b/Source/System/GLCheck.cpp index b10453836..93d7d0bc9 100644 --- a/Source/System/GLCheck.cpp +++ b/Source/System/GLCheck.cpp @@ -1,13 +1,75 @@ #include "GLCheck.h" #include "glad/gl.h" -#include -#include +#include +#include +#include "ConsoleMan.h" +using namespace RTE; void CheckOpenGLError(const char* stmt, const char* fname, int line) { - GLenum err = glGetError(); - if (err != GL_NO_ERROR) { - printf("OpenGL error %08x, at %s:%i - for %s\n", err, fname, line, stmt); - abort(); + GLenum errorCode = glGetError(); + + if (errorCode != GL_NO_ERROR) { + std::string fileString = fname; + std::string error = "Unknown error"; + std::string description = "No description"; + + // Decode the error code + switch (errorCode) { + case GL_INVALID_ENUM: { + error = "GL_INVALID_ENUM"; + description = "An unacceptable value has been specified for an enumerated argument."; + break; + } + + case GL_INVALID_VALUE: { + error = "GL_INVALID_VALUE"; + description = "A numeric argument is out of range."; + break; + } + + case GL_INVALID_OPERATION: { + error = "GL_INVALID_OPERATION"; + description = "The specified operation is not allowed in the current state."; + break; + } + + case GL_STACK_OVERFLOW: { + error = "GL_STACK_OVERFLOW"; + description = "This command would cause a stack overflow."; + break; + } + + case GL_STACK_UNDERFLOW: { + error = "GL_STACK_UNDERFLOW"; + description = "This command would cause a stack underflow."; + break; + } + + case GL_OUT_OF_MEMORY: { + error = "GL_OUT_OF_MEMORY"; + description = "There is not enough memory left to execute the command."; + break; + } + + case GL_INVALID_FRAMEBUFFER_OPERATION: { + error = "GL_INVALID_FRAMEBUFFER_OPERATION"; + description = "The object bound to FRAMEBUFFER_BINDING is not \"framebuffer complete\"."; + break; + } + + default: { + error = "Unknown Error" + std::to_string(errorCode); + description = ""; + } + } + + // Log the error + std::stringstream errorMessage; + errorMessage << "ERROR: An internal OpenGL call failed in " + << fileString.substr(fileString.find_last_of("\\/") + 1) << "(" << line << ")." + << "\nExpression:\n " << stmt + << "\nError description:\n " << error << "\n " << description << "\n"; + g_ConsoleMan.PrintString(errorMessage.str()); } } diff --git a/Source/System/GLCheck.h b/Source/System/GLCheck.h index 260b20ce2..8a92522be 100644 --- a/Source/System/GLCheck.h +++ b/Source/System/GLCheck.h @@ -5,12 +5,8 @@ void CheckOpenGLError(const char* stmt, const char* fname, int line); /// Debug macro to be used for all GL calls. -#ifdef DEBUG #define GL_CHECK(stmt) \ do { \ stmt; \ CheckOpenGLError(#stmt, __FILE__, __LINE__); \ } while (0) -#else -#define GL_CHECK(stmt) stmt -#endif diff --git a/Source/System/RTEError.cpp b/Source/System/RTEError.cpp index dd5cb5e5c..14ff3b27f 100644 --- a/Source/System/RTEError.cpp +++ b/Source/System/RTEError.cpp @@ -20,6 +20,20 @@ #include #include +#ifdef _MSC_VER +#include +#elif defined(__linux__) +#include +#endif + +#ifdef __linux__ +#include +#include +#include +#elif defined(__APPLE__) && defined(__MACH__) +#include +#endif + using namespace RTE; bool RTEError::s_CurrentlyAborting = false; @@ -326,6 +340,8 @@ void RTEError::AbortFunc(const std::string& description, const std::source_locat g_ConsoleMan.PrintString(abortMessage); + DumpHardwareInfo(); + std::string callstack = ""; #ifdef _WIN32 @@ -386,8 +402,8 @@ void RTEError::AssertFunc(const std::string& description, const std::source_loca if (!s_IgnoreAllAsserts) { std::string assertMessage = - "Assertion in file '" + fileName + "', line " + lineNum + ",\nin function '" + funcName + "'\nbecause:\n\n" + description + "\n\n" - "You may choose to ignore this and crash immediately\nor at some unexpected point later on.\n\nProceed at your own risk!"; + "Assertion in file '" + fileName + "', line " + lineNum + ",\nin function '" + funcName + "'\nbecause:\n\n" + description + "\n\n" + + "You may choose to ignore this and crash immediately\nor at some unexpected point later on.\n\nProceed at your own risk!"; if (ShowAssertMessageBox(assertMessage)) { AbortFunc(description, srcLocation); @@ -404,6 +420,121 @@ void RTEError::AssertFunc(const std::string& description, const std::source_loca } } +void RTEError::DumpHardwareInfo() { + std::string glVersion = reinterpret_cast(glGetString(GL_VERSION)); + std::string glVendor = reinterpret_cast(glGetString(GL_VENDOR)); + std::string glRenderer = reinterpret_cast(glGetString(GL_RENDERER)); + std::string hwInfo = "GL Version: " + glVersion + "\n" + + "GL Vendor: " + glVendor + "\n" + + "GL Renderer: " + glRenderer + "\n"; + +#if defined(_MSC_VER) || defined(__linux__) + int vendorRegs[4] = {0}; +#ifdef _MSC_VER + __cpuid(vendorRegs, 0); +#else + __cpuid(0, vendorRegs[0], vendorRegs[1], vendorRegs[2], vendorRegs[3]); +#endif + + std::string cpuVendor(reinterpret_cast(&vendorRegs[1]), 4); + cpuVendor += std::string(reinterpret_cast(&vendorRegs[3]), 4); + cpuVendor += std::string(reinterpret_cast(&vendorRegs[2]), 4); + + hwInfo += "CPU Manufacturer ID: " + cpuVendor + "\n"; + + std::string cpuModel; + int modelRegs[12]; +#ifdef _MSC_VER + __cpuid(modelRegs, 0x80000000); +#else + __cpuid(0x80000000, modelRegs[0], modelRegs[1], modelRegs[2], modelRegs[3]); +#endif + if (modelRegs[0] >= 0x80000004) { + for (size_t i = 0; i <= 2; ++i) { +#ifdef _MSC_VER + __cpuid(&modelRegs[0] + i * 4, i + 0x80000002); +#else + __cpuid(i + 0x80000002, modelRegs[0 + i * 4], modelRegs[1 + i * 4], modelRegs[2 + i * 4], modelRegs[3 + i * 4]); +#endif + } + for (size_t i = 0; i < 12; ++i) { + cpuModel += std::string(reinterpret_cast(&modelRegs[i]), 4); + } + + hwInfo += "CPU Model: " + cpuModel + "\n"; + } +#elif defined(__APPLE__) && defined(__MACH__) + char vendor[1024]; + size_t vendorSize = sizeof(vendor); + int error = sysctlbyname("machdep.cpu.vendor", &vendor, &vendorSize, nullptr, 0); + if (!error) { + hwInfo += "CPU Vendor: " + std::string(vendor) + "\n"; + } + char brand[1024]; + size_t brandSize = sizeof(brand); + error = sysctlbyname("machdep.cpu.brand_string", &brand, &brandSize, nullptr, 0); + if (!error) { + hwInfo += "CPU Model: " + std::string(brand) + "\n"; + } +#endif + + g_ConsoleMan.PrintString(hwInfo); + +#ifdef __unix__ + struct utsname unameData; + if (uname(&unameData) == 0) { + std::string osInfo = "uname: " + std::string(unameData.sysname) + " " + std::string(unameData.release) + " " + std::string(unameData.version); + g_ConsoleMan.PrintString(osInfo); + } +#endif + +#ifdef _MSC_VER + g_ConsoleMan.PrintString("OS: Windows"); +#endif + +#ifdef __linux__ + // Read distribution info from /etc/os-release + if (std::filesystem::exists("/etc/os-release")) { + std::ifstream osReleaseFile("/etc/os-release"); + if (osReleaseFile.is_open()) { + std::string line; + while (std::getline(osReleaseFile, line)) { + if (line.find("PRETTY_NAME") != std::string::npos) { + g_ConsoleMan.PrintString("OS: " + line.substr(line.find_first_of('"') + 1, line.find_last_of('"') - line.find_first_of('"') - 1)); + break; + } + } + osReleaseFile.close(); + } + } else { + g_ConsoleMan.PrintString("OS: Unknown Linux (/etc/os-release not found)"); + } +#endif + +#if defined(__APPLE__) && defined(__MACH__) + char osType[1024]; + size_t osTypeSize = sizeof(osType); + error = sysctlbyname("kern.ostype", &osType, &osTypeSize, nullptr, 0); + if (!error) { + g_ConsoleMan.PrintString("OS Type: " + std::string(osType)); + } + + char osRelease[1024]; + size_t osReleaseSize = sizeof(osRelease); + error = sysctlbyname("kern.osrelease", &osRelease, &osReleaseSize, nullptr, 0); + if (!error) { + g_ConsoleMan.PrintString("OS Release: " + std::string(osRelease)); + } + + char osVersion[1024]; + size_t osVersionSize = sizeof(osVersion); + error = sysctlbyname("kern.osversion", &osVersion, &osVersionSize, nullptr, 0); + if (!error) { + g_ConsoleMan.PrintString("OS Version: " + std::string(osVersion)); + } +#endif +} + bool RTEError::DumpAbortScreen() { int success = -1; if (glReadPixels != nullptr) { @@ -442,9 +573,10 @@ bool RTEError::DumpAbortSave() { void RTEError::FormatFunctionSignature(std::string& symbolName) { // TODO: Expand this with more dumb signatures, or make something that makes more sense. - static const std::array, 3> stlSigs{{{std::regex("( >)"), ">"}, - {std::regex("(std::basic_string,std::allocator>)"), "std::string"}, - {std::regex("(class ?std::basic_string,class ?std::allocator>)"), "std::string"}}}; + static const std::array, 3> stlSigs{ + {{std::regex("( >)"), ">"}, + {std::regex("(std::basic_string,std::allocator>)"), "std::string"}, + {std::regex("(class ?std::basic_string,class ?std::allocator>)"), "std::string"}}}; for (const auto& [fullSig, simpleSig]: stlSigs) { symbolName = std::regex_replace(symbolName, fullSig, simpleSig); } diff --git a/Source/System/RTEError.h b/Source/System/RTEError.h index 30a4c0d48..d06c8b49b 100644 --- a/Source/System/RTEError.h +++ b/Source/System/RTEError.h @@ -66,6 +66,15 @@ namespace RTE { /// @return Whether to abort, or ignore the assert and continue execution. static bool ShowAssertMessageBox(const std::string& message); + /// Prints details on the user hardware to the abort log. + /// @remark + /// Included details: + /// OpenGL version string, OpenGL GPU vendor string, OpenGL Renderer string. + /// CPU vendor and brand string (as reported by cpuid on windows and linux or sysctl on macos). + /// linux: uname sysname, release, version. + /// macos: sysctl kern.osrelease, kern.ostype, kern.osversion. + static void DumpHardwareInfo(); + /// Saves the current frame to a file. /// @return Whether the file was saved successfully.@return static bool DumpAbortScreen();