You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

552 lines
20 KiB

#ifndef Magnum_Context_h
#define Magnum_Context_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015
Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
/** @file
* @brief Class @ref Magnum::Context, @ref Magnum::Extension, macro @ref MAGNUM_ASSERT_VERSION_SUPPORTED(), @ref MAGNUM_ASSERT_EXTENSION_SUPPORTED()
*/
#include <cstdlib>
#include <array>
#include <bitset>
#include <vector>
#include <Corrade/Containers/EnumSet.h>
#include "Magnum/Magnum.h"
#include "Magnum/OpenGL.h"
#include "Magnum/visibility.h"
#include "MagnumExternal/Optional/optional.hpp"
namespace Magnum {
namespace Implementation { struct State; }
namespace Platform { class Context; }
/**
@brief Run-time information about OpenGL extension
Encapsulates runtime information about OpenGL extension, such as name string,
minimal required OpenGL version and version in which the extension was adopted
to core.
See also @ref Extensions namespace, which contain compile-time information
about OpenGL extensions.
*/
class MAGNUM_EXPORT Extension {
friend Context;
public:
/** @brief All extensions for given OpenGL version */
static const std::vector<Extension>& extensions(Version version);
/** @brief Minimal version required by this extension */
constexpr Version requiredVersion() const { return _requiredVersion; }
/** @brief Version in which this extension was adopted to core */
constexpr Version coreVersion() const { return _coreVersion; }
/** @brief Extension string */
constexpr const char* string() const { return _string; }
private:
/* GCC 4.6 doesn't like const members, as std::vector doesn't have
proper move semantic yet */
std::size_t _index;
Version _requiredVersion;
Version _coreVersion;
const char* _string;
constexpr Extension(std::size_t index, Version requiredVersion, Version coreVersion, const char* string): _index(index), _requiredVersion(requiredVersion), _coreVersion(coreVersion), _string(string) {}
};
/**
@brief Magnum context
Provides access to version and extension information. Instance available
through @ref Context::current() is automatically created during construction of
`*Application` classes in @ref Platform namespace. You can safely assume that
the instance is available during whole lifetime of `*Application` object. It's
also possible to create the context without using any `*Application` class
using @ref Platform::Context subclass, see @ref platform documentation for more
information.
*/
class MAGNUM_EXPORT Context {
friend Platform::Context;
public:
#ifndef MAGNUM_TARGET_WEBGL
/**
* @brief Context flag
*
* @see @ref Flags, @ref flags(),
* @ref Platform::Sdl2Application::Configuration::setFlags() "Platform::*Application::Configuration::setFlags()"
* @requires_gles Context flags are not available in WebGL.
*/
enum class Flag: GLint {
/**
* Debug context
* @requires_gl43 Extension @extension{KHR,debug}
* @requires_es_extension Extension @es_extension{ANDROID,extension_pack_es31a}/
* @es_extension2{KHR,debug,debug}
*/
#ifndef MAGNUM_TARGET_GLES
Debug = GL_CONTEXT_FLAG_DEBUG_BIT,
#else
Debug = GL_CONTEXT_FLAG_DEBUG_BIT_KHR,
#endif
#ifndef MAGNUM_TARGET_GLES
/**
* Context with robust access
* @requires_extension Extension @extension{ARB,robustness}
* @requires_es_extension Extension @es_extension{EXT,robustness}
* @todo In ES available under glGetIntegerv(CONTEXT_ROBUST_ACCESS_EXT),
* how to make it compatible?
*/
RobustAccess = GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB
#endif
};
/**
* @brief Context flags
*
* @see @ref flags()
* @requires_gles Context flags are not available in WebGL.
*/
typedef Containers::EnumSet<Flag> Flags;
#endif
/**
* @brief State to reset
*
* @see @ref States, @ref resetState()
*/
enum class State: UnsignedInt {
/** Reset tracked buffer-related bindings and state */
Buffers = 1 << 0,
/** Reset tracked framebuffer-related bindings and state */
Framebuffers = 1 << 1,
/** Reset tracked mesh-related bindings */
Meshes = 1 << 2,
/** Reset tracked pixel storage-related state */
PixelStorage = 1 << 3,
/** Reset tracked renderer-related state */
Renderer = 1 << 4,
/** Reset tracked shader-related bindings */
Shaders = 1 << 5,
/** Reset tracked texture-related bindings and state */
Textures = 1 << 6,
#ifndef MAGNUM_TARGET_GLES2
/** Reset tracked transform feedback-related bindings */
TransformFeedback = 1 << 7
#endif
};
/**
* @brief States to reset
*
* @see @ref resetState()
*/
typedef Containers::EnumSet<State> States;
/**
* @brief Detected driver
*
* @see @ref DetectedDriver, @ref detectedDriver()
*/
enum class DetectedDriver: UnsignedShort {
#ifndef MAGNUM_TARGET_GLES
/** Binary AMD desktop drivers on Windows and Linux */
AMD = 1 << 0,
/** Intel desktop drivers on Windows */
IntelWindows = 1 << 1,
#endif
#ifdef MAGNUM_TARGET_GLES
/**
* OpenGL ES implementation by ANGLE (translated to D3D), used by
* browsers on Windows for Native Client and WebGL. As the WebGL
* specification explicitly disallows exposing driver information
* to the application, this check cannot be done reliably.
*/
ProbablyAngle = 1 << 2
#endif
};
/**
* @brief Detected drivers
*
* @see @ref detectedDriver()
*/
typedef Containers::EnumSet<DetectedDriver> DetectedDrivers;
/** @brief Copying is not allowed */
Context(const Context&) = delete;
/** @brief Move constructor */
Context(Context&& other);
~Context();
/** @brief Copying is not allowed */
Context& operator=(const Context&) = delete;
/** @brief Move assignment is not allowed */
Context& operator=(Context&&) = delete;
/** @brief Current context */
static Context* current() { return _current; }
/**
* @brief OpenGL version
*
* @see @ref versionString(), @ref shadingLanguageVersionString()
*/
Version version() const { return _version; }
/**
* @brief Vendor string
*
* The result is *not* cached, repeated queries will result in repeated
* OpenGL calls.
* @see @ref rendererString(), @fn_gl{GetString} with @def_gl{VENDOR}
*/
std::string vendorString() const {
return reinterpret_cast<const char*>(glGetString(GL_VENDOR));
}
/**
* @brief Renderer string
*
* The result is *not* cached, repeated queries will result in repeated
* OpenGL calls.
* @see @ref vendorString(), @fn_gl{GetString} with @def_gl{RENDERER}
*/
std::string rendererString() const {
return reinterpret_cast<const char*>(glGetString(GL_RENDERER));
}
/**
* @brief Version string
*
* The result is *not* cached, repeated queries will result in repeated
* OpenGL calls.
* @see @ref shadingLanguageVersionString(), @ref version(),
* @fn_gl{GetString} with @def_gl{VERSION}
*/
std::string versionString() const {
return reinterpret_cast<const char*>(glGetString(GL_VERSION));
}
/**
* @brief Shading language version string
*
* The result is *not* cached, repeated queries will result in repeated
* OpenGL calls.
* @see @ref versionString(), @ref version(), @fn_gl{GetString} with
* @def_gl{SHADING_LANGUAGE_VERSION}
*/
std::string shadingLanguageVersionString() const {
return reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
}
/**
* @brief Shading language version strings
*
* The result is *not* cached, repeated queries will result in repeated
* OpenGL calls.
* @see @ref versionString(), @ref version(), @fn_gl{Get} with
* @def_gl{NUM_SHADING_LANGUAGE_VERSIONS}, @fn_gl{GetString} with
* @def_gl{SHADING_LANGUAGE_VERSION}
*/
std::vector<std::string> shadingLanguageVersionStrings() const;
/**
* @brief Extension strings
*
* The result is *not* cached, repeated queries will result in repeated
* 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.
* @see @fn_gl{Get} with @def_gl{NUM_EXTENSIONS}, @fn_gl{GetString}
* with @def_gl{EXTENSIONS}
*/
std::vector<std::string> extensionStrings() const;
#ifndef MAGNUM_TARGET_WEBGL
/**
* @brief Context flags
*
* @requires_gles Context flags are not available in WebGL.
*/
Flags flags() const { return _flags; }
#endif
/**
* @brief Supported extensions
*
* The list contains only extensions from OpenGL versions newer than
* the current.
* @see @ref isExtensionSupported(), @ref Extension::extensions()
*/
const std::vector<Extension>& supportedExtensions() const {
return _supportedExtensions;
}
/**
* @brief Whether given OpenGL version is supported
*
* @see @ref supportedVersion(), @ref MAGNUM_ASSERT_VERSION_SUPPORTED()
*/
bool isVersionSupported(Version version) const {
return _version >= version;
}
/**
* @brief Get supported OpenGL version
*
* Returns first supported OpenGL version from passed list. Convenient
* equivalent to subsequent @ref isVersionSupported() calls, e.g.:
* @code
* Version v = isVersionSupported(Version::GL330) ? Version::GL330 : Version::GL210;
* Version v = supportedVersion({Version::GL330, Version::GL210});
* @endcode
*
* If no version from the list is supported, returns lowest available
* OpenGL version (@ref Version::GL210 for desktop OpenGL,
* @ref Version::GLES200 for OpenGL ES).
* @see @ref isExtensionSupported(Version) const
*/
Version supportedVersion(std::initializer_list<Version> versions) const;
/**
* @brief Whether given extension is supported
*
* Extensions usable with this function are listed in @ref Extensions
* namespace in header @ref Extensions.h. Example usage:
* @code
* if(Context::current()->isExtensionSupported<Extensions::GL::ARB::tessellation_shader>()) {
* // draw fancy detailed model
* } else {
* // texture fallback
* }
* @endcode
*
* @see @ref isExtensionSupported(const Extension&) const,
* @ref MAGNUM_ASSERT_EXTENSION_SUPPORTED(),
* @ref isExtensionDisabled()
*/
template<class T> bool isExtensionSupported() const {
return isExtensionSupported<T>(version());
}
/**
* @brief Whether given extension is supported in given version
*
* Similar to @ref isExtensionSupported(), but checks also that the
* minimal required version of the extension is larger or equal to
* @p version. Useful mainly in shader compilation when the decisions
* depend on selected GLSL version, for example:
* @code
* const Version version = Context::current()->supportedVersion({Version::GL320, Version::GL300, Version::GL210});
* if(Context::current()->isExtensionSupported<Extensions::GL::ARB::explicit_attrib_location>(version)) {
* // Called only if ARB_explicit_attrib_location is supported
* // *and* version is higher than GL 3.1
* }
* @endcode
*/
template<class T> bool isExtensionSupported(Version version) const {
return _extensionRequiredVersion[T::Index] <= version && _extensionStatus[T::Index];
}
/**
* @brief Whether given extension is supported
*
* Can be used e.g. for listing extensions available on current
* hardware, but for general usage prefer @ref isExtensionSupported() const,
* as it does most operations in compile time.
* @see @ref supportedExtensions(), @ref Extension::extensions(),
* @ref MAGNUM_ASSERT_EXTENSION_SUPPORTED()
*/
bool isExtensionSupported(const Extension& extension) const {
return isVersionSupported(_extensionRequiredVersion[extension._index]) && _extensionStatus[extension._index];
}
/**
* @brief Whether given extension is disabled
*
* Can be used for detecting driver bug workarounds. Disabled
* extensions return `false` in @ref isExtensionSupported() even if
* they are advertised as being supported by the driver.
*/
template<class T> bool isExtensionDisabled() const {
return isExtensionDisabled<T>(version());
}
/**
* @brief Whether given extension is disabled for given version
*
* Similar to above, but can also check for extensions which are
* disabled only for particular versions.
*/
template<class T> bool isExtensionDisabled(Version version) const {
/* The extension is advertised, but the minimal version has been increased */
return T::requiredVersion() <= version && _extensionRequiredVersion[T::Index] > version;
}
/**
* @brief Whether given extension is disabled
*
* Can be used e.g. for listing extensions available on current
* hardware, but for general usage prefer @ref isExtensionDisabled() const,
* as it does most operations in compile time.
*/
bool isExtensionDisabled(const Extension& extension) const {
return isVersionSupported(extension._requiredVersion) && !isVersionSupported(_extensionRequiredVersion[extension._index]);
}
/**
* @brief Reset internal state tracker
* @param states Tracked states to reset. Default is all state.
*
* The engine internally tracks object bindings and other state to
* avoid redundant OpenGL calls. In some cases (e.g. when non-Magnum
* code makes GL calls) the internal tracker no longer reflects actual
* state and needs to be reset to avoid strange issues.
*/
void resetState(States states = ~States{});
/**
* @brief Detect driver
*
* Tries to detect driver using various OpenGL state queries. Once the
* detection is done, the result is cached, repeated queries don't
* result in repeated GL calls.
*/
DetectedDrivers detectedDriver();
#ifndef DOXYGEN_GENERATING_OUTPUT
Implementation::State& state() { return *_state; }
#endif
private:
static Context* _current;
explicit Context(void functionLoader());
/* Defined in Implementation/driverSpecific.cpp */
MAGNUM_LOCAL void setupDriverWorkarounds();
Version _version;
#ifndef MAGNUM_TARGET_WEBGL
Flags _flags;
#endif
std::array<Version, 256> _extensionRequiredVersion;
std::bitset<256> _extensionStatus;
std::vector<Extension> _supportedExtensions;
Implementation::State* _state;
std::optional<DetectedDrivers> _detectedDrivers;
};
#ifndef MAGNUM_TARGET_WEBGL
/** @debugoperatorclassenum{Magnum::Context,Magnum::Context::Flag} */
MAGNUM_EXPORT Debug& operator<<(Debug& debug, Context::Flag value);
#endif
/** @hideinitializer
@brief Assert that given OpenGL version is supported
@param version Version
Useful for initial checks on availability of required features.
By default, if assertion fails, an message is printed to error output and the
application aborts. If `CORRADE_NO_ASSERT` is defined, this macro does nothing.
Example usage:
@code
MAGNUM_ASSERT_VERSION_SUPPORTED(Version::GL330);
@endcode
@see @ref Magnum::Context::isVersionSupported() "Context::isVersionSupported()",
@ref MAGNUM_ASSERT_EXTENSION_SUPPORTED(), @ref CORRADE_ASSERT(),
@ref CORRADE_INTERNAL_ASSERT()
*/
#ifdef CORRADE_NO_ASSERT
#define MAGNUM_ASSERT_VERSION_SUPPORTED(version) do {} while(0)
#else
#define MAGNUM_ASSERT_VERSION_SUPPORTED(version) \
do { \
if(!Magnum::Context::current()->isVersionSupported(version)) { \
Corrade::Utility::Error() << "Magnum: required version" << version << "is not supported"; \
std::abort(); \
} \
} while(0)
#endif
/** @hideinitializer
@brief Assert that given OpenGL extension is supported
@param extension Extension name (from @ref Magnum::Extensions "Extensions"
namespace)
Useful for initial checks on availability of required features.
By default, if assertion fails, an message is printed to error output and the
application aborts. If `CORRADE_NO_ASSERT` is defined, this macro does nothing.
Example usage:
@code
MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::ARB::geometry_shader4);
@endcode
@see @ref Magnum::Context::isExtensionSupported() "Context::isExtensionSupported()",
@ref MAGNUM_ASSERT_VERSION_SUPPORTED(), @ref CORRADE_ASSERT(),
@ref CORRADE_INTERNAL_ASSERT()
*/
#ifdef CORRADE_NO_ASSERT
#define MAGNUM_ASSERT_EXTENSION_SUPPORTED(extension) do {} while(0)
#else
#define MAGNUM_ASSERT_EXTENSION_SUPPORTED(extension) \
do { \
if(!Magnum::Context::current()->isExtensionSupported<extension>()) { \
Corrade::Utility::Error() << "Magnum: required extension" << extension::string() << "is not supported"; \
std::abort(); \
} \
} while(0)
#endif
}
#endif