Browse Source

GL: rework Context to not use any STL containers.

We're going to eventually include this class in all Application classes
(need that in order to inherit a to-be-created Configuration class) and
the <string> and <vector> would be just too much. This change caused
magnum-gl-info.wasm (WebGL 2 build) to go down from 247 to 245 kB. Not
much, but that's I guess because there's still a lot other vectors of
strings elsewhere.

There's a lot more places to clean up, will do those in separate
commits. This change is the most atomic I could do, and it introduces a
breaking change to all APIs that returned a std::vector or a
std::string. Fortunately (or as I hope) those weren't used that much, so
it shouldn't cause build breakages for that many people.

Quite a lot of the optimization ideas is borrowed from the new Vk
library -- such as "interning" the driver workaround strings to avoid
allocating their copies.
euler-xxx
Vladimír Vondruš 5 years ago
parent
commit
95b3f8578d
  1. 14
      doc/changelog.dox
  2. 2
      doc/snippets/MagnumPlatform-windowless.cpp
  3. 2
      doc/snippets/getting-started-blue.cpp
  4. 223
      src/Magnum/GL/Context.cpp
  5. 62
      src/Magnum/GL/Context.h
  6. 1
      src/Magnum/GL/Implementation/BufferState.cpp
  7. 2
      src/Magnum/GL/Implementation/ContextState.cpp
  8. 2
      src/Magnum/GL/Implementation/FramebufferState.cpp
  9. 2
      src/Magnum/GL/Implementation/MeshState.cpp
  10. 2
      src/Magnum/GL/Implementation/QueryState.cpp
  11. 2
      src/Magnum/GL/Implementation/RendererState.cpp
  12. 2
      src/Magnum/GL/Implementation/ShaderProgramState.cpp
  13. 2
      src/Magnum/GL/Implementation/ShaderState.cpp
  14. 1
      src/Magnum/GL/Implementation/TextureState.cpp
  15. 195
      src/Magnum/GL/Implementation/driverSpecific.cpp
  16. 47
      src/Magnum/GL/Test/ContextGLTest.cpp
  17. 1
      src/Magnum/Platform/WindowlessGlxApplication.cpp
  18. 1
      src/Magnum/Platform/WindowlessWglApplication.cpp
  19. 4
      src/Magnum/Platform/gl-info.cpp
  20. 11
      src/Magnum/Shaders/Test/PhongGLTest.cpp
  21. 1
      src/MagnumExternal/OpenGL/GL/flextGLPlatform.cpp
  22. 1
      src/MagnumExternal/OpenGL/GL/flextGLPlatform.cpp.template

14
doc/changelog.dox

@ -458,6 +458,20 @@ See also:
@ref Corrade::Containers::ArrayView are now removed. This should have a
significant positive effect on compile times of code using the @ref GL,
@ref Audio, @ref Trade and @ref Text libraries
- As part of the ongoing STL header dependency cleanup,
@ref GL::Context::vendorString(),
@relativeref{GL::Context,rendererString()},
@relativeref{GL::Context,versionString()},
@relativeref{GL::Context,shadingLanguageVersionString()},
@relativeref{GL::Context,shadingLanguageVersionStrings()} and
@relativeref{GL::Context,extensionStrings()} now return
@relativeref{Corrade,Containers::StringView} or a
@relativeref{Corrade,Containers::Array} /
@relativeref{Corrade,Containers::ArrayView} of them, instead of a
@ref std::string and a @ref std::vector. For at least some backwards
compatibility the @ref Corrade/Containers/StringStl.h header is included to
provide implicit conversions to a @ref std::string, but in most cases
you'll be forced to change the code that uses those APIs.
- @ref GL::TextureFormat::SR8 and @ref GL::TextureFormat::SRG8 were present
on ES2 builds by mistake --- the @gl_extension{EXT,texture_sRGB_R8} and
@gl_extension{EXT,texture_sRGB_RG8} extensions require OpenGL ES 3.0 at

2
doc/snippets/MagnumPlatform-windowless.cpp

@ -24,7 +24,7 @@
*/
/* [windowless] */
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Containers/StringView.h>
#include <Magnum/GL/Context.h>
#include <Magnum/Platform/WindowlessEglApplication.h>

2
doc/snippets/getting-started-blue.cpp

@ -26,7 +26,7 @@
#include <Magnum/GL/DefaultFramebuffer.h>
#include <Magnum/Platform/Sdl2Application.h>
/** [0] */
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Containers/StringView.h>
#include <Magnum/GL/Context.h>
#include <Magnum/GL/Renderer.h>
#include <Magnum/GL/Version.h>

223
src/Magnum/GL/Context.cpp

@ -26,13 +26,10 @@
#include "Context.h"
#include <algorithm>
#include <iostream> /* for initialization log redirection */
#include <string>
#include <Corrade/Containers/EnumSet.hpp>
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Utility/Arguments.h>
#include <Corrade/Utility/Debug.h>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/String.h>
#include "Magnum/GL/AbstractFramebuffer.h"
#include "Magnum/GL/AbstractShaderProgram.h"
@ -68,6 +65,8 @@
namespace Magnum { namespace GL {
using namespace Containers::Literals;
/* When adding a new list, Extension::extensions() and Context::Context() needs
to be adapted. Binary search is performed on the extensions, thus they have
to be sorted alphabetically. */
@ -523,6 +522,44 @@ constexpr Extension ExtensionListES320[]{
#endif
#undef _extension
constexpr struct {
Version version;
Containers::ArrayView<const Extension> extensions;
} KnownExtensionsForVersion[]{
#ifndef MAGNUM_TARGET_GLES
{Version::GL300, Containers::arrayView(ExtensionList300)},
{Version::GL310, Containers::arrayView(ExtensionList310)},
{Version::GL320, Containers::arrayView(ExtensionList320)},
{Version::GL330, Containers::arrayView(ExtensionList330)},
{Version::GL400, Containers::arrayView(ExtensionList400)},
{Version::GL410, Containers::arrayView(ExtensionList410)},
{Version::GL420, Containers::arrayView(ExtensionList420)},
{Version::GL430, Containers::arrayView(ExtensionList430)},
{Version::GL440, Containers::arrayView(ExtensionList440)},
{Version::GL450, Containers::arrayView(ExtensionList450)},
{Version::GL460, Containers::arrayView(ExtensionList460)},
#else
{Version::GLES300, Containers::arrayView(ExtensionListES300)},
#ifndef MAGNUM_TARGET_WEBGL
/* No extensions in ES 3.1 */
{Version::GLES320, Containers::arrayView(ExtensionListES320)},
#endif
#endif
{Version::None, Containers::arrayView(ExtensionList)}
};
const Extension* findExtension(const Containers::StringView extension, const std::size_t since = 0) {
for(std::size_t i = since; i != Containers::arraySize(KnownExtensionsForVersion); ++i) {
const auto found = std::lower_bound(KnownExtensionsForVersion[i].extensions.begin(), KnownExtensionsForVersion[i].extensions.end(), extension, [](const Extension& a, const Containers::StringView& b) {
return a.string() < b;
});
if(found != KnownExtensionsForVersion[i].extensions.end() && found->string() == extension)
return found;
}
return {};
}
}
Containers::ArrayView<const Extension> Extension::extensions(Version version) {
@ -657,21 +694,39 @@ Context::Context(NoCreateT, Utility::Arguments& args, Int argc, const char** arg
if(args.value("gpu-validation") == "on" || args.value("gpu-validation") == "ON")
_internalFlags |= InternalFlag::GpuValidation;
/* Disable driver workarounds */
for(auto&& workaround: Utility::String::splitWithoutEmptyParts(args.value("disable-workarounds")))
disableDriverWorkaround(workaround);
/* If there are any disabled workarounds, save them until tryCreate() uses
them. The disableWorkaround() function saves the internal string view
instead of the one passed from the command line so we don't need to
bother with String allocations. */
const Containers::StringView disabledWorkarounds = args.value<Containers::StringView>("disable-workarounds");
if(!disabledWorkarounds.isEmpty()) {
const Containers::Array<Containers::StringView> split = disabledWorkarounds.splitWithoutEmptyParts();
arrayReserve(_driverWorkarounds, split.size());
for(const Containers::StringView workaround: split)
disableDriverWorkaround(workaround);
}
/* Disable extensions */
for(auto&& extension: Utility::String::splitWithoutEmptyParts(args.value("disable-extensions")))
_disabledExtensions.push_back(extension);
/* Disable extensions. Here we search for them among the known extensions
and store the Extension objects instead, which avoids the string copying
and another binary search in tryCreate(). */
const Containers::StringView disabledExtensions = args.value<Containers::StringView>("disable-extensions");
if(!disabledExtensions.isEmpty()) {
const Containers::Array<Containers::StringView> split = disabledExtensions.splitWithoutEmptyParts();
arrayReserve(_disabledExtensions, split.size());
for(const Containers::StringView extension: split) {
if(const Extension* found = findExtension(extension)) {
arrayAppend(_disabledExtensions, *found);
}
}
}
}
Context::Context(Context&& other) noexcept: _version{other._version},
#ifndef MAGNUM_TARGET_WEBGL
_flags{other._flags},
#endif
_extensionRequiredVersion{other._extensionRequiredVersion},
_extensionStatus{other._extensionStatus},
_extensionRequiredVersion{other._extensionRequiredVersion},
_supportedExtensions{std::move(other._supportedExtensions)},
_state{std::move(other._state)},
_detectedDrivers{std::move(other._detectedDrivers)}
@ -710,8 +765,8 @@ bool Context::tryCreate() {
/* WebGL 2.0, treat it as ES 3.0 */
#else
const std::string version = versionString();
if(version.find("WebGL 2") == std::string::npos) {
const Containers::StringView version = versionString();
if(!version.contains("WebGL 2"_s)) {
Error{} << "GL::Context: unsupported version string:" << version;
return false;
}
@ -738,17 +793,17 @@ bool Context::tryCreate() {
#endif
/* Allow ES2 context on driver that reports ES3 as supported */
const std::string version = versionString();
const Containers::StringView version = versionString();
#ifndef MAGNUM_TARGET_GLES
if(version.compare(0, 3, "2.1") == 0)
if(version.hasPrefix("2.1"_s))
#elif defined(MAGNUM_TARGET_WEBGL)
/* Internet Explorer currently has 0.94 */
if(version.find("WebGL 1") != std::string::npos ||
version.find("WebGL 0") != std::string::npos)
if(version.contains("WebGL 1"_s) ||
version.contains("WebGL 0"_s))
#else
if(version.find("OpenGL ES 2.0") != std::string::npos ||
if(version.contains("OpenGL ES 2.0"_s) ||
/* It is possible to use Magnum compiled for ES2 on ES3 contexts */
version.find("OpenGL ES 3.") != std::string::npos)
version.contains("OpenGL ES 3."_s))
#endif
{
majorVersion = 2;
@ -804,51 +859,22 @@ bool Context::tryCreate() {
glGetIntegerv(GL_CONTEXT_FLAGS, reinterpret_cast<GLint*>(&_flags));
#endif
constexpr struct {
Version version;
Containers::ArrayView<const Extension> extensions;
} versions[]{
#ifndef MAGNUM_TARGET_GLES
{Version::GL300, Containers::arrayView(ExtensionList300)},
{Version::GL310, Containers::arrayView(ExtensionList310)},
{Version::GL320, Containers::arrayView(ExtensionList320)},
{Version::GL330, Containers::arrayView(ExtensionList330)},
{Version::GL400, Containers::arrayView(ExtensionList400)},
{Version::GL410, Containers::arrayView(ExtensionList410)},
{Version::GL420, Containers::arrayView(ExtensionList420)},
{Version::GL430, Containers::arrayView(ExtensionList430)},
{Version::GL440, Containers::arrayView(ExtensionList440)},
{Version::GL450, Containers::arrayView(ExtensionList450)},
{Version::GL460, Containers::arrayView(ExtensionList460)},
#else
{Version::GLES300, Containers::arrayView(ExtensionListES300)},
#ifndef MAGNUM_TARGET_WEBGL
/* No extensions in ES 3.1 */
{Version::GLES320, Containers::arrayView(ExtensionListES320)},
#endif
#endif
{Version::None, Containers::arrayView(ExtensionList)}
};
/* Get first future (not supported) version */
std::size_t future = 0;
while(versions[future].version != Version::None && isVersionSupported(versions[future].version))
while(KnownExtensionsForVersion[future].version != Version::None && isVersionSupported(KnownExtensionsForVersion[future].version))
++future;
/* Mark all extensions from past versions as supported */
for(std::size_t i = 0; i != future; ++i)
for(const Extension& extension: versions[i].extensions)
for(const Extension& extension: KnownExtensionsForVersion[i].extensions)
_extensionStatus.set(extension.index(), true);
/* Check for presence of future and vendor extensions */
const std::vector<std::string> extensions = extensionStrings();
for(const std::string& extension: extensions) {
for(std::size_t i = future; i != Containers::arraySize(versions); ++i) {
const auto found = std::lower_bound(versions[i].extensions.begin(), versions[i].extensions.end(), extension, [](const Extension& a, const std::string& b) { return a.string() < b; });
if(found != versions[i].extensions.end() && found->string() == extension) {
_supportedExtensions.push_back(*found);
_extensionStatus.set(found->index(), true);
}
const Containers::Array<Containers::StringView> extensions = extensionStrings();
for(const Containers::StringView extension: extensions) {
if(const Extension* found = findExtension(extension, future)) {
arrayAppend(_supportedExtensions, *found);
_extensionStatus.set(found->index(), true);
}
}
@ -856,7 +882,7 @@ bool Context::tryCreate() {
for(auto& i: _extensionRequiredVersion) i = Version::None;
/* Initialize required versions from extension info */
for(const auto& version: versions)
for(const auto& version: KnownExtensionsForVersion)
for(const Extension& extension: version.extensions)
_extensionRequiredVersion[extension.index()] = extension.requiredVersion();
@ -873,31 +899,16 @@ bool Context::tryCreate() {
/* Print some info and initialize state tracker (which also prints some
more info). Mesa's renderer string has a space at the end, trim that. */
Debug{output} << "Renderer:" << Utility::String::trim(rendererString()) << "by" << vendorString();
Debug{output} << "Renderer:" << rendererString().trimmed() << "by" << vendorString();
Debug{output} << "OpenGL version:" << versionString();
/* Disable extensions as requested by the user */
if(!_disabledExtensions.empty()) {
bool headerPrinted = false;
/* Disable extensions that are known and supported and print a message
for each */
for(auto&& extension: _disabledExtensions) {
for(const auto& version: versions) {
const auto found = std::lower_bound(version.extensions.begin(), version.extensions.end(), extension, [](const Extension& a, const std::string& b) { return a.string() < b; });
/* No error message here because some of the extensions could
be from Vulkan or OpenAL. That also means we print the
header only when we actually have something to say */
if(found == version.extensions.end() || found->string() != extension)
continue;
_extensionRequiredVersion[found->index()] = Version::None;
if(!headerPrinted) {
Debug{output} << "Disabling extensions:";
headerPrinted = true;
}
Debug{output} << " " << extension;
}
Debug{output} << "Disabling extensions:";
for(const Extension& extension: _disabledExtensions) {
_extensionRequiredVersion[extension.index()] = Version::None;
Debug{output} << " " << extension.string();
}
}
@ -939,45 +950,42 @@ bool Context::tryCreate() {
return true;
}
std::string Context::vendorString() const {
return Utility::String::fromArray(reinterpret_cast<const char*>(glGetString(GL_VENDOR)));
Containers::StringView Context::vendorString() const {
return {reinterpret_cast<const char*>(glGetString(GL_VENDOR)), Containers::StringViewFlag::Global};
}
std::string Context::rendererString() const {
return Utility::String::fromArray(reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
Containers::StringView Context::rendererString() const {
return {reinterpret_cast<const char*>(glGetString(GL_RENDERER)), Containers::StringViewFlag::Global};
}
std::string Context::versionString() const {
return Utility::String::fromArray(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
Containers::StringView Context::versionString() const {
return {reinterpret_cast<const char*>(glGetString(GL_VERSION)), Containers::StringViewFlag::Global};
}
std::string Context::shadingLanguageVersionString() const {
return Utility::String::fromArray(reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION)));
Containers::StringView Context::shadingLanguageVersionString() const {
return {reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION)), Containers::StringViewFlag::Global};
}
std::vector<std::string> Context::shadingLanguageVersionStrings() const {
Containers::Array<Containers::StringView> Context::shadingLanguageVersionStrings() const {
#ifndef MAGNUM_TARGET_GLES
GLint versionCount = 0;
glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &versionCount);
/* The implementation doesn't yet support this query (< OpenGL 4.3) */
if(!versionCount)
return {shadingLanguageVersionString()};
/* Get all of them */
std::vector<std::string> versions;
versions.reserve(versionCount);
for(GLint i = 0; i != versionCount; ++i)
versions.emplace_back(reinterpret_cast<const char*>(glGetStringi(GL_SHADING_LANGUAGE_VERSION, i)));
return versions;
#else
return {shadingLanguageVersionString()};
/* If zero, the implementation doesn't yet support this query (< GL4.3) */
/** @todo doesn't this throw a GL error? some better handling? */
if(versionCount) {
/* Get all of them */
Containers::Array<Containers::StringView> versions{std::size_t(versionCount)};
for(GLint i = 0; i != versionCount; ++i)
versions[i] = {reinterpret_cast<const char*>(glGetStringi(GL_SHADING_LANGUAGE_VERSION, i)), Containers::StringViewFlag::Global};
return versions;
}
#endif
}
std::vector<std::string> Context::extensionStrings() const {
std::vector<std::string> extensions;
return Containers::array({shadingLanguageVersionString()});
}
Containers::Array<Containers::StringView> Context::extensionStrings() const {
/* If we have GL 3.0 / GLES 3.0 at least, ask the new way. Otherwise don't
even attempt to query GL_NUM_EXTENSIONS as that would cause a GL error
on GL 2.1. Happens with Mesa's zink that's just 2.1 currently (Apr 2020)
@ -989,9 +997,10 @@ std::vector<std::string> Context::extensionStrings() const {
{
GLint extensionCount = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount);
extensions.reserve(extensionCount);
Containers::Array<Containers::StringView> extensions{std::size_t(extensionCount)};
for(GLint i = 0; i != extensionCount; ++i)
extensions.emplace_back(reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i)));
extensions[i] = {reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i)), Containers::StringViewFlag::Global};
return extensions;
}
#ifndef MAGNUM_TARGET_GLES3
else
@ -1000,14 +1009,8 @@ std::vector<std::string> Context::extensionStrings() const {
#ifndef MAGNUM_TARGET_GLES3
/* OpenGL 2.1 / OpenGL ES 2.0 doesn't have glGetStringi() */
{
/* Don't crash when glGetString() returns nullptr (i.e. don't trust the
old implementations) */
extensions = Utility::String::splitWithoutEmptyParts(Utility::String::fromArray(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))), ' ');
}
return Containers::StringView{reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)), Containers::StringViewFlag::Global}.splitWithoutEmptyParts();
#endif
return extensions;
}
#ifndef MAGNUM_TARGET_GLES

62
src/Magnum/GL/Context.h

@ -30,12 +30,11 @@
*/
#include <cstdlib>
#include <vector>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/EnumSet.h>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Pointer.h>
#include <Corrade/Containers/StaticArray.h>
#include <Corrade/Utility/StlForwardString.h>
#include "Magnum/Magnum.h"
#include "Magnum/Math/BoolVector.h"
@ -45,6 +44,12 @@
#include "Magnum/GL/visibility.h"
#ifdef MAGNUM_BUILD_DEPRECATED
/* For return types of Context::versionString() etc., which used to be a
std::string. Not ideal, but at least something. */
#include <Corrade/Containers/StringStl.h>
#endif
namespace Magnum {
namespace GL {
@ -513,52 +518,62 @@ class MAGNUM_GL_EXPORT Context {
* @brief Vendor string
*
* The result is *not* cached, repeated queries will result in repeated
* OpenGL calls.
* OpenGL calls. The returned view is always
* @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} and
* @relativeref{Corrade::Containers::StringViewFlag,Global}.
* @see @ref rendererString(), @fn_gl{GetString} with
* @def_gl_keyword{VENDOR}
*/
std::string vendorString() const;
Containers::StringView vendorString() const;
/**
* @brief Renderer string
*
* The result is *not* cached, repeated queries will result in repeated
* OpenGL calls.
* OpenGL calls. The returned view is always
* @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} and
* @relativeref{Corrade::Containers::StringViewFlag,Global}.
* @see @ref vendorString(), @fn_gl{GetString} with
* @def_gl_keyword{RENDERER}
*/
std::string rendererString() const;
Containers::StringView rendererString() const;
/**
* @brief Version string
*
* The result is *not* cached, repeated queries will result in repeated
* OpenGL calls.
* OpenGL calls. The returned view is always
* @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} and
* @relativeref{Corrade::Containers::StringViewFlag,Global}.
* @see @ref shadingLanguageVersionString(), @ref version(),
* @fn_gl{GetString} with @def_gl_keyword{VERSION}
*/
std::string versionString() const;
Containers::StringView versionString() const;
/**
* @brief Shading language version string
*
* The result is *not* cached, repeated queries will result in repeated
* OpenGL calls.
* OpenGL calls. The returned view is always
* @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} and
* @relativeref{Corrade::Containers::StringViewFlag,Global}.
* @see @ref versionString(), @ref version(), @fn_gl{GetString} with
* @def_gl_keyword{SHADING_LANGUAGE_VERSION}
*/
std::string shadingLanguageVersionString() const;
Containers::StringView shadingLanguageVersionString() const;
/**
* @brief Shading language version strings
*
* The result is *not* cached, repeated queries will result in repeated
* OpenGL calls.
* OpenGL calls. The returned view is always
* @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} and
* @relativeref{Corrade::Containers::StringViewFlag,Global}.
* @see @ref versionString(), @ref version(), @fn_gl{Get} with
* @def_gl_keyword{NUM_SHADING_LANGUAGE_VERSIONS}, @fn_gl{GetString}
* with @def_gl_keyword{SHADING_LANGUAGE_VERSION}
*/
std::vector<std::string> shadingLanguageVersionStrings() const;
Containers::Array<Containers::StringView> shadingLanguageVersionStrings() const;
/**
* @brief Extension strings
@ -567,11 +582,13 @@ class MAGNUM_GL_EXPORT Context {
* OpenGL calls. Note that this function returns list of all extensions
* reported by the driver (even those not supported by Magnum), see
* @ref supportedExtensions(), @ref Extension::extensions() or
* @ref isExtensionSupported() for alternatives.
* @ref isExtensionSupported() for alternatives. The returned views are
* always @relativeref{Corrade,Containers::StringViewFlag::NullTerminated}
* and @relativeref{Corrade::Containers::StringViewFlag,Global}.
* @see @fn_gl{Get} with @def_gl_keyword{NUM_EXTENSIONS},
* @fn_gl{GetString} with @def_gl_keyword{EXTENSIONS}
*/
std::vector<std::string> extensionStrings() const;
Containers::Array<Containers::StringView> extensionStrings() const;
#ifndef MAGNUM_TARGET_WEBGL
/**
@ -589,7 +606,7 @@ class MAGNUM_GL_EXPORT Context {
* the current.
* @see @ref isExtensionSupported(), @ref Extension::extensions()
*/
const std::vector<Extension>& supportedExtensions() const {
Containers::ArrayView<const Extension> supportedExtensions() const {
return _supportedExtensions;
}
@ -750,7 +767,7 @@ class MAGNUM_GL_EXPORT Context {
typedef Containers::EnumSet<InternalFlag> InternalFlags;
CORRADE_ENUMSET_FRIEND_OPERATORS(InternalFlags)
bool isDriverWorkaroundDisabled(const char* workaround);
bool isDriverWorkaroundDisabled(Containers::StringView workaround);
Implementation::State& state() { return *_state; }
/* This function is called from MeshState constructor, which means the
@ -778,7 +795,7 @@ class MAGNUM_GL_EXPORT Context {
friend Implementation::ContextState;
#endif
void disableDriverWorkaround(const std::string& workaround);
void disableDriverWorkaround(Containers::StringView workaround);
/* Defined in Implementation/driverSpecific.cpp */
MAGNUM_GL_LOCAL void setupDriverWorkarounds();
@ -794,17 +811,20 @@ class MAGNUM_GL_EXPORT Context {
Flags _flags;
#endif
Containers::StaticArray<Implementation::ExtensionCount, Version> _extensionRequiredVersion;
Math::BoolVector<Implementation::ExtensionCount> _extensionStatus;
std::vector<Extension> _supportedExtensions;
/* For all extensions that are marked as supported in _extensionStatus,
this field contains the minimal required GL version the extension
needs. Extensions that are disabled have None here. */
Containers::StaticArray<Implementation::ExtensionCount, Version> _extensionRequiredVersion;
Containers::Array<Extension> _supportedExtensions;
Containers::Pointer<Implementation::State> _state;
Containers::Optional<DetectedDrivers> _detectedDrivers;
/* True means known and disabled, false means known */
std::vector<std::pair<std::string, bool>> _driverWorkarounds;
std::vector<std::string> _disabledExtensions;
Containers::Array<std::pair<Containers::StringView, bool>> _driverWorkarounds;
Containers::Array<Extension> _disabledExtensions;
InternalFlags _internalFlags;
};

1
src/Magnum/GL/Implementation/BufferState.cpp

@ -25,6 +25,7 @@
#include "BufferState.h"
#include <Corrade/Containers/StringView.h>
#include <Corrade/Utility/Assert.h>
#include "Magnum/GL/Context.h"

2
src/Magnum/GL/Implementation/ContextState.cpp

@ -25,6 +25,8 @@
#include "ContextState.h"
#include <Corrade/Containers/StringView.h>
#include "Magnum/GL/Context.h"
namespace Magnum { namespace GL { namespace Implementation {

2
src/Magnum/GL/Implementation/FramebufferState.cpp

@ -25,6 +25,8 @@
#include "FramebufferState.h"
#include <Corrade/Containers/StringView.h>
#include "Magnum/GL/Context.h"
#include "Magnum/GL/Extensions.h"
#include "Magnum/GL/Renderbuffer.h"

2
src/Magnum/GL/Implementation/MeshState.cpp

@ -25,6 +25,8 @@
#include "MeshState.h"
#include <Corrade/Containers/StringView.h>
#include "Magnum/GL/Context.h"
#include "Magnum/GL/Extensions.h"
#include "Magnum/GL/MeshView.h"

2
src/Magnum/GL/Implementation/QueryState.cpp

@ -25,6 +25,8 @@
#include "QueryState.h"
#include <Corrade/Containers/StringView.h>
#include "Magnum/GL/AbstractQuery.h"
#include "Magnum/GL/Context.h"
#include "Magnum/GL/Extensions.h"

2
src/Magnum/GL/Implementation/RendererState.cpp

@ -25,6 +25,8 @@
#include "RendererState.h"
#include <Corrade/Containers/StringView.h>
#include "Magnum/PixelStorage.h"
#include "Magnum/GL/Context.h"
#include "Magnum/GL/Extensions.h"

2
src/Magnum/GL/Implementation/ShaderProgramState.cpp

@ -25,6 +25,8 @@
#include "ShaderProgramState.h"
#include <Corrade/Containers/StringView.h>
#include "Magnum/GL/AbstractShaderProgram.h"
#include "Magnum/GL/Context.h"
#include "Magnum/GL/Extensions.h"

2
src/Magnum/GL/Implementation/ShaderState.cpp

@ -27,6 +27,8 @@
/* Needed only for Emscripten+pthread- / Windows+Intel-specific workarounds,
but I won't bother crafting the preprocessor logic for this. */
#include <Corrade/Containers/StringView.h>
#include "Magnum/GL/Context.h"
#include "Magnum/GL/Shader.h"

1
src/Magnum/GL/Implementation/TextureState.cpp

@ -26,6 +26,7 @@
#include "TextureState.h"
#include <tuple>
#include <Corrade/Containers/StringView.h>
#include <Corrade/Utility/Assert.h>
#include "Magnum/GL/AbstractTexture.h"

195
src/Magnum/GL/Implementation/driverSpecific.cpp

@ -23,11 +23,8 @@
DEALINGS IN THE SOFTWARE.
*/
#include <cstring>
#include <algorithm>
#include <string>
#include <vector>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Containers/StringView.h>
#include <Corrade/Containers/GrowableArray.h>
#include "Magnum/GL/Context.h"
#include "Magnum/GL/Extensions.h"
@ -37,14 +34,16 @@ namespace Magnum { namespace GL {
namespace {
/* Search the code for the following strings to see where they are implemented. */
const char* KnownWorkarounds[]{
using namespace Containers::Literals;
/* Search the code for the following strings to see where they are implemented */
constexpr Containers::StringView KnownWorkarounds[]{
/* [workarounds] */
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL)
/* ANGLE's shader linker insists on returning a message consisting of a
single newline on success, causing annoying noise in the console. Similar to
"intel-windows-chatty-shader-compiler". */
"angle-chatty-shader-compiler",
"angle-chatty-shader-compiler"_s,
#endif
#if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES)
@ -69,7 +68,7 @@ const char* KnownWorkarounds[]{
data modification on *any* buffer, which would have extreme perf
implications. So FORTUNATELY unbinding the textures worked around this too,
and is a much nicer workaround after all. */
"apple-buffer-texture-unbind-on-buffer-modify",
"apple-buffer-texture-unbind-on-buffer-modify"_s,
#endif
#if defined(CORRADE_TARGET_ANDROID) && defined(MAGNUM_TARGET_GLES)
@ -77,7 +76,7 @@ const char* KnownWorkarounds[]{
running from the Android shell (through ADB). No such error happens in an
APK. Detecting using the $SHELL environment variable and disabling
GL_EXT_disjoint_timer_query in that case. */
"arm-mali-timer-queries-oom-in-shell",
"arm-mali-timer-queries-oom-in-shell"_s,
#endif
#if !defined(MAGNUM_TARGET_GLES) && defined(CORRADE_TARGET_WINDOWS)
@ -90,30 +89,30 @@ const char* KnownWorkarounds[]{
svga3d-texture-upload-slice-by-slice workaround. The compressed image up/
download is affected as well, but we lack APIs for easy format-dependent
slicing and offset calculation, so those currently still fail. */
"amd-windows-cubemap-image3d-slice-by-slice",
"amd-windows-cubemap-image3d-slice-by-slice"_s,
/* AMD Windows drivers have broken the DSA glCopyTextureSubImage3D(), returning
GL_INVALID_VALUE. The non-DSA code path works. */
"amd-windows-broken-dsa-cubemap-copy",
"amd-windows-broken-dsa-cubemap-copy"_s,
/* AMD Windows glCreateQueries() works for everything except
GL_TRANSFORM_FEEDBACK_[STREAM_]OVERFLOW, probably they just forgot to adapt
it to this new GL 4.6 addition. Calling the non-DSA code path in that case
instead. Similar to "mesa-dsa-createquery-except-pipeline-stats". */
"amd-windows-dsa-createquery-except-xfb-overflow",
"amd-windows-dsa-createquery-except-xfb-overflow"_s,
#endif
#if !defined(MAGNUM_TARGET_GLES) && !defined(CORRADE_TARGET_APPLE)
/* Creating core context with specific version on AMD and NV proprietary
drivers on Linux/Windows and Intel drivers on Windows causes the context to
be forced to given version instead of selecting latest available version */
"no-forward-compatible-core-context",
"no-forward-compatible-core-context"_s,
#endif
#if !defined(MAGNUM_TARGET_GLES) && defined(CORRADE_TARGET_WINDOWS)
/* On Windows Intel drivers ARB_shading_language_420pack is exposed in GLSL
even though the extension (e.g. binding keyword) is not supported */
"intel-windows-glsl-exposes-unsupported-shading-language-420pack",
"intel-windows-glsl-exposes-unsupported-shading-language-420pack"_s,
#endif
#ifndef MAGNUM_TARGET_GLES
@ -121,12 +120,12 @@ const char* KnownWorkarounds[]{
ARB_pipeline_statistics_query, probably just forgotten. Calling the non-DSA
code path in that case instead. Similar to
"amd-windows-dsa-createquery-except-xfb-overflow". */
"mesa-dsa-createquery-except-pipeline-stats",
"mesa-dsa-createquery-except-pipeline-stats"_s,
/* Forward-compatible GL contexts on Mesa still report line width range as
[1, 7], but setting wide line width fails. According to the specs the max
value on forward compatible contexts should be 1.0, so patching it. */
"mesa-forward-compatible-line-width-range",
"mesa-forward-compatible-line-width-range"_s,
#endif
#if !defined(MAGNUM_TARGET_GLES2) && defined(CORRADE_TARGET_WINDOWS)
@ -135,31 +134,31 @@ const char* KnownWorkarounds[]{
arrays are not in scope anymore. Enabling *synchronous* debug output
circumvents this bug. Can be triggered by running TransformFeedbackGLTest
with GL_KHR_debug extension disabled. */
"nv-windows-dangling-transform-feedback-varying-names",
"nv-windows-dangling-transform-feedback-varying-names"_s,
#endif
#ifndef MAGNUM_TARGET_GLES
/* Layout qualifier causes compiler error with GLSL 1.20 on Mesa, GLSL 1.30 on
NVidia and 1.40 on macOS. Everything is fine when using a newer GLSL
version. */
"no-layout-qualifiers-on-old-glsl",
"no-layout-qualifiers-on-old-glsl"_s,
/* NVidia drivers (358.16) report compressed block size from internal format
query in bits instead of bytes */
"nv-compressed-block-size-in-bits",
"nv-compressed-block-size-in-bits"_s,
/* NVidia drivers (358.16) report different compressed image size for cubemaps
based on whether the texture is immutable or not and not based on whether
I'm querying all faces (ARB_DSA) or a single face (non-DSA, EXT_DSA) */
"nv-cubemap-inconsistent-compressed-image-size",
"nv-cubemap-inconsistent-compressed-image-size"_s,
/* NVidia drivers (358.16) return only the first slice of compressed cube map
image when querying all six slices using the ARB_DSA API */
"nv-cubemap-broken-full-compressed-image-query",
"nv-cubemap-broken-full-compressed-image-query"_s,
/* NVidia drivers return 0 when asked for GL_CONTEXT_PROFILE_MASK, so it needs
to be worked around by asking for GL_ARB_compatibility */
"nv-zero-context-profile-mask",
"nv-zero-context-profile-mask"_s,
/* (Headless) EGL contexts for desktop GL on NVidia 384 and 390 drivers don't
have correct statically linked GL 1.0 and 1.1 functions (such as
@ -167,7 +166,7 @@ const char* KnownWorkarounds[]{
eglGetProcAddress(). Doesn't seem to happen on pre-384 and 396, but it's not
possible to get driver version through EGL, so enabling this unconditionally
on all EGL NV contexts. */
"nv-egl-incorrect-gl11-function-pointers",
"nv-egl-incorrect-gl11-function-pointers"_s,
/* On NV driver 450.80.02, eglQueryDeviceAttribEXT() segfaults when querying
GPUs that the user does not have access to (i.e. via cgroup). Instead,
@ -175,19 +174,19 @@ const char* KnownWorkarounds[]{
error that can be retrieved via eglGetError() to see if the user has access
to that device. On well-behaved driver versions, eglQueryDeviceAttribEXT()
returns false instead of segfaulting. */
"nv-egl-crashy-query-device-attrib",
"nv-egl-crashy-query-device-attrib"_s,
#endif
#ifndef MAGNUM_TARGET_GLES
/* SVGA3D (VMware host GL driver) glDrawArrays() draws nothing when the vertex
buffer memory is initialized using glNamedBufferData() from ARB_DSA. Using
the non-DSA glBufferData() works. */
"svga3d-broken-dsa-bufferdata",
"svga3d-broken-dsa-bufferdata"_s,
/* SVGA3D does out-of-bound writes in some cases of glGetTexSubImage(), leading
to memory corruption on client machines. That's nasty, so the whole
ARB_get_texture_sub_image is disabled. */
"svga3d-gettexsubimage-oob-write",
"svga3d-gettexsubimage-oob-write"_s,
#endif
/* SVGA3D has broken handling of glTex[ture][Sub]Image*D() for 1D arrays, 2D
@ -196,25 +195,25 @@ const char* KnownWorkarounds[]{
with buffer images. Seems to be fixed in Mesa 13, but I have no such system
to verify that on.
https://github.com/mesa3d/mesa/commit/2aa9ff0cda1f6ad97c83d5583fab7a84efabe19e */
"svga3d-texture-upload-slice-by-slice",
"svga3d-texture-upload-slice-by-slice"_s,
#if defined(CORRADE_TARGET_EMSCRIPTEN) && defined(__EMSCRIPTEN_PTHREADS__)
/* Shader sources containing UTF-8 characters are converted to empty strings
when running on Emscripten with -s USE_PTHREADS=1. Working around that by
replacing all chars > 127 with spaces. Relevant code:
https://github.com/kripken/emscripten/blob/7f89560101843198787530731f40a65288f6f15f/src/fetch-worker.js#L54-L58 */
"emscripten-pthreads-broken-unicode-shader-sources",
"emscripten-pthreads-broken-unicode-shader-sources"_s,
#endif
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL)
/* Empty EGL_CONTEXT_FLAGS_KHR cause SwiftShader 3.3.0.1 to fail context
creation with EGL_BAD_ATTRIBUTE. Not sending the flags then. Relevant code:
https://github.com/google/swiftshader/blob/5fb5e817a20d3e60f29f7338493f922b5ac9d7c4/src/OpenGL/libEGL/libEGL.cpp#L794-L810 */
"swiftshader-no-empty-egl-context-flags",
"swiftshader-no-empty-egl-context-flags"_s,
/* SwiftShader 3.3.0.1 crashes deep inside eglMakeCurrent() when using
EGL_NO_SURFACE. Supplying a 32x32 PBuffer to work around that. */
"swiftshader-egl-context-needs-pbuffer",
"swiftshader-egl-context-needs-pbuffer"_s,
#endif
#if defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
@ -225,14 +224,14 @@ const char* KnownWorkarounds[]{
ES3. OTOH, glVertexAttribDivisor is there for both ANGLE and EXT. Relevant
code: https://github.com/google/swiftshader/blob/ad5c2952ca88730c07e04f6f1566194b66860c26/src/OpenGL/libGLESv2/libGLESv2.cpp#L6352-L6357
Disabling the two extensions on ES2 contexts to avoid nullptr crashes. */
"swiftshader-no-es2-draw-instanced-entrypoints",
"swiftshader-no-es2-draw-instanced-entrypoints"_s,
/* SwiftShader 4.1.0 on ES2 contexts reports GL_OES_texture_3D but from all its
entrypoints only glTexImage3DOES is present, all others are present only in
the ES3 unsuffixed versions. Relevant code:
https://github.com/google/swiftshader/blob/ad5c2952ca88730c07e04f6f1566194b66860c26/src/OpenGL/libGLESv2/libGLESv2.cpp#L6504
Disabling the extension on ES2 contexts to avoid nullptr crashes. */
"swiftshader-no-es2-oes-texture-3d-entrypoints",
"swiftshader-no-es2-oes-texture-3d-entrypoints"_s,
#endif
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
@ -242,14 +241,14 @@ const char* KnownWorkarounds[]{
otherwise. No other driver does that. As a workaround, setting
Buffer::TargetHint::TransformFeedback will make it use
Buffer::TargetHint::Array instead, as that works okay. */
"swiftshader-broken-xfb-buffer-binding-target",
"swiftshader-broken-xfb-buffer-binding-target"_s,
/* SwiftShader 4.1.0 does implement gl_VertexID for ES3 contexts, but in
practice it doesn't work, returning a constant value. In order to make this
easier to check, there's a dummy MAGNUM_shader_vertex_id extension that's
defined on all GL 3.0+ and GLES 3.0+ / WebGL 2+ contexts *except* for
SwiftShader. */
"swiftshader-broken-shader-vertex-id",
"swiftshader-broken-shader-vertex-id"_s,
#endif
#ifndef MAGNUM_TARGET_GLES
@ -258,7 +257,7 @@ const char* KnownWorkarounds[]{
bound for reading. Relevant code:
https://github.com/mesa3d/mesa/blob/212c0c630a849e4737e2808a993d708cbb2f18f7/src/mesa/main/framebuffer.c#L841-L843
Workaround is to explicitly bind the framebuffer for reading. */
"mesa-implementation-color-read-format-dsa-explicit-binding",
"mesa-implementation-color-read-format-dsa-explicit-binding"_s,
#endif
#if !defined(MAGNUM_TARGET_GLES2) && defined(CORRADE_TARGET_WINDOWS)
@ -272,7 +271,7 @@ const char* KnownWorkarounds[]{
glGetInteger() is actually able to return a correct value in *one
circumstance*, it's preferrable to the other random shit the driver is
doing. */
"intel-windows-implementation-color-read-format-completely-broken",
"intel-windows-implementation-color-read-format-completely-broken"_s,
#endif
#if !defined(MAGNUM_TARGET_GLES) && defined(CORRADE_TARGET_WINDOWS)
@ -306,8 +305,8 @@ const char* KnownWorkarounds[]{
on DSA that's not there. It's clearly Intel drivers fault.
- With both enabled, things seem to be fine, and I hope it stays that way
also for future driver updates. */
"intel-windows-crazy-broken-buffer-dsa",
"intel-windows-crazy-broken-vao-dsa",
"intel-windows-crazy-broken-buffer-dsa"_s,
"intel-windows-crazy-broken-vao-dsa"_s,
/* ARB_direct_state_access implementation on Intel Windows drivers has broken
*everything* related to cube map textures (but not cube map arrays) -- data
@ -315,37 +314,37 @@ const char* KnownWorkarounds[]{
complaining about "Wrong <func> 6 provided for <target> 34067" and similar
(GL_TEXTURE_CUBE_MAP is 34067). Using the non-DSA code paths as a
workaround (for the 3D image up/download as well). */
"intel-windows-broken-dsa-for-cubemaps",
"intel-windows-broken-dsa-for-cubemaps"_s,
/* DSA glBindTextureUnit() on Intel Windows drivers simply doesn't work when
passing 0 to it. Non-zero IDs work correctly except for cube maps. Using the
non-DSA code path for unbinding and cube maps as a workaround. */
"intel-windows-half-baked-dsa-texture-bind",
"intel-windows-half-baked-dsa-texture-bind"_s,
/* DSA glNamedFramebufferTexture() on Intel Windows drivers doesn't work for
layered cube map array attachments. Non-layered or non-array cube map
attachment works. Using the non-DSA code path as a workaround. */
"intel-windows-broken-dsa-layered-cubemap-array-framebuffer-attachment",
"intel-windows-broken-dsa-layered-cubemap-array-framebuffer-attachment"_s,
/* DSA glClearNamedFramebuffer*() on Intel Windows drivers doesn't do anything.
Using the non-DSA code path as a workaournd. */
"intel-windows-broken-dsa-framebuffer-clear",
"intel-windows-broken-dsa-framebuffer-clear"_s,
/* Using DSA glCreateQueries() on Intel Windows drivers breaks
glBeginQueryIndexed(). Using the non-DSA glGenQueries() instead makes it
work properly. See TransformFeedbackGLTest for a test. */
"intel-windows-broken-dsa-indexed-queries",
"intel-windows-broken-dsa-indexed-queries"_s,
/* DSA-ified "vertex layout" glVertexArrayAttribIFormat() is broken when
passing shorts instead of full 32bit ints. Using the old-style
glVertexAttribIPointer() works correctly. No idea if the non-DSA
glVertexAttribIFormat() works or not. A test that triggers this issue is in
MeshGLTest::addVertexBufferIntWithShort(). */
"intel-windows-broken-dsa-integer-vertex-attributes",
"intel-windows-broken-dsa-integer-vertex-attributes"_s,
/* Shader compiler on Intel Windows drivers insists on telling me "No errors."
when it should just stay silent. See also "angle-chatty-shader-compiler". */
"intel-windows-chatty-shader-compiler",
"intel-windows-chatty-shader-compiler"_s,
/* When using more than just a vertex and fragment shader (geometry shader,
e.g.), ARB_explicit_uniform_location on Intel silently uses wrong
@ -359,7 +358,7 @@ const char* KnownWorkarounds[]{
location 1, setting color to location 2 didn't work, ending up with a
Generic error again (driver version 27). Because this is impossible to
prevent, the extension is completely disabled on all Intel Windows drivers. */
"intel-windows-explicit-uniform-location-is-less-explicit-than-you-hoped",
"intel-windows-explicit-uniform-location-is-less-explicit-than-you-hoped"_s,
#endif
#ifndef MAGNUM_TARGET_GLES
@ -367,13 +366,13 @@ const char* KnownWorkarounds[]{
GL_IMPLEMENTATION_COLOR_READ_FORMAT and _TYPE is queried using
glGetNamedFramebufferParameter(). Using either glGetInteger() or
glGetFramebufferParameter() works correctly. */
"nv-implementation-color-read-format-dsa-broken",
"nv-implementation-color-read-format-dsa-broken"_s,
#endif
#ifndef MAGNUM_TARGET_GLES
/* ApiTrace needs an explicit initial glViewport() call to initialize its
framebuffer size, otherwise it assumes it's zero-sized. */
"apitrace-zero-initial-viewport",
"apitrace-zero-initial-viewport"_s,
#endif
#if defined(MAGNUM_TARGET_WEBGL) && !defined(MAGNUM_TARGET_GLES2)
@ -386,11 +385,29 @@ const char* KnownWorkarounds[]{
https://www.khronos.org/webgl/public-mailing-list/public_webgl/1705/msg00015.php
and https://github.com/emscripten-core/emscripten/pull/9652 for the
Emscripten-side part of this workaround. */
"firefox-fake-disjoint-timer-query-webgl2",
"firefox-fake-disjoint-timer-query-webgl2"_s,
#endif
/* [workarounds] */
};
/* I could use std::find(), right? Well, it'd be a whole lot more typing and
an #include <algorithm> *and* #include <iterator> or whatever as well,
because apparently ONE CAN'T GET std::begin() / std::end() without including
tens thousands lines of irrelevant shit, FFS.
Also the comparison to array end to discover if it wasn't found is just a
useless verbose crap shit as well, so we'll do better here and return a null
view instead.
Since the workaround list isn't really huge for an average platform (16 on
Linux, Windows probably ~30?) and there's very few used heavily, I won't
bother with some binary search, which needs extra testing effort. */
Containers::StringView findWorkaround(Containers::StringView workaround) {
for(Containers::StringView i: KnownWorkarounds)
if(workaround == i) return i;
return {};
}
}
auto Context::detectedDriver() -> DetectedDrivers {
@ -398,33 +415,33 @@ auto Context::detectedDriver() -> DetectedDrivers {
_detectedDrivers = DetectedDrivers{};
const std::string renderer = rendererString();
const std::string vendor = vendorString();
const std::string version = versionString();
const Containers::StringView renderer = rendererString();
const Containers::StringView vendor = vendorString();
const Containers::StringView version = versionString();
/* Apple has its own drivers */
#if !defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_WEBGL)
/* AMD binary desktop drivers */
if(vendor.find("ATI Technologies Inc.") != std::string::npos)
if(vendor.contains("ATI Technologies Inc."_s))
return *_detectedDrivers |= DetectedDriver::Amd;
#ifdef CORRADE_TARGET_WINDOWS
/* Intel Windows drivers */
if(vendor.find("Intel") != std::string::npos)
if(vendor.contains("Intel"_s))
return *_detectedDrivers |= DetectedDriver::IntelWindows;
#endif
/* Mesa drivers */
if(version.find("Mesa") != std::string::npos) {
if(version.contains("Mesa"_s)) {
*_detectedDrivers |= DetectedDriver::Mesa;
if(renderer.find("SVGA3D") != std::string::npos)
if(renderer.contains("SVGA3D"_s))
*_detectedDrivers |= DetectedDriver::Svga3D;
return *_detectedDrivers;
}
if(vendor.find("NVIDIA Corporation") != std::string::npos)
if(vendor.contains("NVIDIA Corporation"_s))
return *_detectedDrivers |= DetectedDriver::NVidia;
#endif
@ -433,53 +450,63 @@ auto Context::detectedDriver() -> DetectedDrivers {
/* ANGLE. Can detect easily on ES, have to resort to hacks on WebGL.
Sources: http://stackoverflow.com/a/20149090 + http://webglreport.com */
#ifndef MAGNUM_TARGET_WEBGL
if(renderer.find("ANGLE") != std::string::npos)
if(renderer.contains("ANGLE"_s))
return *_detectedDrivers |= DetectedDriver::Angle;
#else
{
Range1Di range;
glGetIntegerv(GL_ALIASED_LINE_WIDTH_RANGE, range.data());
if(range.min() == 1 && range.max() == 1 && vendor != "Internet Explorer")
if(range.min() == 1 && range.max() == 1 && vendor != "Internet Explorer"_s)
return *_detectedDrivers |= DetectedDriver::Angle;
}
#endif
#ifndef MAGNUM_TARGET_WEBGL
/* SwiftShader */
if(renderer.find("SwiftShader") != std::string::npos)
if(renderer.contains("SwiftShader"_s))
return *_detectedDrivers |= DetectedDriver::SwiftShader;
#endif
#endif
#ifdef CORRADE_TARGET_ANDROID
if(vendor.find("ARM") != std::string::npos && renderer.find("Mali") != std::string::npos)
if(vendor.contains("ARM"_s) && renderer.contains("Mali"_s))
return *_detectedDrivers |= DetectedDriver::ArmMali;
#endif
return *_detectedDrivers;
}
void Context::disableDriverWorkaround(const std::string& workaround) {
void Context::disableDriverWorkaround(const Containers::StringView workaround) {
/* Find the workaround. Note that we'll add the found view to the array
and not the passed view, as the found view is guaranteed to stay in
scope */
const Containers::StringView found = findWorkaround(workaround);
/* Ignore unknown workarounds */
/** @todo this will probably cause false positives when both GL and Vulkan
is used together? */
if(std::find(std::begin(KnownWorkarounds), std::end(KnownWorkarounds), workaround) == std::end(KnownWorkarounds)) {
Warning() << "GL: unknown workaround" << workaround;
if(found.isEmpty()) {
Warning{} << "GL: unknown workaround" << workaround;
return;
}
_driverWorkarounds.emplace_back(workaround, true);
arrayAppend(_driverWorkarounds, Containers::InPlaceInit, found, true);
}
bool Context::isDriverWorkaroundDisabled(const char* workaround) {
CORRADE_INTERNAL_ASSERT(std::find_if(std::begin(KnownWorkarounds), std::end(KnownWorkarounds), [&](const char* a) {
return std::strcmp(a, workaround) == 0;
}) != std::end(KnownWorkarounds));
bool Context::isDriverWorkaroundDisabled(const Containers::StringView workaround) {
/* Find the workaround. Note that we'll add the found view to the array
and not the passed view, as the found view is guaranteed to stay in
scope */
Containers::StringView found = findWorkaround(workaround);
CORRADE_INTERNAL_ASSERT(!found.isEmpty());
/* If the workaround was already asked for or disabled, return its state,
otherwise add it to the list as used one */
otherwise add it to the list as used one. Here we again cheat a bit and
compare just data pointers instead of the whole string as we store only
the views in the KnownWorkarounds list. */
for(const auto& i: _driverWorkarounds)
if(i.first == workaround) return i.second;
_driverWorkarounds.emplace_back(workaround, false);
if(i.first.data() == found.data()) return i.second;
arrayAppend(_driverWorkarounds, Containers::InPlaceInit, found, false);
return false;
}
@ -489,17 +516,17 @@ void Context::setupDriverWorkarounds() {
_extensionRequiredVersion[Extensions::extension::Index] = Version::version
#ifndef MAGNUM_TARGET_GLES
if(!isDriverWorkaroundDisabled("no-layout-qualifiers-on-old-glsl")) {
if(!isDriverWorkaroundDisabled("no-layout-qualifiers-on-old-glsl"_s)) {
_setRequiredVersion(ARB::explicit_attrib_location, GL320);
_setRequiredVersion(ARB::explicit_uniform_location, GL320);
_setRequiredVersion(ARB::shading_language_420pack, GL320);
}
#ifdef CORRADE_TARGET_WINDOWS
if((detectedDriver() & DetectedDriver::IntelWindows) && !isExtensionSupported<Extensions::ARB::shading_language_420pack>() && !isDriverWorkaroundDisabled("intel-windows-glsl-exposes-unsupported-shading-language-420pack"))
if((detectedDriver() & DetectedDriver::IntelWindows) && !isExtensionSupported<Extensions::ARB::shading_language_420pack>() && !isDriverWorkaroundDisabled("intel-windows-glsl-exposes-unsupported-shading-language-420pack"_s))
_setRequiredVersion(ARB::shading_language_420pack, None);
if((detectedDriver() & DetectedDriver::IntelWindows) && isExtensionSupported<Extensions::ARB::explicit_uniform_location>() && !isDriverWorkaroundDisabled("intel-windows-explicit-uniform-location-is-less-explicit-than-you-hoped")) {
if((detectedDriver() & DetectedDriver::IntelWindows) && isExtensionSupported<Extensions::ARB::explicit_uniform_location>() && !isDriverWorkaroundDisabled("intel-windows-explicit-uniform-location-is-less-explicit-than-you-hoped"_s)) {
_setRequiredVersion(ARB::explicit_uniform_location, None);
}
#endif
@ -508,7 +535,7 @@ void Context::setupDriverWorkarounds() {
#ifndef MAGNUM_TARGET_GLES
if((detectedDriver() & DetectedDriver::Svga3D) &&
isExtensionSupported<Extensions::ARB::get_texture_sub_image>() &&
!isDriverWorkaroundDisabled("svga3d-gettexsubimage-oob-write"))
!isDriverWorkaroundDisabled("svga3d-gettexsubimage-oob-write"_s))
_setRequiredVersion(ARB::get_texture_sub_image, None);
#endif
@ -516,26 +543,26 @@ void Context::setupDriverWorkarounds() {
if(detectedDriver() & Context::DetectedDriver::SwiftShader) {
if((isExtensionSupported<Extensions::ANGLE::instanced_arrays>() ||
isExtensionSupported<Extensions::EXT::instanced_arrays>()) &&
!isDriverWorkaroundDisabled("swiftshader-no-es2-draw-instanced-entrypoints")) {
!isDriverWorkaroundDisabled("swiftshader-no-es2-draw-instanced-entrypoints"_s)) {
_setRequiredVersion(ANGLE::instanced_arrays, None);
_setRequiredVersion(EXT::instanced_arrays, None);
}
if(isExtensionSupported<Extensions::OES::texture_3D>() &&
!isDriverWorkaroundDisabled("swiftshader-no-es2-oes-texture-3d-entrypoints"))
!isDriverWorkaroundDisabled("swiftshader-no-es2-oes-texture-3d-entrypoints"_s))
_setRequiredVersion(OES::texture_3D, None);
}
#endif
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
if((detectedDriver() & Context::DetectedDriver::SwiftShader) &&
!isDriverWorkaroundDisabled("swiftshader-broken-shader-vertex-id"))
!isDriverWorkaroundDisabled("swiftshader-broken-shader-vertex-id"_s))
_setRequiredVersion(MAGNUM::shader_vertex_id, None);
#endif
#if defined(CORRADE_TARGET_ANDROID) && defined(MAGNUM_TARGET_GLES)
if((detectedDriver() & Context::DetectedDriver::ArmMali) &&
std::getenv("SHELL") && !isDriverWorkaroundDisabled("arm-mali-timer-queries-oom-in-shell"))
std::getenv("SHELL") && !isDriverWorkaroundDisabled("arm-mali-timer-queries-oom-in-shell"_s))
_setRequiredVersion(EXT::disjoint_timer_query, None);
#endif
@ -565,7 +592,7 @@ void Context::setupDriverWorkarounds() {
#ifndef MAGNUM_TARGET_GLES
if(isExtensionSupported<Extensions::GREMEDY::string_marker>() &&
!isDriverWorkaroundDisabled("apitrace-zero-initial-viewport")) {
!isDriverWorkaroundDisabled("apitrace-zero-initial-viewport"_s)) {
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
@ -573,9 +600,9 @@ void Context::setupDriverWorkarounds() {
#endif
#if defined(MAGNUM_TARGET_WEBGL) && !defined(MAGNUM_TARGET_GLES2)
if(rendererString() == "Mozilla") {
if(rendererString() == "Mozilla"_s) {
for(const auto& extension: extensionStrings()) {
if(extension == "GL_EXT_disjoint_timer_query" && !isDriverWorkaroundDisabled("firefox-fake-disjoint-timer-query-webgl2")) {
if(extension == "GL_EXT_disjoint_timer_query"_s && !isDriverWorkaroundDisabled("firefox-fake-disjoint-timer-query-webgl2"_s)) {
_extensionStatus.set(Extensions::EXT::disjoint_timer_query_webgl2::Index, true);
}
}

47
src/Magnum/GL/Test/ContextGLTest.cpp

@ -24,6 +24,8 @@
*/
#include <algorithm>
#include <Corrade/Containers/StringView.h>
#include <Corrade/TestSuite/Compare/Numeric.h>
#include "Magnum/GL/Context.h"
#include "Magnum/GL/Extensions.h"
@ -39,6 +41,8 @@ namespace Magnum { namespace GL { namespace Test { namespace {
struct ContextGLTest: OpenGLTester {
explicit ContextGLTest();
void stringFlags();
void makeCurrent();
#ifndef CORRADE_TARGET_EMSCRIPTEN
@ -56,6 +60,8 @@ struct ContextGLTest: OpenGLTester {
ContextGLTest::ContextGLTest() {
addTests({
&ContextGLTest::stringFlags,
&ContextGLTest::makeCurrent,
#ifndef CORRADE_TARGET_EMSCRIPTEN
@ -71,6 +77,45 @@ ContextGLTest::ContextGLTest() {
&ContextGLTest::isExtensionDisabled});
}
void ContextGLTest::stringFlags() {
Context& context = Context::current();
CORRADE_VERIFY(!context.vendorString().isEmpty());
CORRADE_COMPARE(context.vendorString().flags(), Containers::StringViewFlag::Global|Containers::StringViewFlag::NullTerminated);
CORRADE_VERIFY(!context.rendererString().isEmpty());
CORRADE_COMPARE(context.rendererString().flags(), Containers::StringViewFlag::Global|Containers::StringViewFlag::NullTerminated);
CORRADE_VERIFY(!context.versionString().isEmpty());
CORRADE_COMPARE(context.versionString().flags(), Containers::StringViewFlag::Global|Containers::StringViewFlag::NullTerminated);
CORRADE_VERIFY(!context.shadingLanguageVersionString().isEmpty());
CORRADE_COMPARE(context.shadingLanguageVersionString().flags(), Containers::StringViewFlag::Global|Containers::StringViewFlag::NullTerminated);
for(Containers::StringView languageVersion: context.shadingLanguageVersionStrings()) {
/* One of these might be empty */
CORRADE_COMPARE(languageVersion.flags(), Containers::StringViewFlag::Global|Containers::StringViewFlag::NullTerminated);
}
for(Containers::StringView extension: context.extensionStrings()) {
CORRADE_VERIFY(!extension.isEmpty());
/* On GL 2.1 and GLES2 the extensions are split from a long string and
thus aren't all null-terminated, only the last one */
#ifndef MAGNUM_TARGET_GLES
if(context.isVersionSupported(Version::GL300))
#else
if(context.isVersionSupported(Version::GLES300))
#endif
{
CORRADE_COMPARE(extension.flags(), Containers::StringViewFlag::Global|Containers::StringViewFlag::NullTerminated);
} else {
CORRADE_COMPARE_AS(extension.flags(), Containers::StringViewFlag::Global,
TestSuite::Compare::GreaterOrEqual);
}
}
}
void ContextGLTest::makeCurrent() {
CORRADE_VERIFY(Context::hasCurrent());
@ -166,7 +211,7 @@ void ContextGLTest::isExtensionSupported() {
CORRADE_SKIP(Extensions::ARB::explicit_attrib_location::string() + std::string(" extension should be supported, can't test"));
/* Test that we have proper extension list parser */
std::vector<std::string> extensions = Context::current().extensionStrings();
Containers::Array<Containers::StringView> extensions = Context::current().extensionStrings();
CORRADE_VERIFY(std::find(extensions.begin(), extensions.end(),
Extensions::EXT::texture_filter_anisotropic::string()) != extensions.end());
CORRADE_VERIFY(std::find(extensions.begin(), extensions.end(),

1
src/Magnum/Platform/WindowlessGlxApplication.cpp

@ -27,6 +27,7 @@
#include "WindowlessGlxApplication.h"
#include <cstring>
#include <Corrade/Containers/StringView.h>
#include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/Debug.h>

1
src/Magnum/Platform/WindowlessWglApplication.cpp

@ -26,6 +26,7 @@
#include "WindowlessWglApplication.h"
#include <cstring>
#include <Corrade/Containers/StringView.h>
#include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/Debug.h>

4
src/Magnum/Platform/gl-info.cpp

@ -195,6 +195,8 @@ class MagnumInfo: public Platform::WindowlessApplication {
int exec() override { return 0; }
};
using namespace Containers::Literals;
MagnumInfo::MagnumInfo(const Arguments& arguments): Platform::WindowlessApplication{arguments, NoCreate} {
Utility::Arguments args;
args.addBooleanOption('s', "short").setHelp("short", "display just essential info and exit")
@ -359,7 +361,7 @@ MagnumInfo::MagnumInfo(const Arguments& arguments): Platform::WindowlessApplicat
Debug{} << "Detected driver:" << c.detectedDriver();
Debug{} << "Supported GLSL versions:";
Debug{} << " " << Utility::String::joinWithoutEmptyParts(c.shadingLanguageVersionStrings(), ", ");
Debug{} << " " << ", "_s.joinWithoutEmptyParts(c.shadingLanguageVersionStrings());
if(args.isSet("extension-strings")) {
Debug{} << "Extension strings:" << Debug::newline

11
src/Magnum/Shaders/Test/PhongGLTest.cpp

@ -26,6 +26,7 @@
#include <sstream>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Containers/StringView.h>
#include <Corrade/PluginManager/Manager.h>
#include <Corrade/TestSuite/Compare/Numeric.h>
#include <Corrade/Utility/DebugStl.h>
@ -1246,13 +1247,13 @@ void PhongGLTest::renderShininess() {
"ARM Mali has a much larger ring for the overflown shininess when it's exactly 0.");
#endif
#ifndef MAGNUM_TARGET_WEBGL
CORRADE_EXPECT_FAIL_IF(data.shininess == 0.0f && (GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::Mesa) && GL::Context::current().rendererString().find("AMD") != std::string::npos,
CORRADE_EXPECT_FAIL_IF(data.shininess == 0.0f && (GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::Mesa) && GL::Context::current().rendererString().contains("AMD"),
"AMD Mesa drivers have a much larger ring for the overflown shininess when it's exactly 0.");
CORRADE_EXPECT_FAIL_IF(data.shininess <= 0.0011f && (GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::Mesa) && GL::Context::current().rendererString().find("llvmpipe") != std::string::npos,
CORRADE_EXPECT_FAIL_IF(data.shininess <= 0.0011f && (GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::Mesa) && GL::Context::current().rendererString().contains("llvmpipe"),
"Mesa llvmpipe drivers have a much larger ring for the overflown shininess.");
#endif
#if defined(CORRADE_TARGET_APPLE) && !defined(CORRADE_TARGET_IOS)
CORRADE_EXPECT_FAIL_IF(data.shininess == 0.0f && GL::Context::current().rendererString().find("AMD") != std::string::npos,
CORRADE_EXPECT_FAIL_IF(data.shininess == 0.0f && GL::Context::current().rendererString().contains("AMD"),
"AMD on macOS has a much larger ring for the overflown shininess when it's exactly 0.");
#endif
CORRADE_COMPARE_WITH(
@ -1268,10 +1269,10 @@ void PhongGLTest::renderShininess() {
|| (data.shininess <= 0.0011f && (GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader))
#endif
#ifndef MAGNUM_TARGET_WEBGL
|| (data.shininess == 0.0f && (GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::Mesa) && GL::Context::current().rendererString().find("AMD") != std::string::npos)
|| (data.shininess == 0.0f && (GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::Mesa) && GL::Context::current().rendererString().contains("AMD"))
#endif
#if defined(CORRADE_TARGET_APPLE) && !defined(CORRADE_TARGET_IOS)
|| (data.shininess == 0.0f && GL::Context::current().rendererString().find("AMD") != std::string::npos)
|| (data.shininess == 0.0f && GL::Context::current().rendererString().contains("AMD"))
#endif
#if defined(CORRADE_TARGET_ANDROID) && defined(MAGNUM_TARGET_GLES2)
|| (data.shininess == 0.0f && (GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::ArmMali))

1
src/MagnumExternal/OpenGL/GL/flextGLPlatform.cpp vendored

@ -33,6 +33,7 @@
#ifdef MAGNUM_PLATFORM_USE_EGL
#include <cstring>
#include <EGL/egl.h>
#include <Corrade/Containers/StringView.h>
#include "Magnum/GL/Context.h"
#endif

1
src/MagnumExternal/OpenGL/GL/flextGLPlatform.cpp.template vendored

@ -34,6 +34,7 @@
#ifdef MAGNUM_PLATFORM_USE_EGL
#include <cstring>
#include <EGL/egl.h>
#include <Corrade/Containers/StringView.h>
#include "Magnum/GL/Context.h"
#endif

Loading…
Cancel
Save