// // Copyright (C) 2002-2005 3Dlabs Inc. Ltd. // Copyright (C) 2012-2013 LunarG, Inc. // Copyright (C) 2017, 2022-2024 Arm Limited. // Copyright (C) 2015-2020 Google, Inc. // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // // Neither the name of 3Dlabs Inc. Ltd. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // // Help manage multiple profiles, versions, extensions etc. // // These don't return error codes, as the presumption is parsing will // always continue as if the tested feature were enabled, and thus there // is no error recovery needed. // // // HOW TO add a feature enabled by an extension. // // To add a new hypothetical "Feature F" to the front end, where an extension // "XXX_extension_X" can be used to enable the feature, do the following. // // OVERVIEW: Specific features are what are error-checked for, not // extensions: A specific Feature F might be enabled by an extension, or a // particular version in a particular profile, or a stage, or combinations, etc. // // The basic mechanism is to use the following to "declare" all the things that // enable/disable Feature F, in a code path that implements Feature F: // // requireProfile() // profileRequires() // requireStage() // checkDeprecated() // requireNotRemoved() // requireExtensions() // extensionRequires() // // Typically, only the first two calls are needed. They go into a code path that // implements Feature F, and will log the proper error/warning messages. Parsing // will then always continue as if the tested feature was enabled. // // There is typically no if-testing or conditional parsing, just insertion of the calls above. // However, if symbols specific to the extension are added (step 5), they will // only be added under tests that the minimum version and profile are present. // // 1) Add a symbol name for the extension string at the bottom of Versions.h: // // const char* const XXX_extension_X = "XXX_extension_X"; // // 2) Add extension initialization to TParseVersions::initializeExtensionBehavior(), // the first function below and optionally a entry to extensionData for additional // error checks: // // extensionBehavior[XXX_extension_X] = EBhDisable; // (Optional) exts[] = {XXX_extension_X, EShTargetSpv_1_4} // // 3) Add any preprocessor directives etc. in the next function, TParseVersions::getPreamble(): // // "#define XXX_extension_X 1\n" // // The new-line is important, as that ends preprocess tokens. // // 4) Insert a profile check in the feature's path (unless all profiles support the feature, // for some version level). That is, call requireProfile() to constrain the profiles, e.g.: // // // ... in a path specific to Feature F... // requireProfile(loc, // ECoreProfile | ECompatibilityProfile, // "Feature F"); // // 5) For each profile that supports the feature, insert version/extension checks: // // The mostly likely scenario is that Feature F can only be used with a // particular profile if XXX_extension_X is present or the version is // high enough that the core specification already incorporated it. // // // following the requireProfile() call... // profileRequires(loc, // ECoreProfile | ECompatibilityProfile, // 420, // 0 if no version incorporated the feature into the core spec. // XXX_extension_X, // can be a list of extensions that all add the feature // "Feature F Description"); // // This allows the feature if either A) one of the extensions is enabled or // B) the version is high enough. If no version yet incorporates the feature // into core, pass in 0. // // This can be called multiple times, if different profiles support the // feature starting at different version numbers or with different // extensions. // // This must be called for each profile allowed by the initial call to requireProfile(). // // Profiles are all masks, which can be "or"-ed together. // // ENoProfile // ECoreProfile // ECompatibilityProfile // EEsProfile // // The ENoProfile profile is only for desktop, before profiles showed up in version 150; // All other #version with no profile default to either es or core, and so have profiles. // // You can select all but a particular profile using ~. The following basically means "desktop": // // ~EEsProfile // // 6) If built-in symbols are added by the extension, add them in Initialize.cpp: Their use // will be automatically error checked against the extensions enabled at that moment. // see the comment at the top of Initialize.cpp for where to put them. Establish them at // the earliest release that supports the extension. Then, tag them with the // set of extensions that both enable them and are necessary, given the version of the symbol // table. (There is a different symbol table for each version.) // // 7) If the extension has additional requirements like minimum SPIR-V version required, add them // to extensionRequires() #include "parseVersions.h" #include "localintermediate.h" namespace glslang { // // Initialize all extensions, almost always to 'disable', as once their features // are incorporated into a core version, their features are supported through allowing that // core version, not through a pseudo-enablement of the extension. // void TParseVersions::initializeExtensionBehavior() { … } // Get code that is not part of a shared symbol table, is specific to this shader, // or needed by the preprocessor (which does not use a shared symbol table). void TParseVersions::getPreamble(std::string& preamble) { … } // // Map from stage enum to externally readable text name. // const char* StageName(EShLanguage stage) { … } // // When to use requireStage() // // If only some stages support a feature. // // Operation: If the current stage is not present, give an error message. // void TParseVersions::requireStage(const TSourceLoc& loc, EShLanguageMask languageMask, const char* featureDesc) { … } // If only one stage supports a feature, this can be called. But, all supporting stages // must be specified with one call. void TParseVersions::requireStage(const TSourceLoc& loc, EShLanguage stage, const char* featureDesc) { … } // // When to use requireProfile(): // // Use if only some profiles support a feature. However, if within a profile the feature // is version or extension specific, follow this call with calls to profileRequires(). // // Operation: If the current profile is not one of the profileMask, // give an error message. // void TParseVersions::requireProfile(const TSourceLoc& loc, int profileMask, const char* featureDesc) { … } // // When to use profileRequires(): // // If a set of profiles have the same requirements for what version or extensions // are needed to support a feature. // // It must be called for each profile that needs protection. Use requireProfile() first // to reduce that set of profiles. // // Operation: Will issue warnings/errors based on the current profile, version, and extension // behaviors. It only checks extensions when the current profile is one of the profileMask. // // A minVersion of 0 means no version of the profileMask support this in core, // the extension must be present. // // entry point that takes multiple extensions void TParseVersions::profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, int numExtensions, const char* const extensions[], const char* featureDesc) { … } // entry point for the above that takes a single extension void TParseVersions::profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, const char* extension, const char* featureDesc) { … } void TParseVersions::unimplemented(const TSourceLoc& loc, const char* featureDesc) { … } // // Within a set of profiles, see if a feature is deprecated and give an error or warning based on whether // a future compatibility context is being use. // void TParseVersions::checkDeprecated(const TSourceLoc& loc, int profileMask, int depVersion, const char* featureDesc) { … } // // Within a set of profiles, see if a feature has now been removed and if so, give an error. // The version argument is the first version no longer having the feature. // void TParseVersions::requireNotRemoved(const TSourceLoc& loc, int profileMask, int removedVersion, const char* featureDesc) { … } // Returns true if at least one of the extensions in the extensions parameter is requested. Otherwise, returns false. // Warns appropriately if the requested behavior of an extension is "warn". bool TParseVersions::checkExtensionsRequested(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc) { … } // // Use when there are no profile/version to check, it's just an error if one of the // extensions is not present. // void TParseVersions::requireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc) { … } // // Use by preprocessor when there are no profile/version to check, it's just an error if one of the // extensions is not present. // void TParseVersions::ppRequireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc) { … } TExtensionBehavior TParseVersions::getExtensionBehavior(const char* extension) { … } // Returns true if the given extension is set to enable, require, or warn. bool TParseVersions::extensionTurnedOn(const char* const extension) { … } // See if any of the extensions are set to enable, require, or warn. bool TParseVersions::extensionsTurnedOn(int numExtensions, const char* const extensions[]) { … } // // Change the current state of an extension's behavior. // void TParseVersions::updateExtensionBehavior(int line, const char* extension, const char* behaviorString) { … } void TParseVersions::updateExtensionBehavior(const char* extension, TExtensionBehavior behavior) { … } // Check if extension is used with correct shader stage. void TParseVersions::checkExtensionStage(const TSourceLoc& loc, const char * const extension) { … } // Check if extension has additional requirements void TParseVersions::extensionRequires(const TSourceLoc &loc, const char * const extension, const char *behaviorString) { … } // Call for any operation needing full GLSL integer data-type support. void TParseVersions::fullIntegerCheck(const TSourceLoc& loc, const char* op) { … } // Call for any operation needing GLSL double data-type support. void TParseVersions::doubleCheck(const TSourceLoc& loc, const char* op) { … } // Call for any operation needing GLSL float16 data-type support. void TParseVersions::float16Check(const TSourceLoc& loc, const char* op, bool builtIn) { … } bool TParseVersions::float16Arithmetic() { … } bool TParseVersions::int16Arithmetic() { … } bool TParseVersions::int8Arithmetic() { … } void TParseVersions::requireFloat16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) { … } void TParseVersions::requireInt16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) { … } void TParseVersions::requireInt8Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) { … } void TParseVersions::float16ScalarVectorCheck(const TSourceLoc& loc, const char* op, bool builtIn) { … } // Call for any operation needing GLSL float32 data-type support. void TParseVersions::explicitFloat32Check(const TSourceLoc& loc, const char* op, bool builtIn) { … } // Call for any operation needing GLSL float64 data-type support. void TParseVersions::explicitFloat64Check(const TSourceLoc& loc, const char* op, bool builtIn) { … } // Call for any operation needing GLSL explicit int8 data-type support. void TParseVersions::explicitInt8Check(const TSourceLoc& loc, const char* op, bool builtIn) { … } // Call for any operation needing GLSL float16 opaque-type support void TParseVersions::float16OpaqueCheck(const TSourceLoc& loc, const char* op, bool builtIn) { … } // Call for any operation needing GLSL explicit int16 data-type support. void TParseVersions::explicitInt16Check(const TSourceLoc& loc, const char* op, bool builtIn) { … } void TParseVersions::int16ScalarVectorCheck(const TSourceLoc& loc, const char* op, bool builtIn) { … } void TParseVersions::int8ScalarVectorCheck(const TSourceLoc& loc, const char* op, bool builtIn) { … } // Call for any operation needing GLSL explicit int32 data-type support. void TParseVersions::explicitInt32Check(const TSourceLoc& loc, const char* op, bool builtIn) { … } // Call for any operation needing GLSL 64-bit integer data-type support. void TParseVersions::int64Check(const TSourceLoc& loc, const char* op, bool builtIn) { … } void TParseVersions::fcoopmatCheckNV(const TSourceLoc& loc, const char* op, bool builtIn) { … } void TParseVersions::intcoopmatCheckNV(const TSourceLoc& loc, const char* op, bool builtIn) { … } void TParseVersions::coopmatCheck(const TSourceLoc& loc, const char* op, bool builtIn) { … } // Call for any operation removed because SPIR-V is in use. void TParseVersions::spvRemoved(const TSourceLoc& loc, const char* op) { … } // Call for any operation removed because Vulkan SPIR-V is being generated. void TParseVersions::vulkanRemoved(const TSourceLoc& loc, const char* op) { … } // Call for any operation that requires Vulkan. void TParseVersions::requireVulkan(const TSourceLoc& loc, const char* op) { … } // Call for any operation that requires SPIR-V. void TParseVersions::requireSpv(const TSourceLoc& loc, const char* op) { … } void TParseVersions::requireSpv(const TSourceLoc& loc, const char *op, unsigned int version) { … } } // end namespace glslang