514 lines
18 KiB
C++
514 lines
18 KiB
C++
//
|
|
// SuperTuxKart - a fun racing game with go-kart
|
|
// Copyright (C) 2014-2015 Joerg Henrichs
|
|
//
|
|
// This program 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.
|
|
//
|
|
// This program 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 this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
#include "graphics/graphics_restrictions.hpp"
|
|
|
|
#include "io/file_manager.hpp"
|
|
#include "io/xml_node.hpp"
|
|
#include "utils/log.hpp"
|
|
#include "utils/no_copy.hpp"
|
|
#include "utils/string_utils.hpp"
|
|
#include "utils/types.hpp"
|
|
|
|
#include <algorithm>
|
|
|
|
namespace GraphicsRestrictions
|
|
{
|
|
|
|
class Rule;
|
|
namespace Private
|
|
{
|
|
/** Stores for each grpahics restriction if it's enabled or not. */
|
|
std::vector<bool> m_all_graphics_restriction;
|
|
|
|
/** The list of names used in the XML file for the graphics
|
|
* restriction types. They must be in the same order as the types. */
|
|
|
|
const char *m_names_of_restrictions[] = {
|
|
"UniformBufferObject",
|
|
"GeometryShader4",
|
|
"DrawIndirect",
|
|
"TextureView",
|
|
"TextureStorage",
|
|
"ImageLoadStore",
|
|
"BaseInstance",
|
|
"ComputeShader",
|
|
"ShaderStorageBufferObject",
|
|
"MultiDrawIndirect",
|
|
"ShaderAtomicCounters",
|
|
"BufferStorage",
|
|
"BindlessTexture",
|
|
"TextureCompressionS3TC",
|
|
"AMDVertexShaderLayer",
|
|
"DriverRecentEnough",
|
|
"HighDefinitionTextures",
|
|
"AdvancedPipeline",
|
|
"FramebufferSRGBWorking",
|
|
"FramebufferSRGBCapable",
|
|
"GI",
|
|
};
|
|
} // namespace Private
|
|
using namespace Private;
|
|
|
|
/** Returns the graphics restrictions type for a string, or
|
|
* GR_COUNT if the name is not found. */
|
|
GraphicsRestrictionsType getTypeForName(const std::string &name)
|
|
{
|
|
unsigned int i = 0;
|
|
while (m_names_of_restrictions[i] != NULL)
|
|
{
|
|
if (name == m_names_of_restrictions[i])
|
|
return (GraphicsRestrictionsType)i;
|
|
i++;
|
|
}
|
|
return GR_COUNT;
|
|
} // getTypeForName
|
|
|
|
// ============================================================================
|
|
/** A small utility class to manage and compare version tuples.
|
|
*/
|
|
class Version
|
|
{
|
|
private:
|
|
/** The array containing the version number. */
|
|
std::vector<uint32_t> m_version;
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Searches for the first number in the string, then converts the rest of the
|
|
* string into a tuple. E.g. "Build 12.34.56" --> [12,34,56].
|
|
*/
|
|
void convertVersionString(const std::string &version)
|
|
{
|
|
std::string s = version;
|
|
std::string::iterator p = s.begin();
|
|
while( (p !=s.end()) && ((*p<'0') || (*p>'9')) )
|
|
p++;
|
|
s.erase(s.begin(), p);
|
|
m_version = StringUtils::splitToUInt(s, '.');
|
|
} // convertVersionString
|
|
|
|
public:
|
|
// ------------------------------------------------------------------------
|
|
/** Dummy default constructor. */
|
|
Version() {}
|
|
// ------------------------------------------------------------------------
|
|
/** Simple constructor which takes a string with "." separated numbers.
|
|
*/
|
|
Version(const std::string &version)
|
|
{
|
|
convertVersionString(version);
|
|
} // Version(std::string)
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** Create a version instance from the driver and car name. For example for
|
|
* an Intel HD3000 card the string is "3.1.0 - Build 9.17.10.3517" it would
|
|
* create [9,17,10,3517] - i.e. it takes the vendor info into account.
|
|
* \param driver_version The GL_VERSION string (i.e. opengl and version
|
|
* number).
|
|
* \param card_name The GL_RENDERER string (i.e. graphics card).
|
|
*/
|
|
Version(const std::string &driver_version, const std::string &card_name)
|
|
{
|
|
m_version.clear();
|
|
|
|
// Mesa needs to be tested first, otherwise (if testing for card name
|
|
// further down) it would be detected as a non-mesa driver.
|
|
if (driver_version.find("Mesa") != std::string::npos)
|
|
{
|
|
std::string driver;
|
|
// Try to force the driver name to be in a standard way by removing
|
|
// optional strings
|
|
driver = StringUtils::replace(driver_version, "-devel", "");
|
|
driver = StringUtils::replace(driver, "(Core Profile) ", "");
|
|
std::vector<std::string> l = StringUtils::split(driver, ' ');
|
|
if (l.size() > 2)
|
|
{
|
|
// driver can be: "1.4 (3.0 Mesa 10.1.0)" --> l[3] must be used
|
|
if (l[2] != "Mesa")
|
|
convertVersionString(l[2]);
|
|
else
|
|
convertVersionString(l[3]);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Intel card: driver version = "3.1.0 - Build 9.17.10.3517"
|
|
// ---------------------------------------------------------
|
|
if (StringUtils::startsWith(card_name, "Intel"))
|
|
{
|
|
std::vector<std::string> s = StringUtils::split(driver_version, '-');
|
|
if (s.size() == 2)
|
|
{
|
|
convertVersionString(s[1]);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Nvidia: driver_version = "4.3.0 NVIDIA 340.58"
|
|
// ----------------------------------------------
|
|
if (driver_version.find("NVIDIA") != std::string::npos)
|
|
{
|
|
std::vector<std::string> s = StringUtils::split(driver_version, ' ');
|
|
if (s.size() == 3)
|
|
{
|
|
convertVersionString(s[2]);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
// ATI: some drivers use e.g.: "4.1 ATI-1.24.38"
|
|
if (driver_version.find("ATI-") != std::string::npos)
|
|
{
|
|
std::string driver;
|
|
// Try to force the driver name to be in a standard way by removing
|
|
// optional strings
|
|
driver = StringUtils::replace(driver_version, "ATI-", "");
|
|
std::vector<std::string> s = StringUtils::split(driver, ' ');
|
|
convertVersionString(s[1]);
|
|
return;
|
|
}
|
|
|
|
// AMD: driver_version = "4.3.13283 Core Profile/Debug Context 14.501.1003.0"
|
|
// ----------------------------------------------
|
|
if (card_name.find("AMD") != std::string::npos)
|
|
{
|
|
std::vector<std::string> s = StringUtils::split(driver_version, ' ');
|
|
if (s.size() == 5)
|
|
{
|
|
convertVersionString(s[4]);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
// ATI: other drivers use "4.0.10188 Core Profile Context"
|
|
if (card_name.find("ATI") != std::string::npos)
|
|
{
|
|
std::vector<std::string> s = StringUtils::split(driver_version, ' ');
|
|
convertVersionString(s[0]);
|
|
return;
|
|
}
|
|
|
|
Log::warn("Graphics", "Can not find version for '%s' '%s' - ignored.",
|
|
driver_version.c_str(), card_name.c_str());
|
|
|
|
} // Version
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** Compares two version numbers. Equal returns true if the elements are
|
|
* identical.
|
|
*/
|
|
bool operator== (const Version &other) const
|
|
{
|
|
if (m_version.size() != other.m_version.size()) return false;
|
|
for(unsigned int i=0; i<m_version.size(); i++)
|
|
if(other.m_version[i]!=m_version[i]) return false;
|
|
return true;
|
|
} // operator==
|
|
// ------------------------------------------------------------------------
|
|
/** Compares two version numbers. Equal returns true if the elements are
|
|
* identical.
|
|
*/
|
|
bool operator!= (const Version &other) const
|
|
{
|
|
return !this->operator==(other);
|
|
} // operator!=
|
|
// ------------------------------------------------------------------------
|
|
/** If *this < other. */
|
|
bool operator< (const Version &other) const
|
|
{
|
|
unsigned int min_n = std::min(m_version.size(), other.m_version.size());
|
|
for (unsigned int i = 0; i<min_n; i++)
|
|
{
|
|
if (m_version[i] > other.m_version[i]) return false;
|
|
if (m_version[i] < other.m_version[i]) return true;
|
|
}
|
|
if (m_version.size() >= other.m_version.size())
|
|
return false;
|
|
else
|
|
return true;
|
|
} // operator<
|
|
// ------------------------------------------------------------------------
|
|
/** If *this <= other. */
|
|
bool operator<= (const Version &other) const
|
|
{
|
|
unsigned int min_n = std::min(m_version.size(), other.m_version.size());
|
|
for (unsigned int i = 0; i<min_n; i++)
|
|
{
|
|
if (m_version[i] > other.m_version[i]) return false;
|
|
if (m_version[i] < other.m_version[i]) return true;
|
|
}
|
|
if (m_version.size() > other.m_version.size())
|
|
return false;
|
|
else
|
|
return true;
|
|
} // operator<=
|
|
|
|
}; // class Version
|
|
// ============================================================================
|
|
class Rule : public NoCopy
|
|
{
|
|
private:
|
|
/** Operators to test for a card. */
|
|
enum {CARD_IS, CARD_CONTAINS} m_card_test;
|
|
|
|
/** Name of the card for which this rule applies. */
|
|
std::string m_card_name;
|
|
|
|
/** Operators to test version numbers with. */
|
|
enum {VERSION_IGNORE, VERSION_EQUAL, VERSION_LESS,
|
|
VERSION_LESS_EQUAL} m_version_test;
|
|
|
|
/** Driver version for which this rule applies. */
|
|
Version m_driver_version;
|
|
|
|
/** For which OS this rule applies. */
|
|
std::string m_os;
|
|
|
|
/** Which options to disable. */
|
|
std::vector<std::string> m_disable_options;
|
|
public:
|
|
Rule(const XMLNode *rule)
|
|
{
|
|
m_version_test = VERSION_IGNORE;
|
|
if(rule->get("is", &m_card_name))
|
|
{
|
|
m_card_test = CARD_IS;
|
|
}
|
|
else if(rule->get("contains", &m_card_name))
|
|
{
|
|
m_card_test = CARD_CONTAINS;
|
|
}
|
|
|
|
rule->get("os", &m_os);
|
|
|
|
std::string s;
|
|
if(rule->get("version", &s) && s.size()>1)
|
|
{
|
|
if(s.substr(0, 2)=="<=")
|
|
{
|
|
m_version_test = VERSION_LESS_EQUAL;
|
|
s = s.substr(2, s.size());
|
|
}
|
|
else if(s[0]=='<')
|
|
{
|
|
m_version_test = VERSION_LESS;
|
|
s.erase(s.begin());
|
|
}
|
|
else if(s[0]=='=')
|
|
{
|
|
m_version_test = VERSION_EQUAL;
|
|
s.erase(s.begin());
|
|
}
|
|
else
|
|
{
|
|
Log::warn("Graphics", "Invalid verison '%s' found - ignored.",
|
|
s.c_str());
|
|
}
|
|
m_driver_version = Version(s);
|
|
} // has version
|
|
|
|
if(rule->get("disable", &s))
|
|
m_disable_options = StringUtils::split(s, ' ');
|
|
} // Rule
|
|
// ------------------------------------------------------------------------
|
|
bool applies(const std::string &card, const Version &version) const
|
|
{
|
|
// Test for OS
|
|
// -----------
|
|
if(m_os.size()>0)
|
|
{
|
|
#ifdef __linux__
|
|
if(m_os!="linux") return false;
|
|
#endif
|
|
#ifdef WIN32
|
|
if(m_os!="windows") return false;
|
|
#endif
|
|
#ifdef __APPLE__
|
|
if(m_os!="osx") return false;
|
|
#endif
|
|
#ifdef BSD
|
|
if(m_os!="bsd") return false;
|
|
#endif
|
|
} // m_os.size()>0
|
|
|
|
// Test for card
|
|
// -------------
|
|
switch(m_card_test)
|
|
{
|
|
case CARD_IS:
|
|
if(card!=m_card_name) return false;
|
|
break;
|
|
case CARD_CONTAINS:
|
|
if(card.find(m_card_name)==std::string::npos)
|
|
return false;
|
|
break;
|
|
} // switch m_card_test
|
|
|
|
// Test for driver version
|
|
// -----------------------
|
|
switch(m_version_test)
|
|
{
|
|
case VERSION_IGNORE: break; // always true
|
|
case VERSION_EQUAL: if(!(version==m_driver_version)) return false;
|
|
break;
|
|
case VERSION_LESS_EQUAL:
|
|
if (!(version <= m_driver_version)) return false;
|
|
break;
|
|
case VERSION_LESS:
|
|
if (!(version < m_driver_version )) return false;
|
|
break;
|
|
} // switch m_version_test
|
|
return true;
|
|
// -----------------------------------------------
|
|
} // applies
|
|
|
|
// ------------------------------------------------------------------------
|
|
/** Returns a list of options to disable. */
|
|
const std::vector<std::string>& getRestrictions() const
|
|
{
|
|
return m_disable_options;
|
|
} // getRestrictions
|
|
// ------------------------------------------------------------------------
|
|
}; // class Rule
|
|
|
|
// ============================================================================
|
|
/** Very rudimentary and brute unit testing. Better than nothing :P
|
|
*/
|
|
void unitTesting()
|
|
{
|
|
assert(Version("1") == Version("1"));
|
|
assert(Version("1") != Version("2"));
|
|
assert(Version("1") <= Version("2"));
|
|
assert(Version("1") < Version("2"));
|
|
assert(Version("1.2.3") < Version("2"));
|
|
assert(Version("1.2.3") < Version("1.3"));
|
|
assert(Version("1.2.3") < Version("1.2.4"));
|
|
assert(Version("1.2.3") < Version("1.2.3.1"));
|
|
assert(Version("1.2.3") <= Version("2"));
|
|
assert(Version("1.2.3") <= Version("1.3"));
|
|
assert(Version("1.2.3") <= Version("1.2.4"));
|
|
assert(Version("1.2.3") <= Version("1.2.3.1"));
|
|
assert(Version("1.2.3") <= Version("1.2.3"));
|
|
assert(Version("1.2.3") == Version("1.2.3"));
|
|
assert(Version("10.3") < Version("10.3.2"));
|
|
assert(Version("10.3") <= Version("10.3.2"));
|
|
assert(!(Version("10.3.2") < Version("10.3")));
|
|
assert(!(Version("10.3.2") <= Version("10.3")));
|
|
assert(Version("3.3 NVIDIA-10.0.19 310.90.10.05b1",
|
|
"NVIDIA GeForce GTX 680MX OpenGL Engine")
|
|
== Version("310.90.10.5") );
|
|
|
|
assert(Version("4.1 NVIDIA-10.0.43 310.41.05f01",
|
|
"NVIDIA GeForce GTX 780M OpenGL Engine")
|
|
== Version("310.41.05"));
|
|
|
|
assert(Version("3.1 (Core Profile) Mesa 10.3.0",
|
|
"Mesa DRI Mobile Intel\u00ae GM45 Express Chipset")
|
|
== Version("10.3.0") );
|
|
assert(Version("3.3 (Core Profile) Mesa 10.5.0-devel",
|
|
"Gallium 0.4 on NVC1")
|
|
== Version("10.5.0") );
|
|
assert(Version("3.3 (Core Profile) Mesa 10.5.0-devel",
|
|
"Mesa DRI Intel(R) Sandybridge Mobile")
|
|
== Version("10.5.0") );
|
|
assert(Version("2.1 Mesa 10.5.0-devel (git-82e919d)",
|
|
"Gallium 0.4 on i915 (chipse)")
|
|
== Version("10.5.0") );
|
|
assert(Version("1.4 (3.0 Mesa 10.1.0)",
|
|
"Mesa DRI Intel(R) Ivybridge Mobile")
|
|
== Version("10.1.0"));
|
|
assert(Version("4.3.13283 Core Profile Context 14.501.1003.0",
|
|
"AMD Radeon R9 200 Series")
|
|
== Version("14.501.1003.0"));
|
|
assert(Version("4.0.10188 Core Profile Context",
|
|
"ATI Radeon HD 5400 Series")
|
|
== Version("4.0.10188"));
|
|
assert(Version("4.1 ATI-1.24.38", "AMD Radeon HD 6970M OpenGL Engine")
|
|
== Version("1.24.38"));
|
|
|
|
} // unitTesting
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Reads in the graphical restriction file.
|
|
* \param driver_version The GL_VERSION string (i.e. opengl and version
|
|
* number).
|
|
* \param card_name The GL_RENDERER string (i.e. graphics card).
|
|
*/
|
|
void init(const std::string &driver_version,
|
|
const std::string &card_name)
|
|
{
|
|
for (unsigned int i = 0; i < GR_COUNT; i++)
|
|
m_all_graphics_restriction.push_back(false);
|
|
|
|
std::string filename =
|
|
file_manager->getUserConfigFile("graphical_restrictions.xml");
|
|
if (!file_manager->fileExists(filename))
|
|
filename = file_manager->getAsset("graphical_restrictions.xml");
|
|
const XMLNode *rules = file_manager->createXMLTree(filename);
|
|
if (!rules)
|
|
{
|
|
Log::warn("Graphics", "Could not find graphical_restrictions.xm");
|
|
return;
|
|
}
|
|
if (rules->getName() != "graphical-restrictions")
|
|
{
|
|
delete rules;
|
|
Log::warn("Graphics", "'%s' did not contain graphical-restrictions tag",
|
|
filename.c_str());
|
|
return;
|
|
}
|
|
|
|
Version version(driver_version, card_name);
|
|
for (unsigned int i = 0; i<rules->getNumNodes(); i++)
|
|
{
|
|
const XMLNode *xml_rule = rules->getNode(i);
|
|
if (xml_rule->getName() != "card")
|
|
{
|
|
Log::warn("Graphics", "Incorrect node '%s' found in '%s' - ignored.",
|
|
xml_rule->getName().c_str(), filename.c_str());
|
|
continue;
|
|
}
|
|
Rule rule(xml_rule);
|
|
if (rule.applies(card_name, version))
|
|
{
|
|
std::vector<std::string> restrictions = rule.getRestrictions();
|
|
std::vector<std::string>::iterator p;
|
|
for (p = restrictions.begin(); p != restrictions.end(); p++)
|
|
{
|
|
GraphicsRestrictionsType t = getTypeForName(*p);
|
|
if (t != GR_COUNT)
|
|
m_all_graphics_restriction[t] = true;
|
|
} // for p in rules
|
|
} // if m_all_rules[i].applies()
|
|
}
|
|
delete rules;
|
|
} // init
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Returns if the specified graphics restriction is defined.
|
|
* \param type The graphical restriction to tes.t
|
|
*/
|
|
bool isDisabled(GraphicsRestrictionsType type)
|
|
{
|
|
return m_all_graphics_restriction[type];
|
|
} // isDisabled
|
|
|
|
} // namespace HardwareStats
|