/* Copyright (c) 2013 yvt Portion of the code is based on Serverbrowser.cpp. This file is part of OpenSpades. OpenSpades 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 3 of the License, or (at your option) any later version. OpenSpades 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 OpenSpades. If not, see . */ #include #include "StartupScreenHelper.h" #include "StartupScreen.h" #include #include #include #include "../Imports/OpenGL.h" //for gpu info #include "../Imports/SDL.h" SPADES_SETTING(r_bloom, ""); SPADES_SETTING(r_lens, ""); SPADES_SETTING(r_cameraBlur, ""); SPADES_SETTING(r_softParticles, ""); SPADES_SETTING(r_mapSoftShadow, ""); SPADES_SETTING(r_modelShadows, ""); SPADES_SETTING(r_radiosity, ""); SPADES_SETTING(r_dlights, ""); SPADES_SETTING(r_water, ""); SPADES_SETTING(r_multisamples, ""); SPADES_SETTING(r_fxaa, ""); SPADES_SETTING(r_depthBits, ""); SPADES_SETTING(r_colorBits, ""); SPADES_SETTING(r_videoWidth, ""); SPADES_SETTING(r_videoHeight, ""); SPADES_SETTING(r_fullscreen, ""); SPADES_SETTING(r_fogShadow, ""); SPADES_SETTING(r_lensFlare, ""); SPADES_SETTING(r_lensFlareDynamic, ""); SPADES_SETTING(r_blitFramebuffer, ""); SPADES_SETTING(r_srgb, ""); SPADES_SETTING(r_shadowMapSize, ""); SPADES_SETTING(s_maxPolyphonics, ""); SPADES_SETTING(s_eax, ""); SPADES_SETTING(r_maxAnisotropy, ""); SPADES_SETTING(r_colorCorrection, ""); SPADES_SETTING(r_physicalLighting, ""); SPADES_SETTING(r_occlusionQuery, ""); SPADES_SETTING(r_depthOfField, ""); SPADES_SETTING(r_vsync, ""); SPADES_SETTING(r_renderer, ""); SPADES_SETTING(r_swUndersampling, ""); namespace spades { namespace gui { StartupScreenHelper::StartupScreenHelper(): scr(nullptr), shaderHighCapable(false), postFilterHighCapable(false), particleHighCapable(false) { SPADES_MARK_FUNCTION(); } StartupScreenHelper::~StartupScreenHelper() { SPADES_MARK_FUNCTION(); } void StartupScreenHelper::StartupScreenDestroyed() { SPADES_MARK_FUNCTION(); scr = nullptr; } void StartupScreenHelper::ExamineSystem() { SPADES_MARK_FUNCTION(); // check GL capabilities SPLog("Performing ecapability query"); int idDisplay = 0; int numDisplayMode = SDL_GetNumDisplayModes(idDisplay); SDL_DisplayMode mode; modes.clear(); if(numDisplayMode > 0){ for(int i = 0; i < numDisplayMode; i++) { SDL_GetDisplayMode(idDisplay, i, &mode); if(mode.w < 800 || mode.h < 600) continue; modes.push_back(spades::IntVector3::Make(mode.w, mode.h, 0)); SPLog("Video Mode Found: %dx%d", mode.w, mode.h); } }else{ SPLog("Failed to get video mode list. Presetting default list"); modes.push_back(spades::IntVector3::Make(800, 600, 0)); modes.push_back(spades::IntVector3::Make(1024, 768, 0)); modes.push_back(spades::IntVector3::Make(1280, 720, 0)); modes.push_back(spades::IntVector3::Make(1920, 1080, 0)); } bool capable = true; SDL_Window *window = SDL_CreateWindow("OpenSpades: Please wait...", 1, 1, 1, 1, SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS); if(window == nullptr) { SPLog("Failed to create SDL window: %s", SDL_GetError()); } SDL_GLContext context = window ? SDL_GL_CreateContext(window) : nullptr; if(window != nullptr && context == nullptr) { SPLog("Failed to create OpenGL context: %s", SDL_GetError()); } if(!context){ // OpenGL initialization failed! std::string err = SDL_GetError(); SPLog("SDL_SetVideoMode failed: %s", err.c_str()); AddReport("OpenGL-capable graphics accelerator is unavailable.", MakeVector4(1.f, 0.5f, 0.5f, 1.f)); AddReport(); AddReport("OpenGL/SDL couldn't be initialized."); AddReport("Falling back to the software renderer."); AddReport(); AddReport("Message from SDL:", MakeVector4(1.f, 1.f, 1.f, 0.7f)); AddReport(err, MakeVector4(1.f, 1.f, 1.f, 0.7f)); capable = false; shaderHighCapable = false; postFilterHighCapable = false; particleHighCapable = false; }else{ SDL_GL_MakeCurrent(window, context); shaderHighCapable = true; postFilterHighCapable = true; particleHighCapable = true; const char *str; GLint maxTextureSize; GLint max3DTextureSize; GLint maxCombinedTextureUnits; GLint maxVertexTextureUnits; GLint maxVaryingComponents; SPLog("--- OpenGL Renderer Info ---"); AddReport("OpenGL-capable graphics accelerator is available."); if((str = (const char*)glGetString(GL_VENDOR)) != NULL) { SPLog("Vendor: %s", str); AddReport(std::string("Vendor: ") + str, MakeVector4(1.f, 1.f, 1.f, 0.7f)); } if((str = (const char *)glGetString(GL_RENDERER)) != NULL) { SPLog("Name: %s", str); AddReport(std::string("Name: ") + str, MakeVector4(1.f, 1.f, 1.f, 0.7f)); } if((str = (const char *)glGetString(GL_VERSION)) != NULL) { AddReport(std::string("Version: ") + str, MakeVector4(1.f, 1.f, 1.f, 0.7f)); SPLog("Version: %s", str); } if((str = (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION)) != NULL) { AddReport(std::string("GLSL Version: ") + str, MakeVector4(1.f, 1.f, 1.f, 0.7f)); SPLog("Shading Language Version: %s", str); } AddReport(); maxTextureSize = 0; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); if(maxTextureSize > 0) { SPLog("Max Texture Size: %d", (int)maxTextureSize); } max3DTextureSize = 0; glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &max3DTextureSize); if(max3DTextureSize > 0) { SPLog("Max 3D Texture Size: %d", (int)max3DTextureSize); } maxCombinedTextureUnits = 0; glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxCombinedTextureUnits); if(maxCombinedTextureUnits > 0) { SPLog("Max Combined Texture Image Units: %d", (int)maxCombinedTextureUnits); } maxVertexTextureUnits = 0; glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextureUnits); if(maxVertexTextureUnits > 0) { SPLog("Max Vertex Texture Image Units: %d", (int)maxVertexTextureUnits); } maxVaryingComponents = 0; glGetIntegerv(GL_MAX_VARYING_COMPONENTS, &maxVaryingComponents); if(maxVaryingComponents > 0) { SPLog("Max Varying Components: %d", (int)maxVaryingComponents); } str = (const char*)glGetString(GL_EXTENSIONS); std::string extensions; if(str) extensions = str; const char * const requiredExtensions[] = { "GL_ARB_multitexture", "GL_ARB_shader_objects", "GL_ARB_shading_language_100", "GL_ARB_texture_non_power_of_two", "GL_ARB_vertex_buffer_object", "GL_EXT_framebuffer_object", NULL }; SPLog("--- Extensions ---"); std::vector strs = spades::Split(str, " "); for(size_t i = 0; i < strs.size(); i++) { SPLog("%s", strs[i].c_str()); } SPLog("------------------"); for(size_t i = 0; requiredExtensions[i]; i++) { const char *ex = requiredExtensions[i]; if(extensions.find(ex) == std::string::npos) { // extension not found AddReport(std::string(ex) + " is NOT SUPPORTED!", MakeVector4(1.f, 0.5f, 0.5f, 1.f)); capable = false; } } // non-requred extensions if(extensions.find("GL_ARB_framebuffer_sRGB") == std::string::npos) { if(r_srgb) { r_srgb = 0; SPLog("Disabling r_srgb: no GL_ARB_framebuffer_sRGB"); } incapableConfigs.insert (std::make_pair ("r_srgb", [](std::string value) -> std::string { if(std::stoi(value) != 0) { return "SRGB framebuffer is disabled because your video card doesn't support GL_ARB_framebuffer_sRGB."; }else{ return std::string(); } })); AddReport("GL_ARB_framebuffer_sRGB is NOT SUPPORTED", MakeVector4(1.f, 1.f, 0.5f, 1.f)); AddReport(" r_srgb is disabled.", MakeVector4(1.f, 1.f, 1.f, 0.7f)); } if(extensions.find("GL_EXT_framebuffer_blit") == std::string::npos) { if(r_blitFramebuffer) { r_blitFramebuffer = 0; SPLog("Disabling r_blitFramebuffer: no GL_EXT_framebuffer_blit"); } if(r_multisamples) { r_multisamples = 0; SPLog("Disabling r_multisamples: no GL_EXT_framebuffer_blit"); } incapableConfigs.insert(std::make_pair("r_blitFramebuffer", [](std::string value) -> std::string { if(std::stoi(value) != 0) { return "r_blitFramebuffer is disabled because your video card doesn't support GL_EXT_framebuffer_blit."; }else{ return std::string(); } })); AddReport("GL_EXT_framebuffer_blit is NOT SUPPORTED", MakeVector4(1.f, 1.f, 0.5f, 1.f)); AddReport(" r_blitFramebuffer is disabled.", MakeVector4(1.f, 1.f, 1.f, 0.7f)); } if(extensions.find("GL_EXT_texture_filter_anisotropic") == std::string::npos) { if((float)r_maxAnisotropy > 1.1f) { r_maxAnisotropy = 1; SPLog("Setting r_maxAnisotropy to 1: no GL_EXT_texture_filter_anisotropic"); } incapableConfigs.insert (std::make_pair ("r_maxAnisotropy", [](std::string value) -> std::string { if(std::stof(value) > 1.001f) { return "Anisotropic texture filtering is disabled because your video card doesn't support GL_EXT_texture_filter_anisotropic."; }else{ return std::string(); } })); AddReport("GL_EXT_texture_filter_anisotropic is NOT SUPPORTED", MakeVector4(1.f, 1.f, 0.5f, 1.f)); AddReport(" r_maxAnisotropy is disabled.", MakeVector4(1.f, 1.f, 1.f, 0.7f)); } if(extensions.find("GL_ARB_occlusion_query") == std::string::npos) { if(r_occlusionQuery) { r_occlusionQuery = 0; SPLog("Disabling r_occlusionQuery: no GL_ARB_occlusion_query"); } incapableConfigs.insert (std::make_pair ("r_occlusionQuery", [](std::string value) -> std::string { if(std::stoi(value)) { return "Occlusion query is disabled because your video card doesn't support GL_ARB_occlusion_query."; }else{ return std::string(); } })); AddReport("GL_ARB_occlusion_query is NOT SUPPORTED", MakeVector4(1.f, 1.f, 0.5f, 1.f)); AddReport(" r_occlusionQuery is disabled.", MakeVector4(1.f, 1.f, 1.f, 0.7f)); } if(extensions.find("GL_NV_conditional_render") == std::string::npos) { if(r_occlusionQuery) { r_occlusionQuery = 0; SPLog("Disabling r_occlusionQuery: no GL_NV_conditional_render"); } incapableConfigs.insert (std::make_pair ("r_occlusionQuery", [](std::string value) -> std::string { if(std::stoi(value)) { return "Occlusion query is disabled because your video card doesn't support GL_NV_conditional_render."; }else{ return std::string(); } })); AddReport("GL_NV_conditional_render is NOT SUPPORTED", MakeVector4(1.f, 1.f, 0.5f, 1.f)); AddReport(" r_occlusionQuery is disabled.", MakeVector4(1.f, 1.f, 1.f, 0.7f)); } AddReport("Max Texture Size: " + std::to_string(maxTextureSize), MakeVector4(1.f, 1.f, 1.f, 0.7f)); if(maxTextureSize < 1024) { capable = false; AddReport(" TOO SMALL (1024 requred)", MakeVector4(1.f, 0.5f, 0.5f, 1.f)); } if((int)r_shadowMapSize > maxTextureSize) { SPLog("Changed r_shadowMapSize from %d to %d: too small GL_MAX_TEXTURE_SIZE", (int)r_shadowMapSize, maxTextureSize); r_shadowMapSize = maxTextureSize; } AddReport("Max 3D Texture Size: " + std::to_string(max3DTextureSize), MakeVector4(1.f, 1.f, 1.f, 0.7f)); if(max3DTextureSize < 512) { AddReport(" Global Illumination is disabled (512 required)", MakeVector4(1.f, 1.f, 0.5f, 1.f)); incapableConfigs.insert (std::make_pair ("r_radiosity", [](std::string value) -> std::string { if(std::stoi(value)) { return "Global illumination is disabled because your video card doesn't support a 3D texture of at least 512x512x64."; }else{ return std::string(); } })); if(r_radiosity) { r_radiosity = 0; SPLog("Disabling r_radiosity: too small GL_MAX_3D_TEXTURE_SIZE"); } } AddReport("Max Combined Texture Image Units: " + std::to_string(maxCombinedTextureUnits), MakeVector4(1.f, 1.f, 1.f, 0.7f)); if(maxCombinedTextureUnits < 12) { AddReport(" Global Illumination is disabled (12 required)", MakeVector4(1.f, 1.f, 0.5f, 1.f)); incapableConfigs.insert (std::make_pair ("r_radiosity", [](std::string value) -> std::string { if(std::stoi(value)) { return "Global illumination is disabled because your video card supports too few combined texture image units."; }else{ return std::string(); } })); if(r_radiosity) { r_radiosity = 0; SPLog("Disabling r_radiosity: too small GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS"); } } AddReport("Max Vertex Texture Image Units: " + std::to_string(maxVertexTextureUnits), MakeVector4(1.f, 1.f, 1.f, 0.7f)); if(maxVertexTextureUnits < 3) { AddReport(" Water 2 is disabled (3 required)", MakeVector4(1.f, 1.f, 0.5f, 1.f)); shaderHighCapable = false; incapableConfigs.insert (std::make_pair ("r_water", [](std::string value) -> std::string { if(std::stoi(value) >= 2) { return "Water 2 is disabled because your video card supports too few vertex texture image units."; }else{ return std::string(); } })); if((int)r_water >= 2) { r_water = 1; SPLog("Disabling Water 2: too small GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS"); } } AddReport("Max Varying Components: " + std::to_string(maxVaryingComponents), MakeVector4(1.f, 1.f, 1.f, 0.7f)); if(maxVaryingComponents < 37) { AddReport(" Shaded Particle is disabled (37 required)", MakeVector4(1.f, 1.f, 0.5f, 1.f)); particleHighCapable = false; incapableConfigs.insert (std::make_pair ("r_softParticles", [](std::string value) -> std::string { if(std::stoi(value) >= 2) { return "Shaded particle is disabled because your video card supports too few varying fragment shader input components."; }else{ return std::string(); } })); if((int)r_softParticles >= 2) { r_softParticles = 1; SPLog("Disabling shaded particle: too small GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS"); } } AddReport(); if(capable){ AddReport("Your video card supports all " "required OpenGL extensions/features.", MakeVector4(0.5f, 1.f, 0.5f, 1.f)); }else{ AddReport("Your video card/driver doesn't support " "at least one of required OpenGL extensions/features." " Falling back to the software renderer.", MakeVector4(1.f, 0.5f, 0.5f, 1.f)); } SDL_GL_DeleteContext(context); SDL_DestroyWindow(window); SPLog("SDL Capability Query Window finalized"); } SPLog("OpenGL driver is OpenSpades capable: %s", capable ? "YES": "NO"); openGLCapable = capable; if(!openGLCapable) { if(spades::EqualsIgnoringCase(r_renderer, "gl")){ SPLog("Switched to software renderer"); r_renderer = "sw"; } incapableConfigs.insert (std::make_pair ("r_renderer", [](std::string value) -> std::string { if(spades::EqualsIgnoringCase(value, "gl")) { return "OpenGL renderer is disabled because " "your video card/driver doesn't support " "at least one of required OpenGL extensions/features."; }else{ return std::string(); } })); } } void StartupScreenHelper::Start() { if(scr == nullptr){ return; } scr->Start(); } int StartupScreenHelper::GetNumVideoModes() { return static_cast(modes.size()); } int StartupScreenHelper::GetVideoModeWidth(int index) { if(index < 0 || index >= GetNumVideoModes()) SPInvalidArgument("index"); return modes[index].x; } int StartupScreenHelper::GetVideoModeHeight(int index) { if(index < 0 || index >= GetNumVideoModes()) SPInvalidArgument("index"); return modes[index].y; } int StartupScreenHelper::GetNumReportLines() { return static_cast(reportLines.size()); } std::string StartupScreenHelper::GetReportLineText(int index) { if(index < 0 || index >= GetNumReportLines()) SPInvalidArgument("index"); return reportLines[index].text; } Vector4 StartupScreenHelper::GetReportLineColor(int index) { if(index < 0 || index >= GetNumReportLines()) SPInvalidArgument("index"); return reportLines[index].color; } void StartupScreenHelper::AddReport(const std::string& text, Vector4 color) { ReportLine l = {text, color}; reportLines.push_back(l); report += text; report += '\n'; } std::string StartupScreenHelper::CheckConfigCapability(const std::string &cfg, const std::string &value) { auto range = incapableConfigs.equal_range(cfg); std::string ret; bool hasMulti = false; for(auto it = range.first; it != range.second; it++) { auto& f = it->second; auto err = f(value); if(err.size() > 0) { if(ret.size() == 0) { ret = err; }else{ if(!hasMulti) { ret = "- " + ret; hasMulti = true; } ret += "\n- " + err; } } } return ret; } } }