diff --git a/src/Magnum/Audio/Context.cpp b/src/Magnum/Audio/Context.cpp index f2688aca7..6df502584 100644 --- a/src/Magnum/Audio/Context.cpp +++ b/src/Magnum/Audio/Context.cpp @@ -128,10 +128,16 @@ Context::Context(NoCreateT, Int argc, const char** argv) noexcept: _device{}, _c Utility::Arguments args{"magnum", Utility::Arguments::Flag::IgnoreUnknownOptions}; args.addOption("log", "default").setHelp("log", "console logging", "default|quiet|verbose") + .addOption("disable-extensions").setHelp("disable-extensions", "API extensions to disable", "LIST") .setFromEnvironment("log") .parse(argc, argv); + /* Decide how to display initialization log */ _displayInitializationLog = !(args.value("log") == "quiet" || args.value("log") == "QUIET"); + + /* Disable extensions */ + for(auto&& extension: Utility::String::splitWithoutEmptyParts(args.value("disable-extensions"))) + _disabledExtensionStrings.push_back(extension); } void Context::create(const Configuration& configuration) { @@ -211,14 +217,43 @@ bool Context::tryCreate(const Configuration& configuration) { const auto found = extensionMap.find(extension); if(found != extensionMap.end()) { _supportedExtensions.push_back(found->second); - _extensionStatus.set(found->second.index()); + _extensionStatus.set(found->second.index(), true); } } - if(_displayInitializationLog) { - /* Print some info */ - Debug() << "Audio Renderer:" << rendererString() << "by" << vendorString(); - Debug() << "OpenAL version:" << versionString(); + std::ostream* output = _displayInitializationLog ? Debug::output() : nullptr; + + /* Print some info */ + Debug{output} << "Audio renderer:" << rendererString() << "by" << vendorString(); + Debug{output} << "OpenAL version:" << versionString(); + + /* Disable extensions as requested by the user */ + if(!_disabledExtensionStrings.empty()) { + bool headerPrinted = false; + + /* Disable extensions that are known and supported and print a message + for each */ + for(auto&& extension: _disabledExtensionStrings) { + auto found = extensionMap.find(extension); + /* No error message here because some of the extensions could be + from Vulkan or OpenGL. That also means we print the header only + when we actually have something to say */ + if(found == extensionMap.end()) continue; + + /* If the extension isn't supported in the first place, don't do + anything. If it is, set its status as unsupported but flip the + corresponding bit in the disabled bitmap so we know it is + supported and only got disabled */ + if(!_extensionStatus[found->second.index()]) continue; + _extensionStatus.set(found->second.index(), false); + _disabledExtensions.set(found->second.index(), true); + + if(!headerPrinted) { + Debug{output} << "Disabling extensions:"; + headerPrinted = true; + } + Debug{output} << " " << extension; + } } return true; diff --git a/src/Magnum/Audio/Context.h b/src/Magnum/Audio/Context.h index ff5da52a3..075a3a8a0 100644 --- a/src/Magnum/Audio/Context.h +++ b/src/Magnum/Audio/Context.h @@ -43,6 +43,7 @@ #include "Magnum/Magnum.h" #include "Magnum/Tags.h" #include "Magnum/Audio/visibility.h" +#include "Magnum/Math/BoolVector.h" #include "MagnumExternal/OpenAL/extensions.h" namespace Magnum { namespace Audio { @@ -91,13 +92,16 @@ The context is configurable through command-line options, that can be passed for example from the `Platform::*Application` classes. Usage: @code{.sh} - [--magnum-help] [--magnum-log default|quiet|verbose] ... + [--magnum-help] [--magnum-disable-extensions LIST] + [--magnum-log default|quiet|verbose] ... @endcode Arguments: - `...` --- main application arguments (see `-h` or `--help` for details) - `--magnum-help` --- display this help message and exit +- `--magnum-disable-extensions LIST` --- API extensions to disable + (environment: `MAGNUM_DISABLE_EXTENSIONS`) - `--magnum-log default|quiet|verbose` --- console logging (environment: `MAGNUM_LOG`) (default: `default`) @@ -380,6 +384,28 @@ class MAGNUM_AUDIO_EXPORT Context { return _extensionStatus[extension.index()]; } + /** + * @brief Whether given extension is disabled + * + * Can be used for detecting driver bug workarounds. Disabled + * extensions return @cpp false @ce in @ref isExtensionSupported() even + * if they are advertised as being supported by the driver. + */ + template bool isExtensionDisabled() const { + return _disabledExtensions[T::Index]; + } + + /** + * @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 _disabledExtensions[extension.index()]; + } + private: MAGNUM_AUDIO_LOCAL static Context* _current; @@ -388,8 +414,10 @@ class MAGNUM_AUDIO_EXPORT Context { ALCdevice* _device; ALCcontext* _context; - std::bitset _extensionStatus; + Math::BoolVector _extensionStatus; + Math::BoolVector _disabledExtensions; std::vector _supportedExtensions; + std::vector _disabledExtensionStrings; }; /** diff --git a/src/Magnum/Audio/Test/ContextALTest.cpp b/src/Magnum/Audio/Test/ContextALTest.cpp index cfad54461..0ac3b282b 100644 --- a/src/Magnum/Audio/Test/ContextALTest.cpp +++ b/src/Magnum/Audio/Test/ContextALTest.cpp @@ -43,7 +43,9 @@ struct ContextALTest: TestSuite::Tester { void ignoreUnrelatedOptions(); void extensionsString(); - void isExtensionEnabled(); + void isExtensionSupported(); + void isExtensionUnsupported(); + void isExtensionDisabled(); }; ContextALTest::ContextALTest(): @@ -57,7 +59,9 @@ ContextALTest::ContextALTest(): addTests({&ContextALTest::ignoreUnrelatedOptions, &ContextALTest::extensionsString, - &ContextALTest::isExtensionEnabled}); + &ContextALTest::isExtensionSupported, + &ContextALTest::isExtensionUnsupported, + &ContextALTest::isExtensionDisabled}); } void ContextALTest::construct() { @@ -110,10 +114,43 @@ void ContextALTest::extensionsString() { CORRADE_VERIFY(!extensions.empty()); } -void ContextALTest::isExtensionEnabled() { +void ContextALTest::isExtensionSupported() { Context context; + CORRADE_VERIFY(context.isExtensionSupported()); + CORRADE_VERIFY(!context.isExtensionDisabled()); - CORRADE_VERIFY(Context::current().isExtensionSupported()); + Extension e{Extensions::ALC::EXT::ENUMERATION::Index, + Extensions::ALC::EXT::ENUMERATION::string()}; + CORRADE_VERIFY(context.isExtensionSupported(e)); + CORRADE_VERIFY(!context.isExtensionDisabled(e)); +} + +void ContextALTest::isExtensionUnsupported() { + Context context; + + if(context.isExtensionSupported()) + CORRADE_SKIP("Extension" + std::string{Extensions::ALC::SOFTX::HRTF::string()} + " is supported, can't test."); + + CORRADE_VERIFY(!context.isExtensionSupported()); + CORRADE_VERIFY(!context.isExtensionDisabled()); + + Extension e{Extensions::ALC::SOFTX::HRTF::Index, + Extensions::ALC::SOFTX::HRTF::string()}; + CORRADE_VERIFY(!context.isExtensionSupported(e)); + CORRADE_VERIFY(!context.isExtensionDisabled(e)); +} + +void ContextALTest::isExtensionDisabled() { + /* Yes, FFS. this is a weird-ass name */ + const char* argv[] = { "", "--magnum-disable-extensions", "ALC_ENUMERATION_EXT" }; + Context context{Containers::arraySize(argv), argv}; + CORRADE_VERIFY(!context.isExtensionSupported()); + CORRADE_VERIFY(context.isExtensionDisabled()); + + Extension e{Extensions::ALC::EXT::ENUMERATION::Index, + Extensions::ALC::EXT::ENUMERATION::string()}; + CORRADE_VERIFY(!context.isExtensionSupported(e)); + CORRADE_VERIFY(context.isExtensionDisabled(e)); } }}}} diff --git a/src/Magnum/Audio/al-info.cpp b/src/Magnum/Audio/al-info.cpp index 211abc99c..cb4895537 100644 --- a/src/Magnum/Audio/al-info.cpp +++ b/src/Magnum/Audio/al-info.cpp @@ -140,6 +140,8 @@ int main(const int argc, const char** const argv) { d << " " << extensionName << std::string(60-extensionName.size(), ' '); if(c.isExtensionSupported(extension)) d << "SUPPORTED"; + else if(c.isExtensionDisabled(extension)) + d << " removed"; else d << " -"; } diff --git a/src/Magnum/GL/Context.cpp b/src/Magnum/GL/Context.cpp index 7873654a3..5e76d8593 100644 --- a/src/Magnum/GL/Context.cpp +++ b/src/Magnum/GL/Context.cpp @@ -465,7 +465,7 @@ Context::Context(NoCreateT, Utility::Arguments& args, Int argc, const char** arg CORRADE_INTERNAL_ASSERT(args.prefix() == "magnum"); args.addOption("disable-workarounds") .setHelp("disable-workarounds", "driver workarounds to disable\n (see https://doc.magnum.graphics/magnum/opengl-workarounds.html for detailed info)", "LIST") - .addOption("disable-extensions").setHelp("disable-extensions", "OpenGL extensions to disable", "LIST") + .addOption("disable-extensions").setHelp("disable-extensions", "API extensions to disable", "LIST") .addOption("gpu-validation", "off").setHelp("gpu-validation", "GPU validation using KHR_debug (if present)", "off|on") .addOption("log", "default").setHelp("log", "console logging", "default|quiet|verbose") .setFromEnvironment("disable-workarounds") @@ -713,7 +713,7 @@ bool Context::tryCreate() { /* Disable extensions as requested by the user */ if(!_disabledExtensions.empty()) { - Debug{output} << "Disabling extensions:"; + bool headerPrinted = false; /* Put remaining extensions into the hashmap for faster lookup */ std::unordered_map allExtensions{std::move(futureExtensions)}; @@ -725,10 +725,16 @@ bool Context::tryCreate() { for each */ for(auto&& extension: _disabledExtensions) { auto found = allExtensions.find(extension); - /** @todo Error message here? I should not clutter the output at this point */ + /* 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 == allExtensions.end()) continue; _extensionRequiredVersion[found->second.index()] = Version::None; + if(!headerPrinted) { + Debug{output} << "Disabling extensions:"; + headerPrinted = true; + } Debug{output} << " " << extension; } } diff --git a/src/Magnum/GL/Context.h b/src/Magnum/GL/Context.h index 1f9543f7a..5599ad2ff 100644 --- a/src/Magnum/GL/Context.h +++ b/src/Magnum/GL/Context.h @@ -134,7 +134,7 @@ Arguments: - `--magnum-disable-workarounds LIST` --- driver workarounds to disable (see @ref opengl-workarounds for detailed info) (environment: `MAGNUM_DISABLE_WORKAROUNDS`) -- `--magnum-disable-extensions LIST` --- OpenGL extensions to disable +- `--magnum-disable-extensions LIST` --- API extensions to disable (environment: `MAGNUM_DISABLE_EXTENSIONS`) - `--magnum-gpu-validation off|on` --- GPU validation using @gl_extension{KHR,debug}, if present (environment: