2021-10-28 21:26:53 +00:00
//
// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
// Copyright (C) 2013-2016 LunarG, Inc.
2021-10-31 17:19:26 +00:00
// Copyright (C) 2015-2020 Google, Inc.
2021-10-28 21:26:53 +00:00
//
// 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.
//
//
// Implement the top-level of interface to the compiler/linker,
// as defined in ShaderLang.h
// This is the platform independent interface between an OGL driver
// and the shading language compiler/linker.
//
# include <cstring>
# include <iostream>
# include <sstream>
# include <memory>
# include "SymbolTable.h"
# include "ParseHelper.h"
# include "Scan.h"
# include "ScanContext.h"
# ifdef ENABLE_HLSL
2021-10-31 17:19:26 +00:00
# include "../HLSL/hlslParseHelper.h"
# include "../HLSL/hlslParseables.h"
# include "../HLSL/hlslScanContext.h"
2021-10-28 21:26:53 +00:00
# endif
# include "../Include/ShHandle.h"
# include "../../OGLCompilersDLL/InitializeDll.h"
# include "preprocessor/PpContext.h"
# define SH_EXPORTING
# include "../Public/ShaderLang.h"
# include "reflection.h"
# include "iomapper.h"
# include "Initialize.h"
// TODO: this really shouldn't be here, it is only because of the trial addition
// of printing pre-processed tokens, which requires knowing the string literal
// token to print ", but none of that seems appropriate for this file.
# include "preprocessor/PpTokens.h"
2021-10-31 17:19:26 +00:00
// Build-time generated includes
# include "../Include/build_info.h"
2021-10-28 21:26:53 +00:00
namespace { // anonymous namespace for file-local functions and symbols
// Total number of successful initializers of glslang: a refcount
// Shared global; access should be protected by a global mutex/critical section.
int NumberOfClients = 0 ;
using namespace glslang ;
// Create a language specific version of parseables.
TBuiltInParseables * CreateBuiltInParseables ( TInfoSink & infoSink , EShSource source )
{
switch ( source ) {
case EShSourceGlsl : return new TBuiltIns ( ) ; // GLSL builtIns
# ifdef ENABLE_HLSL
case EShSourceHlsl : return new TBuiltInParseablesHlsl ( ) ; // HLSL intrinsics
# endif
default :
infoSink . info . message ( EPrefixInternalError , " Unable to determine source language " ) ;
return nullptr ;
}
}
// Create a language specific version of a parse context.
TParseContextBase * CreateParseContext ( TSymbolTable & symbolTable , TIntermediate & intermediate ,
int version , EProfile profile , EShSource source ,
EShLanguage language , TInfoSink & infoSink ,
SpvVersion spvVersion , bool forwardCompatible , EShMessages messages ,
bool parsingBuiltIns , std : : string sourceEntryPointName = " " )
{
switch ( source ) {
case EShSourceGlsl : {
if ( sourceEntryPointName . size ( ) = = 0 )
intermediate . setEntryPointName ( " main " ) ;
TString entryPoint = sourceEntryPointName . c_str ( ) ;
return new TParseContext ( symbolTable , intermediate , parsingBuiltIns , version , profile , spvVersion ,
language , infoSink , forwardCompatible , messages , & entryPoint ) ;
}
# ifdef ENABLE_HLSL
case EShSourceHlsl :
return new HlslParseContext ( symbolTable , intermediate , parsingBuiltIns , version , profile , spvVersion ,
language , infoSink , sourceEntryPointName . c_str ( ) , forwardCompatible , messages ) ;
# endif
default :
infoSink . info . message ( EPrefixInternalError , " Unable to determine source language " ) ;
return nullptr ;
}
}
// Local mapping functions for making arrays of symbol tables....
const int VersionCount = 17 ; // index range in MapVersionToIndex
int MapVersionToIndex ( int version )
{
int index = 0 ;
switch ( version ) {
case 100 : index = 0 ; break ;
case 110 : index = 1 ; break ;
case 120 : index = 2 ; break ;
case 130 : index = 3 ; break ;
case 140 : index = 4 ; break ;
case 150 : index = 5 ; break ;
case 300 : index = 6 ; break ;
case 330 : index = 7 ; break ;
case 400 : index = 8 ; break ;
case 410 : index = 9 ; break ;
case 420 : index = 10 ; break ;
case 430 : index = 11 ; break ;
case 440 : index = 12 ; break ;
case 310 : index = 13 ; break ;
case 450 : index = 14 ; break ;
case 500 : index = 0 ; break ; // HLSL
case 320 : index = 15 ; break ;
case 460 : index = 16 ; break ;
default : assert ( 0 ) ; break ;
}
assert ( index < VersionCount ) ;
return index ;
}
2021-10-31 17:19:26 +00:00
const int SpvVersionCount = 4 ; // index range in MapSpvVersionToIndex
2021-10-28 21:26:53 +00:00
int MapSpvVersionToIndex ( const SpvVersion & spvVersion )
{
int index = 0 ;
if ( spvVersion . openGl > 0 )
index = 1 ;
2021-10-31 17:19:26 +00:00
else if ( spvVersion . vulkan > 0 ) {
if ( ! spvVersion . vulkanRelaxed )
index = 2 ;
else
index = 3 ;
}
2021-10-28 21:26:53 +00:00
assert ( index < SpvVersionCount ) ;
return index ;
}
const int ProfileCount = 4 ; // index range in MapProfileToIndex
int MapProfileToIndex ( EProfile profile )
{
int index = 0 ;
switch ( profile ) {
case ENoProfile : index = 0 ; break ;
case ECoreProfile : index = 1 ; break ;
case ECompatibilityProfile : index = 2 ; break ;
case EEsProfile : index = 3 ; break ;
default : break ;
}
assert ( index < ProfileCount ) ;
return index ;
}
const int SourceCount = 2 ;
int MapSourceToIndex ( EShSource source )
{
int index = 0 ;
switch ( source ) {
case EShSourceGlsl : index = 0 ; break ;
case EShSourceHlsl : index = 1 ; break ;
default : break ;
}
assert ( index < SourceCount ) ;
return index ;
}
// only one of these needed for non-ES; ES needs 2 for different precision defaults of built-ins
enum EPrecisionClass {
EPcGeneral ,
EPcFragment ,
EPcCount
} ;
// A process-global symbol table per version per profile for built-ins common
// to multiple stages (languages), and a process-global symbol table per version
// per profile per stage for built-ins unique to each stage. They will be sparsely
// populated, so they will only be generated as needed.
//
// Each has a different set of built-ins, and we want to preserve that from
// compile to compile.
//
TSymbolTable * CommonSymbolTable [ VersionCount ] [ SpvVersionCount ] [ ProfileCount ] [ SourceCount ] [ EPcCount ] = { } ;
TSymbolTable * SharedSymbolTables [ VersionCount ] [ SpvVersionCount ] [ ProfileCount ] [ SourceCount ] [ EShLangCount ] = { } ;
TPoolAllocator * PerProcessGPA = nullptr ;
//
// Parse and add to the given symbol table the content of the given shader string.
//
bool InitializeSymbolTable ( const TString & builtIns , int version , EProfile profile , const SpvVersion & spvVersion , EShLanguage language ,
EShSource source , TInfoSink & infoSink , TSymbolTable & symbolTable )
{
TIntermediate intermediate ( language , version , profile ) ;
intermediate . setSource ( source ) ;
std : : unique_ptr < TParseContextBase > parseContext ( CreateParseContext ( symbolTable , intermediate , version , profile , source ,
language , infoSink , spvVersion , true , EShMsgDefault ,
true ) ) ;
TShader : : ForbidIncluder includer ;
TPpContext ppContext ( * parseContext , " " , includer ) ;
TScanContext scanContext ( * parseContext ) ;
parseContext - > setScanContext ( & scanContext ) ;
parseContext - > setPpContext ( & ppContext ) ;
//
// Push the symbol table to give it an initial scope. This
// push should not have a corresponding pop, so that built-ins
// are preserved, and the test for an empty table fails.
//
symbolTable . push ( ) ;
const char * builtInShaders [ 2 ] ;
size_t builtInLengths [ 2 ] ;
builtInShaders [ 0 ] = builtIns . c_str ( ) ;
builtInLengths [ 0 ] = builtIns . size ( ) ;
if ( builtInLengths [ 0 ] = = 0 )
return true ;
TInputScanner input ( 1 , builtInShaders , builtInLengths ) ;
if ( ! parseContext - > parseShaderStrings ( ppContext , input ) ! = 0 ) {
infoSink . info . message ( EPrefixInternalError , " Unable to parse built-ins " ) ;
printf ( " Unable to parse built-ins \n %s \n " , infoSink . info . c_str ( ) ) ;
printf ( " %s \n " , builtInShaders [ 0 ] ) ;
return false ;
}
return true ;
}
int CommonIndex ( EProfile profile , EShLanguage language )
{
return ( profile = = EEsProfile & & language = = EShLangFragment ) ? EPcFragment : EPcGeneral ;
}
//
// To initialize per-stage shared tables, with the common table already complete.
//
void InitializeStageSymbolTable ( TBuiltInParseables & builtInParseables , int version , EProfile profile , const SpvVersion & spvVersion ,
EShLanguage language , EShSource source , TInfoSink & infoSink , TSymbolTable * * commonTable ,
TSymbolTable * * symbolTables )
{
2021-10-31 17:19:26 +00:00
# ifdef GLSLANG_WEB
profile = EEsProfile ;
version = 310 ;
# elif defined(GLSLANG_ANGLE)
profile = ECoreProfile ;
version = 450 ;
# endif
2021-10-28 21:26:53 +00:00
( * symbolTables [ language ] ) . adoptLevels ( * commonTable [ CommonIndex ( profile , language ) ] ) ;
InitializeSymbolTable ( builtInParseables . getStageString ( language ) , version , profile , spvVersion , language , source ,
infoSink , * symbolTables [ language ] ) ;
builtInParseables . identifyBuiltIns ( version , profile , spvVersion , language , * symbolTables [ language ] ) ;
if ( profile = = EEsProfile & & version > = 300 )
( * symbolTables [ language ] ) . setNoBuiltInRedeclarations ( ) ;
if ( version = = 110 )
( * symbolTables [ language ] ) . setSeparateNameSpaces ( ) ;
}
//
// Initialize the full set of shareable symbol tables;
// The common (cross-stage) and those shareable per-stage.
//
bool InitializeSymbolTables ( TInfoSink & infoSink , TSymbolTable * * commonTable , TSymbolTable * * symbolTables , int version , EProfile profile , const SpvVersion & spvVersion , EShSource source )
{
2021-10-31 17:19:26 +00:00
# ifdef GLSLANG_WEB
profile = EEsProfile ;
version = 310 ;
# elif defined(GLSLANG_ANGLE)
profile = ECoreProfile ;
version = 450 ;
# endif
2021-10-28 21:26:53 +00:00
std : : unique_ptr < TBuiltInParseables > builtInParseables ( CreateBuiltInParseables ( infoSink , source ) ) ;
if ( builtInParseables = = nullptr )
return false ;
builtInParseables - > initialize ( version , profile , spvVersion ) ;
// do the common tables
InitializeSymbolTable ( builtInParseables - > getCommonString ( ) , version , profile , spvVersion , EShLangVertex , source ,
infoSink , * commonTable [ EPcGeneral ] ) ;
if ( profile = = EEsProfile )
InitializeSymbolTable ( builtInParseables - > getCommonString ( ) , version , profile , spvVersion , EShLangFragment , source ,
infoSink , * commonTable [ EPcFragment ] ) ;
// do the per-stage tables
// always have vertex and fragment
InitializeStageSymbolTable ( * builtInParseables , version , profile , spvVersion , EShLangVertex , source ,
infoSink , commonTable , symbolTables ) ;
InitializeStageSymbolTable ( * builtInParseables , version , profile , spvVersion , EShLangFragment , source ,
infoSink , commonTable , symbolTables ) ;
2021-10-31 17:19:26 +00:00
# ifndef GLSLANG_WEB
2021-10-28 21:26:53 +00:00
// check for tessellation
if ( ( profile ! = EEsProfile & & version > = 150 ) | |
( profile = = EEsProfile & & version > = 310 ) ) {
InitializeStageSymbolTable ( * builtInParseables , version , profile , spvVersion , EShLangTessControl , source ,
infoSink , commonTable , symbolTables ) ;
InitializeStageSymbolTable ( * builtInParseables , version , profile , spvVersion , EShLangTessEvaluation , source ,
infoSink , commonTable , symbolTables ) ;
}
// check for geometry
if ( ( profile ! = EEsProfile & & version > = 150 ) | |
( profile = = EEsProfile & & version > = 310 ) )
InitializeStageSymbolTable ( * builtInParseables , version , profile , spvVersion , EShLangGeometry , source ,
infoSink , commonTable , symbolTables ) ;
// check for compute
if ( ( profile ! = EEsProfile & & version > = 420 ) | |
( profile = = EEsProfile & & version > = 310 ) )
InitializeStageSymbolTable ( * builtInParseables , version , profile , spvVersion , EShLangCompute , source ,
infoSink , commonTable , symbolTables ) ;
2021-10-31 17:19:26 +00:00
# ifndef GLSLANG_ANGLE
2021-10-28 21:26:53 +00:00
// check for ray tracing stages
if ( profile ! = EEsProfile & & version > = 450 ) {
2021-10-31 17:19:26 +00:00
InitializeStageSymbolTable ( * builtInParseables , version , profile , spvVersion , EShLangRayGen , source ,
2021-10-28 21:26:53 +00:00
infoSink , commonTable , symbolTables ) ;
2021-10-31 17:19:26 +00:00
InitializeStageSymbolTable ( * builtInParseables , version , profile , spvVersion , EShLangIntersect , source ,
2021-10-28 21:26:53 +00:00
infoSink , commonTable , symbolTables ) ;
2021-10-31 17:19:26 +00:00
InitializeStageSymbolTable ( * builtInParseables , version , profile , spvVersion , EShLangAnyHit , source ,
2021-10-28 21:26:53 +00:00
infoSink , commonTable , symbolTables ) ;
2021-10-31 17:19:26 +00:00
InitializeStageSymbolTable ( * builtInParseables , version , profile , spvVersion , EShLangClosestHit , source ,
2021-10-28 21:26:53 +00:00
infoSink , commonTable , symbolTables ) ;
2021-10-31 17:19:26 +00:00
InitializeStageSymbolTable ( * builtInParseables , version , profile , spvVersion , EShLangMiss , source ,
2021-10-28 21:26:53 +00:00
infoSink , commonTable , symbolTables ) ;
2021-10-31 17:19:26 +00:00
InitializeStageSymbolTable ( * builtInParseables , version , profile , spvVersion , EShLangCallable , source ,
2021-10-28 21:26:53 +00:00
infoSink , commonTable , symbolTables ) ;
}
// check for mesh
if ( ( profile ! = EEsProfile & & version > = 450 ) | |
( profile = = EEsProfile & & version > = 320 ) )
InitializeStageSymbolTable ( * builtInParseables , version , profile , spvVersion , EShLangMeshNV , source ,
infoSink , commonTable , symbolTables ) ;
// check for task
if ( ( profile ! = EEsProfile & & version > = 450 ) | |
( profile = = EEsProfile & & version > = 320 ) )
InitializeStageSymbolTable ( * builtInParseables , version , profile , spvVersion , EShLangTaskNV , source ,
infoSink , commonTable , symbolTables ) ;
2021-10-31 17:19:26 +00:00
# endif // !GLSLANG_ANGLE
# endif // !GLSLANG_WEB
2021-10-28 21:26:53 +00:00
return true ;
}
bool AddContextSpecificSymbols ( const TBuiltInResource * resources , TInfoSink & infoSink , TSymbolTable & symbolTable , int version ,
EProfile profile , const SpvVersion & spvVersion , EShLanguage language , EShSource source )
{
std : : unique_ptr < TBuiltInParseables > builtInParseables ( CreateBuiltInParseables ( infoSink , source ) ) ;
if ( builtInParseables = = nullptr )
return false ;
builtInParseables - > initialize ( * resources , version , profile , spvVersion , language ) ;
InitializeSymbolTable ( builtInParseables - > getCommonString ( ) , version , profile , spvVersion , language , source , infoSink , symbolTable ) ;
builtInParseables - > identifyBuiltIns ( version , profile , spvVersion , language , symbolTable , * resources ) ;
return true ;
}
//
// To do this on the fly, we want to leave the current state of our thread's
// pool allocator intact, so:
// - Switch to a new pool for parsing the built-ins
// - Do the parsing, which builds the symbol table, using the new pool
// - Switch to the process-global pool to save a copy of the resulting symbol table
// - Free up the new pool used to parse the built-ins
// - Switch back to the original thread's pool
//
// This only gets done the first time any thread needs a particular symbol table
// (lazy evaluation).
//
void SetupBuiltinSymbolTable ( int version , EProfile profile , const SpvVersion & spvVersion , EShSource source )
{
TInfoSink infoSink ;
// Make sure only one thread tries to do this at a time
glslang : : GetGlobalLock ( ) ;
// See if it's already been done for this version/profile combination
int versionIndex = MapVersionToIndex ( version ) ;
int spvVersionIndex = MapSpvVersionToIndex ( spvVersion ) ;
int profileIndex = MapProfileToIndex ( profile ) ;
int sourceIndex = MapSourceToIndex ( source ) ;
if ( CommonSymbolTable [ versionIndex ] [ spvVersionIndex ] [ profileIndex ] [ sourceIndex ] [ EPcGeneral ] ) {
glslang : : ReleaseGlobalLock ( ) ;
return ;
}
// Switch to a new pool
TPoolAllocator & previousAllocator = GetThreadPoolAllocator ( ) ;
TPoolAllocator * builtInPoolAllocator = new TPoolAllocator ;
SetThreadPoolAllocator ( builtInPoolAllocator ) ;
// Dynamically allocate the local symbol tables so we can control when they are deallocated WRT when the pool is popped.
TSymbolTable * commonTable [ EPcCount ] ;
TSymbolTable * stageTables [ EShLangCount ] ;
for ( int precClass = 0 ; precClass < EPcCount ; + + precClass )
commonTable [ precClass ] = new TSymbolTable ;
for ( int stage = 0 ; stage < EShLangCount ; + + stage )
stageTables [ stage ] = new TSymbolTable ;
// Generate the local symbol tables using the new pool
InitializeSymbolTables ( infoSink , commonTable , stageTables , version , profile , spvVersion , source ) ;
// Switch to the process-global pool
SetThreadPoolAllocator ( PerProcessGPA ) ;
// Copy the local symbol tables from the new pool to the global tables using the process-global pool
for ( int precClass = 0 ; precClass < EPcCount ; + + precClass ) {
if ( ! commonTable [ precClass ] - > isEmpty ( ) ) {
CommonSymbolTable [ versionIndex ] [ spvVersionIndex ] [ profileIndex ] [ sourceIndex ] [ precClass ] = new TSymbolTable ;
CommonSymbolTable [ versionIndex ] [ spvVersionIndex ] [ profileIndex ] [ sourceIndex ] [ precClass ] - > copyTable ( * commonTable [ precClass ] ) ;
CommonSymbolTable [ versionIndex ] [ spvVersionIndex ] [ profileIndex ] [ sourceIndex ] [ precClass ] - > readOnly ( ) ;
}
}
for ( int stage = 0 ; stage < EShLangCount ; + + stage ) {
if ( ! stageTables [ stage ] - > isEmpty ( ) ) {
SharedSymbolTables [ versionIndex ] [ spvVersionIndex ] [ profileIndex ] [ sourceIndex ] [ stage ] = new TSymbolTable ;
SharedSymbolTables [ versionIndex ] [ spvVersionIndex ] [ profileIndex ] [ sourceIndex ] [ stage ] - > adoptLevels ( * CommonSymbolTable
[ versionIndex ] [ spvVersionIndex ] [ profileIndex ] [ sourceIndex ] [ CommonIndex ( profile , ( EShLanguage ) stage ) ] ) ;
SharedSymbolTables [ versionIndex ] [ spvVersionIndex ] [ profileIndex ] [ sourceIndex ] [ stage ] - > copyTable ( * stageTables [ stage ] ) ;
SharedSymbolTables [ versionIndex ] [ spvVersionIndex ] [ profileIndex ] [ sourceIndex ] [ stage ] - > readOnly ( ) ;
}
}
// Clean up the local tables before deleting the pool they used.
for ( int precClass = 0 ; precClass < EPcCount ; + + precClass )
delete commonTable [ precClass ] ;
for ( int stage = 0 ; stage < EShLangCount ; + + stage )
delete stageTables [ stage ] ;
delete builtInPoolAllocator ;
SetThreadPoolAllocator ( & previousAllocator ) ;
glslang : : ReleaseGlobalLock ( ) ;
}
2021-10-31 17:19:26 +00:00
// Function to Print all builtins
void DumpBuiltinSymbolTable ( TInfoSink & infoSink , const TSymbolTable & symbolTable )
{
# if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
infoSink . debug < < " BuiltinSymbolTable { \n " ;
symbolTable . dump ( infoSink , true ) ;
infoSink . debug < < " } \n " ;
# endif
}
2021-10-28 21:26:53 +00:00
// Return true if the shader was correctly specified for version/profile/stage.
bool DeduceVersionProfile ( TInfoSink & infoSink , EShLanguage stage , bool versionNotFirst , int defaultVersion ,
EShSource source , int & version , EProfile & profile , const SpvVersion & spvVersion )
{
const int FirstProfileVersion = 150 ;
bool correct = true ;
if ( source = = EShSourceHlsl ) {
version = 500 ; // shader model; currently a characteristic of glslang, not the input
profile = ECoreProfile ; // allow doubles in prototype parsing
return correct ;
}
// Get a version...
if ( version = = 0 ) {
version = defaultVersion ;
// infoSink.info.message(EPrefixWarning, "#version: statement missing; use #version on first line of shader");
}
// Get a good profile...
if ( profile = = ENoProfile ) {
if ( version = = 300 | | version = = 310 | | version = = 320 ) {
correct = false ;
infoSink . info . message ( EPrefixError , " #version: versions 300, 310, and 320 require specifying the 'es' profile " ) ;
profile = EEsProfile ;
} else if ( version = = 100 )
profile = EEsProfile ;
else if ( version > = FirstProfileVersion )
profile = ECoreProfile ;
else
profile = ENoProfile ;
} else {
// a profile was provided...
if ( version < 150 ) {
correct = false ;
infoSink . info . message ( EPrefixError , " #version: versions before 150 do not allow a profile token " ) ;
if ( version = = 100 )
profile = EEsProfile ;
else
profile = ENoProfile ;
} else if ( version = = 300 | | version = = 310 | | version = = 320 ) {
if ( profile ! = EEsProfile ) {
correct = false ;
infoSink . info . message ( EPrefixError , " #version: versions 300, 310, and 320 support only the es profile " ) ;
}
profile = EEsProfile ;
} else {
if ( profile = = EEsProfile ) {
correct = false ;
infoSink . info . message ( EPrefixError , " #version: only version 300, 310, and 320 support the es profile " ) ;
if ( version > = FirstProfileVersion )
profile = ECoreProfile ;
else
profile = ENoProfile ;
}
// else: typical desktop case... e.g., "#version 410 core"
}
}
// Fix version...
switch ( version ) {
// ES versions
case 100 : break ;
case 300 : break ;
case 310 : break ;
case 320 : break ;
// desktop versions
case 110 : break ;
case 120 : break ;
case 130 : break ;
case 140 : break ;
case 150 : break ;
case 330 : break ;
case 400 : break ;
case 410 : break ;
case 420 : break ;
case 430 : break ;
case 440 : break ;
case 450 : break ;
case 460 : break ;
// unknown version
default :
correct = false ;
infoSink . info . message ( EPrefixError , " version not supported " ) ;
if ( profile = = EEsProfile )
version = 310 ;
else {
version = 450 ;
profile = ECoreProfile ;
}
break ;
}
2021-10-31 17:19:26 +00:00
# if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
2021-10-28 21:26:53 +00:00
// Correct for stage type...
switch ( stage ) {
case EShLangGeometry :
if ( ( profile = = EEsProfile & & version < 310 ) | |
( profile ! = EEsProfile & & version < 150 ) ) {
correct = false ;
infoSink . info . message ( EPrefixError , " #version: geometry shaders require es profile with version 310 or non-es profile with version 150 or above " ) ;
version = ( profile = = EEsProfile ) ? 310 : 150 ;
if ( profile = = EEsProfile | | profile = = ENoProfile )
profile = ECoreProfile ;
}
break ;
case EShLangTessControl :
case EShLangTessEvaluation :
if ( ( profile = = EEsProfile & & version < 310 ) | |
( profile ! = EEsProfile & & version < 150 ) ) {
correct = false ;
infoSink . info . message ( EPrefixError , " #version: tessellation shaders require es profile with version 310 or non-es profile with version 150 or above " ) ;
version = ( profile = = EEsProfile ) ? 310 : 400 ; // 150 supports the extension, correction is to 400 which does not
if ( profile = = EEsProfile | | profile = = ENoProfile )
profile = ECoreProfile ;
}
break ;
case EShLangCompute :
if ( ( profile = = EEsProfile & & version < 310 ) | |
( profile ! = EEsProfile & & version < 420 ) ) {
correct = false ;
infoSink . info . message ( EPrefixError , " #version: compute shaders require es profile with version 310 or above, or non-es profile with version 420 or above " ) ;
version = profile = = EEsProfile ? 310 : 420 ;
}
break ;
2021-10-31 17:19:26 +00:00
case EShLangRayGen :
case EShLangIntersect :
case EShLangAnyHit :
case EShLangClosestHit :
case EShLangMiss :
case EShLangCallable :
2021-10-28 21:26:53 +00:00
if ( profile = = EEsProfile | | version < 460 ) {
correct = false ;
infoSink . info . message ( EPrefixError , " #version: ray tracing shaders require non-es profile with version 460 or above " ) ;
version = 460 ;
}
break ;
case EShLangMeshNV :
case EShLangTaskNV :
if ( ( profile = = EEsProfile & & version < 320 ) | |
( profile ! = EEsProfile & & version < 450 ) ) {
correct = false ;
infoSink . info . message ( EPrefixError , " #version: mesh/task shaders require es profile with version 320 or above, or non-es profile with version 450 or above " ) ;
version = profile = = EEsProfile ? 320 : 450 ;
}
default :
break ;
}
if ( profile = = EEsProfile & & version > = 300 & & versionNotFirst ) {
correct = false ;
infoSink . info . message ( EPrefixError , " #version: statement must appear first in es-profile shader; before comments or newlines " ) ;
}
// Check for SPIR-V compatibility
if ( spvVersion . spv ! = 0 ) {
switch ( profile ) {
2021-10-31 17:19:26 +00:00
case EEsProfile :
if ( version < 310 ) {
2021-10-28 21:26:53 +00:00
correct = false ;
2021-10-31 17:19:26 +00:00
infoSink . info . message ( EPrefixError , " #version: ES shaders for SPIR-V require version 310 or higher " ) ;
2021-10-28 21:26:53 +00:00
version = 310 ;
}
break ;
case ECompatibilityProfile :
infoSink . info . message ( EPrefixError , " #version: compilation for SPIR-V does not support the compatibility profile " ) ;
break ;
default :
if ( spvVersion . vulkan > 0 & & version < 140 ) {
correct = false ;
infoSink . info . message ( EPrefixError , " #version: Desktop shaders for Vulkan SPIR-V require version 140 or higher " ) ;
version = 140 ;
}
if ( spvVersion . openGl > = 100 & & version < 330 ) {
correct = false ;
infoSink . info . message ( EPrefixError , " #version: Desktop shaders for OpenGL SPIR-V require version 330 or higher " ) ;
version = 330 ;
}
break ;
}
}
2021-10-31 17:19:26 +00:00
# endif
2021-10-28 21:26:53 +00:00
return correct ;
}
// There are multiple paths in for setting environment stuff.
// TEnvironment takes precedence, for what it sets, so sort all this out.
// Ideally, the internal code could be made to use TEnvironment, but for
// now, translate it to the historically used parameters.
void TranslateEnvironment ( const TEnvironment * environment , EShMessages & messages , EShSource & source ,
EShLanguage & stage , SpvVersion & spvVersion )
{
// Set up environmental defaults, first ignoring 'environment'.
if ( messages & EShMsgSpvRules )
spvVersion . spv = EShTargetSpv_1_0 ;
if ( messages & EShMsgVulkanRules ) {
spvVersion . vulkan = EShTargetVulkan_1_0 ;
spvVersion . vulkanGlsl = 100 ;
} else if ( spvVersion . spv ! = 0 )
spvVersion . openGl = 100 ;
// Now, override, based on any content set in 'environment'.
// 'environment' must be cleared to ESh*None settings when items
// are not being set.
if ( environment ! = nullptr ) {
// input language
if ( environment - > input . languageFamily ! = EShSourceNone ) {
stage = environment - > input . stage ;
switch ( environment - > input . dialect ) {
case EShClientNone :
break ;
case EShClientVulkan :
spvVersion . vulkanGlsl = environment - > input . dialectVersion ;
2021-10-31 17:19:26 +00:00
spvVersion . vulkanRelaxed = environment - > input . vulkanRulesRelaxed ;
2021-10-28 21:26:53 +00:00
break ;
case EShClientOpenGL :
spvVersion . openGl = environment - > input . dialectVersion ;
break ;
2021-10-31 17:19:26 +00:00
case EShClientCount :
assert ( 0 ) ;
break ;
2021-10-28 21:26:53 +00:00
}
switch ( environment - > input . languageFamily ) {
case EShSourceNone :
break ;
case EShSourceGlsl :
source = EShSourceGlsl ;
messages = static_cast < EShMessages > ( messages & ~ EShMsgReadHlsl ) ;
break ;
case EShSourceHlsl :
source = EShSourceHlsl ;
messages = static_cast < EShMessages > ( messages | EShMsgReadHlsl ) ;
break ;
2021-10-31 17:19:26 +00:00
case EShSourceCount :
assert ( 0 ) ;
break ;
2021-10-28 21:26:53 +00:00
}
}
// client
switch ( environment - > client . client ) {
case EShClientVulkan :
spvVersion . vulkan = environment - > client . version ;
break ;
default :
break ;
}
// generated code
switch ( environment - > target . language ) {
case EshTargetSpv :
spvVersion . spv = environment - > target . version ;
break ;
default :
break ;
}
}
}
// Most processes are recorded when set in the intermediate representation,
// These are the few that are not.
void RecordProcesses ( TIntermediate & intermediate , EShMessages messages , const std : : string & sourceEntryPointName )
{
if ( ( messages & EShMsgRelaxedErrors ) ! = 0 )
intermediate . addProcess ( " relaxed-errors " ) ;
if ( ( messages & EShMsgSuppressWarnings ) ! = 0 )
intermediate . addProcess ( " suppress-warnings " ) ;
if ( ( messages & EShMsgKeepUncalled ) ! = 0 )
intermediate . addProcess ( " keep-uncalled " ) ;
if ( sourceEntryPointName . size ( ) > 0 ) {
intermediate . addProcess ( " source-entrypoint " ) ;
intermediate . addProcessArgument ( sourceEntryPointName ) ;
}
}
// This is the common setup and cleanup code for PreprocessDeferred and
// CompileDeferred.
// It takes any callable with a signature of
// bool (TParseContextBase& parseContext, TPpContext& ppContext,
// TInputScanner& input, bool versionWillBeError,
// TSymbolTable& , TIntermediate& ,
// EShOptimizationLevel , EShMessages );
// Which returns false if a failure was detected and true otherwise.
//
template < typename ProcessingContext >
bool ProcessDeferred (
TCompiler * compiler ,
const char * const shaderStrings [ ] ,
const int numStrings ,
const int * inputLengths ,
const char * const stringNames [ ] ,
const char * customPreamble ,
const EShOptimizationLevel optLevel ,
const TBuiltInResource * resources ,
int defaultVersion , // use 100 for ES environment, 110 for desktop; this is the GLSL version, not SPIR-V or Vulkan
EProfile defaultProfile ,
// set version/profile to defaultVersion/defaultProfile regardless of the #version
// directive in the source code
bool forceDefaultVersionAndProfile ,
bool forwardCompatible , // give errors for use of deprecated features
EShMessages messages , // warnings/errors/AST; things to print out
TIntermediate & intermediate , // returned tree, etc.
ProcessingContext & processingContext ,
bool requireNonempty ,
TShader : : Includer & includer ,
const std : : string sourceEntryPointName = " " ,
const TEnvironment * environment = nullptr ) // optional way of fully setting all versions, overriding the above
{
// This must be undone (.pop()) by the caller, after it finishes consuming the created tree.
GetThreadPoolAllocator ( ) . push ( ) ;
if ( numStrings = = 0 )
return true ;
// Move to length-based strings, rather than null-terminated strings.
// Also, add strings to include the preamble and to ensure the shader is not null,
// which lets the grammar accept what was a null (post preprocessing) shader.
//
// Shader will look like
// string 0: system preamble
// string 1: custom preamble
// string 2...numStrings+1: user's shader
// string numStrings+2: "int;"
const int numPre = 2 ;
const int numPost = requireNonempty ? 1 : 0 ;
const int numTotal = numPre + numStrings + numPost ;
std : : unique_ptr < size_t [ ] > lengths ( new size_t [ numTotal ] ) ;
std : : unique_ptr < const char * [ ] > strings ( new const char * [ numTotal ] ) ;
std : : unique_ptr < const char * [ ] > names ( new const char * [ numTotal ] ) ;
for ( int s = 0 ; s < numStrings ; + + s ) {
strings [ s + numPre ] = shaderStrings [ s ] ;
if ( inputLengths = = nullptr | | inputLengths [ s ] < 0 )
lengths [ s + numPre ] = strlen ( shaderStrings [ s ] ) ;
else
lengths [ s + numPre ] = inputLengths [ s ] ;
}
if ( stringNames ! = nullptr ) {
for ( int s = 0 ; s < numStrings ; + + s )
names [ s + numPre ] = stringNames [ s ] ;
} else {
for ( int s = 0 ; s < numStrings ; + + s )
names [ s + numPre ] = nullptr ;
}
// Get all the stages, languages, clients, and other environment
// stuff sorted out.
2021-10-31 17:19:26 +00:00
EShSource sourceGuess = ( messages & EShMsgReadHlsl ) ! = 0 ? EShSourceHlsl : EShSourceGlsl ;
2021-10-28 21:26:53 +00:00
SpvVersion spvVersion ;
EShLanguage stage = compiler - > getLanguage ( ) ;
2021-10-31 17:19:26 +00:00
TranslateEnvironment ( environment , messages , sourceGuess , stage , spvVersion ) ;
# ifdef ENABLE_HLSL
EShSource source = sourceGuess ;
2021-10-28 21:26:53 +00:00
if ( environment ! = nullptr & & environment - > target . hlslFunctionality1 )
intermediate . setHlslFunctionality1 ( ) ;
2021-10-31 17:19:26 +00:00
# else
const EShSource source = EShSourceGlsl ;
# endif
2021-10-28 21:26:53 +00:00
// First, without using the preprocessor or parser, find the #version, so we know what
// symbol tables, processing rules, etc. to set up. This does not need the extra strings
// outlined above, just the user shader, after the system and user preambles.
glslang : : TInputScanner userInput ( numStrings , & strings [ numPre ] , & lengths [ numPre ] ) ;
int version = 0 ;
EProfile profile = ENoProfile ;
bool versionNotFirstToken = false ;
bool versionNotFirst = ( source = = EShSourceHlsl )
? true
: userInput . scanVersion ( version , profile , versionNotFirstToken ) ;
bool versionNotFound = version = = 0 ;
if ( forceDefaultVersionAndProfile & & source = = EShSourceGlsl ) {
2021-10-31 17:19:26 +00:00
# if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
2021-10-28 21:26:53 +00:00
if ( ! ( messages & EShMsgSuppressWarnings ) & & ! versionNotFound & &
( version ! = defaultVersion | | profile ! = defaultProfile ) ) {
compiler - > infoSink . info < < " Warning, (version, profile) forced to be ( "
< < defaultVersion < < " , " < < ProfileName ( defaultProfile )
< < " ), while in source code it is ( "
< < version < < " , " < < ProfileName ( profile ) < < " ) \n " ;
}
2021-10-31 17:19:26 +00:00
# endif
2021-10-28 21:26:53 +00:00
if ( versionNotFound ) {
versionNotFirstToken = false ;
versionNotFirst = false ;
versionNotFound = false ;
}
version = defaultVersion ;
profile = defaultProfile ;
}
bool goodVersion = DeduceVersionProfile ( compiler - > infoSink , stage ,
versionNotFirst , defaultVersion , source , version , profile , spvVersion ) ;
2021-10-31 17:19:26 +00:00
# ifdef GLSLANG_WEB
profile = EEsProfile ;
version = 310 ;
# elif defined(GLSLANG_ANGLE)
profile = ECoreProfile ;
version = 450 ;
# endif
2021-10-28 21:26:53 +00:00
bool versionWillBeError = ( versionNotFound | | ( profile = = EEsProfile & & version > = 300 & & versionNotFirst ) ) ;
2021-10-31 17:19:26 +00:00
# if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
2021-10-28 21:26:53 +00:00
bool warnVersionNotFirst = false ;
if ( ! versionWillBeError & & versionNotFirstToken ) {
if ( messages & EShMsgRelaxedErrors )
warnVersionNotFirst = true ;
else
versionWillBeError = true ;
}
2021-10-31 17:19:26 +00:00
# endif
2021-10-28 21:26:53 +00:00
intermediate . setSource ( source ) ;
intermediate . setVersion ( version ) ;
intermediate . setProfile ( profile ) ;
intermediate . setSpv ( spvVersion ) ;
RecordProcesses ( intermediate , messages , sourceEntryPointName ) ;
if ( spvVersion . vulkan > 0 )
intermediate . setOriginUpperLeft ( ) ;
2021-10-31 17:19:26 +00:00
# ifdef ENABLE_HLSL
2021-10-28 21:26:53 +00:00
if ( ( messages & EShMsgHlslOffsets ) | | source = = EShSourceHlsl )
intermediate . setHlslOffsets ( ) ;
2021-10-31 17:19:26 +00:00
# endif
2021-10-28 21:26:53 +00:00
if ( messages & EShMsgDebugInfo ) {
intermediate . setSourceFile ( names [ numPre ] ) ;
for ( int s = 0 ; s < numStrings ; + + s ) {
// The string may not be null-terminated, so make sure we provide
// the length along with the string.
intermediate . addSourceText ( strings [ numPre + s ] , lengths [ numPre + s ] ) ;
}
}
SetupBuiltinSymbolTable ( version , profile , spvVersion , source ) ;
TSymbolTable * cachedTable = SharedSymbolTables [ MapVersionToIndex ( version ) ]
[ MapSpvVersionToIndex ( spvVersion ) ]
[ MapProfileToIndex ( profile ) ]
[ MapSourceToIndex ( source ) ]
[ stage ] ;
// Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool.
std : : unique_ptr < TSymbolTable > symbolTable ( new TSymbolTable ) ;
if ( cachedTable )
symbolTable - > adoptLevels ( * cachedTable ) ;
2021-10-31 17:19:26 +00:00
if ( intermediate . getUniqueId ( ) ! = 0 )
symbolTable - > overwriteUniqueId ( intermediate . getUniqueId ( ) ) ;
2021-10-28 21:26:53 +00:00
// Add built-in symbols that are potentially context dependent;
// they get popped again further down.
if ( ! AddContextSpecificSymbols ( resources , compiler - > infoSink , * symbolTable , version , profile , spvVersion ,
stage , source ) ) {
return false ;
}
2021-10-31 17:19:26 +00:00
if ( messages & EShMsgBuiltinSymbolTable )
DumpBuiltinSymbolTable ( compiler - > infoSink , * symbolTable ) ;
2021-10-28 21:26:53 +00:00
//
// Now we can process the full shader under proper symbols and rules.
//
std : : unique_ptr < TParseContextBase > parseContext ( CreateParseContext ( * symbolTable , intermediate , version , profile , source ,
stage , compiler - > infoSink ,
spvVersion , forwardCompatible , messages , false , sourceEntryPointName ) ) ;
TPpContext ppContext ( * parseContext , names [ numPre ] ? names [ numPre ] : " " , includer ) ;
// only GLSL (bison triggered, really) needs an externally set scan context
glslang : : TScanContext scanContext ( * parseContext ) ;
if ( source = = EShSourceGlsl )
parseContext - > setScanContext ( & scanContext ) ;
parseContext - > setPpContext ( & ppContext ) ;
parseContext - > setLimits ( * resources ) ;
if ( ! goodVersion )
parseContext - > addError ( ) ;
2021-10-31 17:19:26 +00:00
# if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
2021-10-28 21:26:53 +00:00
if ( warnVersionNotFirst ) {
TSourceLoc loc ;
loc . init ( ) ;
parseContext - > warn ( loc , " Illegal to have non-comment, non-whitespace tokens before #version " , " #version " , " " ) ;
}
2021-10-31 17:19:26 +00:00
# endif
2021-10-28 21:26:53 +00:00
parseContext - > initializeExtensionBehavior ( ) ;
// Fill in the strings as outlined above.
std : : string preamble ;
parseContext - > getPreamble ( preamble ) ;
strings [ 0 ] = preamble . c_str ( ) ;
lengths [ 0 ] = strlen ( strings [ 0 ] ) ;
names [ 0 ] = nullptr ;
strings [ 1 ] = customPreamble ;
lengths [ 1 ] = strlen ( strings [ 1 ] ) ;
names [ 1 ] = nullptr ;
assert ( 2 = = numPre ) ;
if ( requireNonempty ) {
const int postIndex = numStrings + numPre ;
strings [ postIndex ] = " \n int; " ;
lengths [ postIndex ] = strlen ( strings [ numStrings + numPre ] ) ;
names [ postIndex ] = nullptr ;
}
TInputScanner fullInput ( numStrings + numPre + numPost , strings . get ( ) , lengths . get ( ) , names . get ( ) , numPre , numPost ) ;
// Push a new symbol allocation scope that will get used for the shader's globals.
symbolTable - > push ( ) ;
bool success = processingContext ( * parseContext , ppContext , fullInput ,
versionWillBeError , * symbolTable ,
intermediate , optLevel , messages ) ;
2021-10-31 17:19:26 +00:00
intermediate . setUniqueId ( symbolTable - > getMaxSymbolId ( ) ) ;
2021-10-28 21:26:53 +00:00
return success ;
}
2021-10-31 17:19:26 +00:00
# if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
2021-10-28 21:26:53 +00:00
// Responsible for keeping track of the most recent source string and line in
// the preprocessor and outputting newlines appropriately if the source string
// or line changes.
class SourceLineSynchronizer {
public :
SourceLineSynchronizer ( const std : : function < int ( ) > & lastSourceIndex ,
std : : string * output )
: getLastSourceIndex ( lastSourceIndex ) , output ( output ) , lastSource ( - 1 ) , lastLine ( 0 ) { }
// SourceLineSynchronizer(const SourceLineSynchronizer&) = delete;
// SourceLineSynchronizer& operator=(const SourceLineSynchronizer&) = delete;
// Sets the internally tracked source string index to that of the most
// recently read token. If we switched to a new source string, returns
// true and inserts a newline. Otherwise, returns false and outputs nothing.
bool syncToMostRecentString ( ) {
if ( getLastSourceIndex ( ) ! = lastSource ) {
// After switching to a new source string, we need to reset lastLine
// because line number resets every time a new source string is
// used. We also need to output a newline to separate the output
// from the previous source string (if there is one).
if ( lastSource ! = - 1 | | lastLine ! = 0 )
* output + = ' \n ' ;
lastSource = getLastSourceIndex ( ) ;
lastLine = - 1 ;
return true ;
}
return false ;
}
// Calls syncToMostRecentString() and then sets the internally tracked line
// number to tokenLine. If we switched to a new line, returns true and inserts
// newlines appropriately. Otherwise, returns false and outputs nothing.
bool syncToLine ( int tokenLine ) {
syncToMostRecentString ( ) ;
const bool newLineStarted = lastLine < tokenLine ;
for ( ; lastLine < tokenLine ; + + lastLine ) {
if ( lastLine > 0 ) * output + = ' \n ' ;
}
return newLineStarted ;
}
// Sets the internally tracked line number to newLineNum.
void setLineNum ( int newLineNum ) { lastLine = newLineNum ; }
private :
SourceLineSynchronizer & operator = ( const SourceLineSynchronizer & ) ;
// A function for getting the index of the last valid source string we've
// read tokens from.
const std : : function < int ( ) > getLastSourceIndex ;
// output string for newlines.
std : : string * output ;
// lastSource is the source string index (starting from 0) of the last token
// processed. It is tracked in order for newlines to be inserted when a new
// source string starts. -1 means we haven't started processing any source
// string.
int lastSource ;
// lastLine is the line number (starting from 1) of the last token processed.
// It is tracked in order for newlines to be inserted when a token appears
// on a new line. 0 means we haven't started processing any line in the
// current source string.
int lastLine ;
} ;
// DoPreprocessing is a valid ProcessingContext template argument,
// which only performs the preprocessing step of compilation.
// It places the result in the "string" argument to its constructor.
//
// This is not an officially supported or fully working path.
struct DoPreprocessing {
explicit DoPreprocessing ( std : : string * string ) : outputString ( string ) { }
bool operator ( ) ( TParseContextBase & parseContext , TPpContext & ppContext ,
TInputScanner & input , bool versionWillBeError ,
TSymbolTable & , TIntermediate & ,
EShOptimizationLevel , EShMessages )
{
// This is a list of tokens that do not require a space before or after.
static const std : : string unNeededSpaceTokens = " ;()[] " ;
static const std : : string noSpaceBeforeTokens = " , " ;
glslang : : TPpToken ppToken ;
parseContext . setScanner ( & input ) ;
ppContext . setInput ( input , versionWillBeError ) ;
std : : string outputBuffer ;
SourceLineSynchronizer lineSync (
std : : bind ( & TInputScanner : : getLastValidSourceIndex , & input ) , & outputBuffer ) ;
parseContext . setExtensionCallback ( [ & lineSync , & outputBuffer ] (
int line , const char * extension , const char * behavior ) {
lineSync . syncToLine ( line ) ;
outputBuffer + = " #extension " ;
outputBuffer + = extension ;
outputBuffer + = " : " ;
outputBuffer + = behavior ;
} ) ;
parseContext . setLineCallback ( [ & lineSync , & outputBuffer , & parseContext ] (
int curLineNum , int newLineNum , bool hasSource , int sourceNum , const char * sourceName ) {
// SourceNum is the number of the source-string that is being parsed.
lineSync . syncToLine ( curLineNum ) ;
outputBuffer + = " #line " ;
outputBuffer + = std : : to_string ( newLineNum ) ;
if ( hasSource ) {
outputBuffer + = ' ' ;
if ( sourceName ! = nullptr ) {
outputBuffer + = ' \" ' ;
outputBuffer + = sourceName ;
outputBuffer + = ' \" ' ;
} else {
outputBuffer + = std : : to_string ( sourceNum ) ;
}
}
if ( parseContext . lineDirectiveShouldSetNextLine ( ) ) {
// newLineNum is the new line number for the line following the #line
// directive. So the new line number for the current line is
newLineNum - = 1 ;
}
outputBuffer + = ' \n ' ;
// And we are at the next line of the #line directive now.
lineSync . setLineNum ( newLineNum + 1 ) ;
} ) ;
parseContext . setVersionCallback (
[ & lineSync , & outputBuffer ] ( int line , int version , const char * str ) {
lineSync . syncToLine ( line ) ;
outputBuffer + = " #version " ;
outputBuffer + = std : : to_string ( version ) ;
if ( str ) {
outputBuffer + = ' ' ;
outputBuffer + = str ;
}
} ) ;
parseContext . setPragmaCallback ( [ & lineSync , & outputBuffer ] (
int line , const glslang : : TVector < glslang : : TString > & ops ) {
lineSync . syncToLine ( line ) ;
outputBuffer + = " #pragma " ;
for ( size_t i = 0 ; i < ops . size ( ) ; + + i ) {
outputBuffer + = ops [ i ] . c_str ( ) ;
}
} ) ;
parseContext . setErrorCallback ( [ & lineSync , & outputBuffer ] (
int line , const char * errorMessage ) {
lineSync . syncToLine ( line ) ;
outputBuffer + = " #error " ;
outputBuffer + = errorMessage ;
} ) ;
int lastToken = EndOfInput ; // lastToken records the last token processed.
do {
int token = ppContext . tokenize ( ppToken ) ;
if ( token = = EndOfInput )
break ;
bool isNewString = lineSync . syncToMostRecentString ( ) ;
bool isNewLine = lineSync . syncToLine ( ppToken . loc . line ) ;
if ( isNewLine ) {
// Don't emit whitespace onto empty lines.
// Copy any whitespace characters at the start of a line
// from the input to the output.
outputBuffer + = std : : string ( ppToken . loc . column - 1 , ' ' ) ;
}
// Output a space in between tokens, but not at the start of a line,
// and also not around special tokens. This helps with readability
// and consistency.
if ( ! isNewString & & ! isNewLine & & lastToken ! = EndOfInput & &
( unNeededSpaceTokens . find ( ( char ) token ) = = std : : string : : npos ) & &
( unNeededSpaceTokens . find ( ( char ) lastToken ) = = std : : string : : npos ) & &
( noSpaceBeforeTokens . find ( ( char ) token ) = = std : : string : : npos ) ) {
outputBuffer + = ' ' ;
}
lastToken = token ;
if ( token = = PpAtomConstString )
outputBuffer + = " \" " ;
outputBuffer + = ppToken . name ;
if ( token = = PpAtomConstString )
outputBuffer + = " \" " ;
} while ( true ) ;
outputBuffer + = ' \n ' ;
* outputString = std : : move ( outputBuffer ) ;
bool success = true ;
if ( parseContext . getNumErrors ( ) > 0 ) {
success = false ;
parseContext . infoSink . info . prefix ( EPrefixError ) ;
parseContext . infoSink . info < < parseContext . getNumErrors ( ) < < " compilation errors. No code generated. \n \n " ;
}
return success ;
}
std : : string * outputString ;
} ;
2021-10-31 17:19:26 +00:00
# endif
2021-10-28 21:26:53 +00:00
// DoFullParse is a valid ProcessingConext template argument for fully
// parsing the shader. It populates the "intermediate" with the AST.
struct DoFullParse {
bool operator ( ) ( TParseContextBase & parseContext , TPpContext & ppContext ,
TInputScanner & fullInput , bool versionWillBeError ,
TSymbolTable & , TIntermediate & intermediate ,
EShOptimizationLevel optLevel , EShMessages messages )
{
bool success = true ;
// Parse the full shader.
if ( ! parseContext . parseShaderStrings ( ppContext , fullInput , versionWillBeError ) )
success = false ;
if ( success & & intermediate . getTreeRoot ( ) ) {
if ( optLevel = = EShOptNoGeneration )
parseContext . infoSink . info . message ( EPrefixNone , " No errors. No code generation or linking was requested. " ) ;
else
success = intermediate . postProcess ( intermediate . getTreeRoot ( ) , parseContext . getLanguage ( ) ) ;
} else if ( ! success ) {
parseContext . infoSink . info . prefix ( EPrefixError ) ;
parseContext . infoSink . info < < parseContext . getNumErrors ( ) < < " compilation errors. No code generated. \n \n " ;
}
2021-10-31 17:19:26 +00:00
# ifndef GLSLANG_ANGLE
2021-10-28 21:26:53 +00:00
if ( messages & EShMsgAST )
intermediate . output ( parseContext . infoSink , true ) ;
2021-10-31 17:19:26 +00:00
# endif
2021-10-28 21:26:53 +00:00
return success ;
}
} ;
2021-10-31 17:19:26 +00:00
# if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
2021-10-28 21:26:53 +00:00
// Take a single compilation unit, and run the preprocessor on it.
// Return: True if there were no issues found in preprocessing,
// False if during preprocessing any unknown version, pragmas or
// extensions were found.
//
// NOTE: Doing just preprocessing to obtain a correct preprocessed shader string
// is not an officially supported or fully working path.
bool PreprocessDeferred (
TCompiler * compiler ,
const char * const shaderStrings [ ] ,
const int numStrings ,
const int * inputLengths ,
const char * const stringNames [ ] ,
const char * preamble ,
const EShOptimizationLevel optLevel ,
const TBuiltInResource * resources ,
int defaultVersion , // use 100 for ES environment, 110 for desktop
EProfile defaultProfile ,
bool forceDefaultVersionAndProfile ,
bool forwardCompatible , // give errors for use of deprecated features
EShMessages messages , // warnings/errors/AST; things to print out
TShader : : Includer & includer ,
TIntermediate & intermediate , // returned tree, etc.
2021-10-31 17:19:26 +00:00
std : : string * outputString ,
TEnvironment * environment = nullptr )
2021-10-28 21:26:53 +00:00
{
DoPreprocessing parser ( outputString ) ;
return ProcessDeferred ( compiler , shaderStrings , numStrings , inputLengths , stringNames ,
preamble , optLevel , resources , defaultVersion ,
defaultProfile , forceDefaultVersionAndProfile ,
forwardCompatible , messages , intermediate , parser ,
2021-10-31 17:19:26 +00:00
false , includer , " " , environment ) ;
2021-10-28 21:26:53 +00:00
}
2021-10-31 17:19:26 +00:00
# endif
2021-10-28 21:26:53 +00:00
//
// do a partial compile on the given strings for a single compilation unit
// for a potential deferred link into a single stage (and deferred full compile of that
// stage through machine-dependent compilation).
//
// all preprocessing, parsing, semantic checks, etc. for a single compilation unit
// are done here.
//
// return: the tree and other information is filled into the intermediate argument,
// and true is returned by the function for success.
//
bool CompileDeferred (
TCompiler * compiler ,
const char * const shaderStrings [ ] ,
const int numStrings ,
const int * inputLengths ,
const char * const stringNames [ ] ,
const char * preamble ,
const EShOptimizationLevel optLevel ,
const TBuiltInResource * resources ,
int defaultVersion , // use 100 for ES environment, 110 for desktop
EProfile defaultProfile ,
bool forceDefaultVersionAndProfile ,
bool forwardCompatible , // give errors for use of deprecated features
EShMessages messages , // warnings/errors/AST; things to print out
TIntermediate & intermediate , // returned tree, etc.
TShader : : Includer & includer ,
const std : : string sourceEntryPointName = " " ,
TEnvironment * environment = nullptr )
{
DoFullParse parser ;
return ProcessDeferred ( compiler , shaderStrings , numStrings , inputLengths , stringNames ,
preamble , optLevel , resources , defaultVersion ,
defaultProfile , forceDefaultVersionAndProfile ,
forwardCompatible , messages , intermediate , parser ,
true , includer , sourceEntryPointName , environment ) ;
}
} // end anonymous namespace for local functions
//
// ShInitialize() should be called exactly once per process, not per thread.
//
int ShInitialize ( )
{
glslang : : InitGlobalLock ( ) ;
if ( ! InitProcess ( ) )
return 0 ;
glslang : : GetGlobalLock ( ) ;
+ + NumberOfClients ;
glslang : : ReleaseGlobalLock ( ) ;
if ( PerProcessGPA = = nullptr )
PerProcessGPA = new TPoolAllocator ( ) ;
glslang : : TScanContext : : fillInKeywordMap ( ) ;
# ifdef ENABLE_HLSL
glslang : : HlslScanContext : : fillInKeywordMap ( ) ;
# endif
return 1 ;
}
//
// Driver calls these to create and destroy compiler/linker
// objects.
//
ShHandle ShConstructCompiler ( const EShLanguage language , int debugOptions )
{
if ( ! InitThread ( ) )
return 0 ;
TShHandleBase * base = static_cast < TShHandleBase * > ( ConstructCompiler ( language , debugOptions ) ) ;
return reinterpret_cast < void * > ( base ) ;
}
ShHandle ShConstructLinker ( const EShExecutable executable , int debugOptions )
{
if ( ! InitThread ( ) )
return 0 ;
TShHandleBase * base = static_cast < TShHandleBase * > ( ConstructLinker ( executable , debugOptions ) ) ;
return reinterpret_cast < void * > ( base ) ;
}
ShHandle ShConstructUniformMap ( )
{
if ( ! InitThread ( ) )
return 0 ;
TShHandleBase * base = static_cast < TShHandleBase * > ( ConstructUniformMap ( ) ) ;
return reinterpret_cast < void * > ( base ) ;
}
void ShDestruct ( ShHandle handle )
{
if ( handle = = 0 )
return ;
TShHandleBase * base = static_cast < TShHandleBase * > ( handle ) ;
if ( base - > getAsCompiler ( ) )
DeleteCompiler ( base - > getAsCompiler ( ) ) ;
else if ( base - > getAsLinker ( ) )
DeleteLinker ( base - > getAsLinker ( ) ) ;
else if ( base - > getAsUniformMap ( ) )
DeleteUniformMap ( base - > getAsUniformMap ( ) ) ;
}
//
// Cleanup symbol tables
//
int ShFinalize ( )
{
glslang : : GetGlobalLock ( ) ;
- - NumberOfClients ;
assert ( NumberOfClients > = 0 ) ;
bool finalize = NumberOfClients = = 0 ;
glslang : : ReleaseGlobalLock ( ) ;
if ( ! finalize )
return 1 ;
for ( int version = 0 ; version < VersionCount ; + + version ) {
for ( int spvVersion = 0 ; spvVersion < SpvVersionCount ; + + spvVersion ) {
for ( int p = 0 ; p < ProfileCount ; + + p ) {
for ( int source = 0 ; source < SourceCount ; + + source ) {
for ( int stage = 0 ; stage < EShLangCount ; + + stage ) {
delete SharedSymbolTables [ version ] [ spvVersion ] [ p ] [ source ] [ stage ] ;
SharedSymbolTables [ version ] [ spvVersion ] [ p ] [ source ] [ stage ] = 0 ;
}
}
}
}
}
for ( int version = 0 ; version < VersionCount ; + + version ) {
for ( int spvVersion = 0 ; spvVersion < SpvVersionCount ; + + spvVersion ) {
for ( int p = 0 ; p < ProfileCount ; + + p ) {
for ( int source = 0 ; source < SourceCount ; + + source ) {
for ( int pc = 0 ; pc < EPcCount ; + + pc ) {
delete CommonSymbolTable [ version ] [ spvVersion ] [ p ] [ source ] [ pc ] ;
CommonSymbolTable [ version ] [ spvVersion ] [ p ] [ source ] [ pc ] = 0 ;
}
}
}
}
}
if ( PerProcessGPA ! = nullptr ) {
delete PerProcessGPA ;
PerProcessGPA = nullptr ;
}
glslang : : TScanContext : : deleteKeywordMap ( ) ;
# ifdef ENABLE_HLSL
glslang : : HlslScanContext : : deleteKeywordMap ( ) ;
# endif
return 1 ;
}
//
// Do a full compile on the given strings for a single compilation unit
// forming a complete stage. The result of the machine dependent compilation
// is left in the provided compile object.
//
// Return: The return value is really boolean, indicating
// success (1) or failure (0).
//
int ShCompile (
const ShHandle handle ,
const char * const shaderStrings [ ] ,
const int numStrings ,
const int * inputLengths ,
const EShOptimizationLevel optLevel ,
const TBuiltInResource * resources ,
int /*debugOptions*/ ,
int defaultVersion , // use 100 for ES environment, 110 for desktop
bool forwardCompatible , // give errors for use of deprecated features
EShMessages messages // warnings/errors/AST; things to print out
)
{
// Map the generic handle to the C++ object
if ( handle = = 0 )
return 0 ;
TShHandleBase * base = reinterpret_cast < TShHandleBase * > ( handle ) ;
TCompiler * compiler = base - > getAsCompiler ( ) ;
if ( compiler = = 0 )
return 0 ;
SetThreadPoolAllocator ( compiler - > getPool ( ) ) ;
compiler - > infoSink . info . erase ( ) ;
compiler - > infoSink . debug . erase ( ) ;
TIntermediate intermediate ( compiler - > getLanguage ( ) ) ;
TShader : : ForbidIncluder includer ;
bool success = CompileDeferred ( compiler , shaderStrings , numStrings , inputLengths , nullptr ,
" " , optLevel , resources , defaultVersion , ENoProfile , false ,
forwardCompatible , messages , intermediate , includer ) ;
//
// Call the machine dependent compiler
//
if ( success & & intermediate . getTreeRoot ( ) & & optLevel ! = EShOptNoGeneration )
success = compiler - > compile ( intermediate . getTreeRoot ( ) , intermediate . getVersion ( ) , intermediate . getProfile ( ) ) ;
intermediate . removeTree ( ) ;
// Throw away all the temporary memory used by the compilation process.
// The push was done in the CompileDeferred() call above.
GetThreadPoolAllocator ( ) . pop ( ) ;
return success ? 1 : 0 ;
}
//
// Link the given compile objects.
//
// Return: The return value of is really boolean, indicating
// success or failure.
//
int ShLinkExt (
const ShHandle linkHandle ,
const ShHandle compHandles [ ] ,
const int numHandles )
{
if ( linkHandle = = 0 | | numHandles = = 0 )
return 0 ;
THandleList cObjects ;
for ( int i = 0 ; i < numHandles ; + + i ) {
if ( compHandles [ i ] = = 0 )
return 0 ;
TShHandleBase * base = reinterpret_cast < TShHandleBase * > ( compHandles [ i ] ) ;
if ( base - > getAsLinker ( ) ) {
cObjects . push_back ( base - > getAsLinker ( ) ) ;
}
if ( base - > getAsCompiler ( ) )
cObjects . push_back ( base - > getAsCompiler ( ) ) ;
if ( cObjects [ i ] = = 0 )
return 0 ;
}
TShHandleBase * base = reinterpret_cast < TShHandleBase * > ( linkHandle ) ;
TLinker * linker = static_cast < TLinker * > ( base - > getAsLinker ( ) ) ;
SetThreadPoolAllocator ( linker - > getPool ( ) ) ;
if ( linker = = 0 )
return 0 ;
linker - > infoSink . info . erase ( ) ;
for ( int i = 0 ; i < numHandles ; + + i ) {
if ( cObjects [ i ] - > getAsCompiler ( ) ) {
if ( ! cObjects [ i ] - > getAsCompiler ( ) - > linkable ( ) ) {
linker - > infoSink . info . message ( EPrefixError , " Not all shaders have valid object code. " ) ;
return 0 ;
}
}
}
bool ret = linker - > link ( cObjects ) ;
return ret ? 1 : 0 ;
}
//
// ShSetEncrpytionMethod is a place-holder for specifying
// how source code is encrypted.
//
void ShSetEncryptionMethod ( ShHandle handle )
{
if ( handle = = 0 )
return ;
}
//
// Return any compiler/linker/uniformmap log of messages for the application.
//
const char * ShGetInfoLog ( const ShHandle handle )
{
if ( handle = = 0 )
return 0 ;
TShHandleBase * base = static_cast < TShHandleBase * > ( handle ) ;
TInfoSink * infoSink ;
if ( base - > getAsCompiler ( ) )
infoSink = & ( base - > getAsCompiler ( ) - > getInfoSink ( ) ) ;
else if ( base - > getAsLinker ( ) )
infoSink = & ( base - > getAsLinker ( ) - > getInfoSink ( ) ) ;
else
return 0 ;
infoSink - > info < < infoSink - > debug . c_str ( ) ;
return infoSink - > info . c_str ( ) ;
}
//
// Return the resulting binary code from the link process. Structure
// is machine dependent.
//
const void * ShGetExecutable ( const ShHandle handle )
{
if ( handle = = 0 )
return 0 ;
TShHandleBase * base = reinterpret_cast < TShHandleBase * > ( handle ) ;
TLinker * linker = static_cast < TLinker * > ( base - > getAsLinker ( ) ) ;
if ( linker = = 0 )
return 0 ;
return linker - > getObjectCode ( ) ;
}
//
// Let the linker know where the application said it's attributes are bound.
// The linker does not use these values, they are remapped by the ICD or
// hardware. It just needs them to know what's aliased.
//
// Return: The return value of is really boolean, indicating
// success or failure.
//
int ShSetVirtualAttributeBindings ( const ShHandle handle , const ShBindingTable * table )
{
if ( handle = = 0 )
return 0 ;
TShHandleBase * base = reinterpret_cast < TShHandleBase * > ( handle ) ;
TLinker * linker = static_cast < TLinker * > ( base - > getAsLinker ( ) ) ;
if ( linker = = 0 )
return 0 ;
linker - > setAppAttributeBindings ( table ) ;
return 1 ;
}
//
// Let the linker know where the predefined attributes have to live.
//
int ShSetFixedAttributeBindings ( const ShHandle handle , const ShBindingTable * table )
{
if ( handle = = 0 )
return 0 ;
TShHandleBase * base = reinterpret_cast < TShHandleBase * > ( handle ) ;
TLinker * linker = static_cast < TLinker * > ( base - > getAsLinker ( ) ) ;
if ( linker = = 0 )
return 0 ;
linker - > setFixedAttributeBindings ( table ) ;
return 1 ;
}
//
// Some attribute locations are off-limits to the linker...
//
int ShExcludeAttributes ( const ShHandle handle , int * attributes , int count )
{
if ( handle = = 0 )
return 0 ;
TShHandleBase * base = reinterpret_cast < TShHandleBase * > ( handle ) ;
TLinker * linker = static_cast < TLinker * > ( base - > getAsLinker ( ) ) ;
if ( linker = = 0 )
return 0 ;
linker - > setExcludedAttributes ( attributes , count ) ;
return 1 ;
}
//
// Return the index for OpenGL to use for knowing where a uniform lives.
//
// Return: The return value of is really boolean, indicating
// success or failure.
//
int ShGetUniformLocation ( const ShHandle handle , const char * name )
{
if ( handle = = 0 )
return - 1 ;
TShHandleBase * base = reinterpret_cast < TShHandleBase * > ( handle ) ;
TUniformMap * uniformMap = base - > getAsUniformMap ( ) ;
if ( uniformMap = = 0 )
return - 1 ;
return uniformMap - > getLocation ( name ) ;
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Deferred-Lowering C++ Interface
// -----------------------------------
//
// Below is a new alternate C++ interface that might potentially replace the above
// opaque handle-based interface.
//
// See more detailed comment in ShaderLang.h
//
namespace glslang {
2021-10-31 17:19:26 +00:00
Version GetVersion ( )
{
Version version ;
version . major = GLSLANG_VERSION_MAJOR ;
version . minor = GLSLANG_VERSION_MINOR ;
version . patch = GLSLANG_VERSION_PATCH ;
version . flavor = GLSLANG_VERSION_FLAVOR ;
return version ;
}
2021-10-28 21:26:53 +00:00
# define QUOTE(s) #s
# define STR(n) QUOTE(n)
const char * GetEsslVersionString ( )
{
2021-10-31 17:19:26 +00:00
return " OpenGL ES GLSL 3.20 glslang Khronos. " STR ( GLSLANG_VERSION_MAJOR ) " . " STR ( GLSLANG_VERSION_MINOR ) " . " STR (
GLSLANG_VERSION_PATCH ) GLSLANG_VERSION_FLAVOR ;
2021-10-28 21:26:53 +00:00
}
const char * GetGlslVersionString ( )
{
2021-10-31 17:19:26 +00:00
return " 4.60 glslang Khronos. " STR ( GLSLANG_VERSION_MAJOR ) " . " STR ( GLSLANG_VERSION_MINOR ) " . " STR (
GLSLANG_VERSION_PATCH ) GLSLANG_VERSION_FLAVOR ;
2021-10-28 21:26:53 +00:00
}
int GetKhronosToolId ( )
{
return 8 ;
}
bool InitializeProcess ( )
{
return ShInitialize ( ) ! = 0 ;
}
void FinalizeProcess ( )
{
ShFinalize ( ) ;
}
class TDeferredCompiler : public TCompiler {
public :
TDeferredCompiler ( EShLanguage s , TInfoSink & i ) : TCompiler ( s , i ) { }
virtual bool compile ( TIntermNode * , int = 0 , EProfile = ENoProfile ) { return true ; }
} ;
TShader : : TShader ( EShLanguage s )
: stage ( s ) , lengths ( nullptr ) , stringNames ( nullptr ) , preamble ( " " )
{
pool = new TPoolAllocator ;
infoSink = new TInfoSink ;
compiler = new TDeferredCompiler ( stage , * infoSink ) ;
intermediate = new TIntermediate ( s ) ;
// clear environment (avoid constructors in them for use in a C interface)
environment . input . languageFamily = EShSourceNone ;
environment . input . dialect = EShClientNone ;
2021-10-31 17:19:26 +00:00
environment . input . vulkanRulesRelaxed = false ;
2021-10-28 21:26:53 +00:00
environment . client . client = EShClientNone ;
environment . target . language = EShTargetNone ;
environment . target . hlslFunctionality1 = false ;
}
TShader : : ~ TShader ( )
{
delete infoSink ;
delete compiler ;
delete intermediate ;
delete pool ;
}
void TShader : : setStrings ( const char * const * s , int n )
{
strings = s ;
numStrings = n ;
lengths = nullptr ;
}
void TShader : : setStringsWithLengths ( const char * const * s , const int * l , int n )
{
strings = s ;
numStrings = n ;
lengths = l ;
}
void TShader : : setStringsWithLengthsAndNames (
const char * const * s , const int * l , const char * const * names , int n )
{
strings = s ;
numStrings = n ;
lengths = l ;
stringNames = names ;
}
void TShader : : setEntryPoint ( const char * entryPoint )
{
intermediate - > setEntryPointName ( entryPoint ) ;
}
void TShader : : setSourceEntryPoint ( const char * name )
{
sourceEntryPointName = name ;
}
2021-10-31 17:19:26 +00:00
// Log initial settings and transforms.
// See comment for class TProcesses.
2021-10-28 21:26:53 +00:00
void TShader : : addProcesses ( const std : : vector < std : : string > & p )
{
intermediate - > addProcesses ( p ) ;
}
2021-10-31 17:19:26 +00:00
void TShader : : setUniqueId ( unsigned long long id )
{
intermediate - > setUniqueId ( id ) ;
}
void TShader : : setInvertY ( bool invert ) { intermediate - > setInvertY ( invert ) ; }
void TShader : : setNanMinMaxClamp ( bool useNonNan ) { intermediate - > setNanMinMaxClamp ( useNonNan ) ; }
# ifndef GLSLANG_WEB
2021-10-28 21:26:53 +00:00
// Set binding base for given resource type
void TShader : : setShiftBinding ( TResourceType res , unsigned int base ) {
intermediate - > setShiftBinding ( res , base ) ;
}
// Set binding base for given resource type for a given binding set.
void TShader : : setShiftBindingForSet ( TResourceType res , unsigned int base , unsigned int set ) {
intermediate - > setShiftBindingForSet ( res , base , set ) ;
}
// Set binding base for sampler types
void TShader : : setShiftSamplerBinding ( unsigned int base ) { setShiftBinding ( EResSampler , base ) ; }
// Set binding base for texture types (SRV)
void TShader : : setShiftTextureBinding ( unsigned int base ) { setShiftBinding ( EResTexture , base ) ; }
// Set binding base for image types
void TShader : : setShiftImageBinding ( unsigned int base ) { setShiftBinding ( EResImage , base ) ; }
// Set binding base for uniform buffer objects (CBV)
void TShader : : setShiftUboBinding ( unsigned int base ) { setShiftBinding ( EResUbo , base ) ; }
// Synonym for setShiftUboBinding, to match HLSL language.
void TShader : : setShiftCbufferBinding ( unsigned int base ) { setShiftBinding ( EResUbo , base ) ; }
// Set binding base for UAV (unordered access view)
void TShader : : setShiftUavBinding ( unsigned int base ) { setShiftBinding ( EResUav , base ) ; }
// Set binding base for SSBOs
void TShader : : setShiftSsboBinding ( unsigned int base ) { setShiftBinding ( EResSsbo , base ) ; }
// Enables binding automapping using TIoMapper
void TShader : : setAutoMapBindings ( bool map ) { intermediate - > setAutoMapBindings ( map ) ; }
// Enables position.Y output negation in vertex shader
2021-10-31 17:19:26 +00:00
2021-10-28 21:26:53 +00:00
// Fragile: currently within one stage: simple auto-assignment of location
void TShader : : setAutoMapLocations ( bool map ) { intermediate - > setAutoMapLocations ( map ) ; }
void TShader : : addUniformLocationOverride ( const char * name , int loc )
{
intermediate - > addUniformLocationOverride ( name , loc ) ;
}
void TShader : : setUniformLocationBase ( int base )
{
intermediate - > setUniformLocationBase ( base ) ;
}
void TShader : : setNoStorageFormat ( bool useUnknownFormat ) { intermediate - > setNoStorageFormat ( useUnknownFormat ) ; }
void TShader : : setResourceSetBinding ( const std : : vector < std : : string > & base ) { intermediate - > setResourceSetBinding ( base ) ; }
void TShader : : setTextureSamplerTransformMode ( EShTextureSamplerTransformMode mode ) { intermediate - > setTextureSamplerTransformMode ( mode ) ; }
2021-10-31 17:19:26 +00:00
# endif
void TShader : : addBlockStorageOverride ( const char * nameStr , TBlockStorageClass backing ) { intermediate - > addBlockStorageOverride ( nameStr , backing ) ; }
void TShader : : setGlobalUniformBlockName ( const char * name ) { intermediate - > setGlobalUniformBlockName ( name ) ; }
void TShader : : setGlobalUniformSet ( unsigned int set ) { intermediate - > setGlobalUniformSet ( set ) ; }
void TShader : : setGlobalUniformBinding ( unsigned int binding ) { intermediate - > setGlobalUniformBinding ( binding ) ; }
void TShader : : setAtomicCounterBlockName ( const char * name ) { intermediate - > setAtomicCounterBlockName ( name ) ; }
void TShader : : setAtomicCounterBlockSet ( unsigned int set ) { intermediate - > setAtomicCounterBlockSet ( set ) ; }
# ifdef ENABLE_HLSL
// See comment above TDefaultHlslIoMapper in iomapper.cpp:
void TShader : : setHlslIoMapping ( bool hlslIoMap ) { intermediate - > setHlslIoMapping ( hlslIoMap ) ; }
void TShader : : setFlattenUniformArrays ( bool flatten ) { intermediate - > setFlattenUniformArrays ( flatten ) ; }
# endif
2021-10-28 21:26:53 +00:00
//
// Turn the shader strings into a parse tree in the TIntermediate.
//
// Returns true for success.
//
bool TShader : : parse ( const TBuiltInResource * builtInResources , int defaultVersion , EProfile defaultProfile , bool forceDefaultVersionAndProfile ,
bool forwardCompatible , EShMessages messages , Includer & includer )
{
if ( ! InitThread ( ) )
return false ;
SetThreadPoolAllocator ( pool ) ;
if ( ! preamble )
preamble = " " ;
return CompileDeferred ( compiler , strings , numStrings , lengths , stringNames ,
preamble , EShOptNone , builtInResources , defaultVersion ,
defaultProfile , forceDefaultVersionAndProfile ,
forwardCompatible , messages , * intermediate , includer , sourceEntryPointName ,
& environment ) ;
}
2021-10-31 17:19:26 +00:00
# if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
2021-10-28 21:26:53 +00:00
// Fill in a string with the result of preprocessing ShaderStrings
// Returns true if all extensions, pragmas and version strings were valid.
//
// NOTE: Doing just preprocessing to obtain a correct preprocessed shader string
// is not an officially supported or fully working path.
bool TShader : : preprocess ( const TBuiltInResource * builtInResources ,
int defaultVersion , EProfile defaultProfile ,
bool forceDefaultVersionAndProfile ,
bool forwardCompatible , EShMessages message ,
std : : string * output_string ,
Includer & includer )
{
if ( ! InitThread ( ) )
return false ;
SetThreadPoolAllocator ( pool ) ;
if ( ! preamble )
preamble = " " ;
return PreprocessDeferred ( compiler , strings , numStrings , lengths , stringNames , preamble ,
EShOptNone , builtInResources , defaultVersion ,
defaultProfile , forceDefaultVersionAndProfile ,
2021-10-31 17:19:26 +00:00
forwardCompatible , message , includer , * intermediate , output_string ,
& environment ) ;
2021-10-28 21:26:53 +00:00
}
2021-10-31 17:19:26 +00:00
# endif
2021-10-28 21:26:53 +00:00
const char * TShader : : getInfoLog ( )
{
return infoSink - > info . c_str ( ) ;
}
const char * TShader : : getInfoDebugLog ( )
{
return infoSink - > debug . c_str ( ) ;
}
2021-10-31 17:19:26 +00:00
TProgram : : TProgram ( ) :
# if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
reflection ( 0 ) ,
# endif
linked ( false )
2021-10-28 21:26:53 +00:00
{
pool = new TPoolAllocator ;
infoSink = new TInfoSink ;
for ( int s = 0 ; s < EShLangCount ; + + s ) {
intermediate [ s ] = 0 ;
newedIntermediate [ s ] = false ;
}
}
TProgram : : ~ TProgram ( )
{
delete infoSink ;
2021-10-31 17:19:26 +00:00
# if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
2021-10-28 21:26:53 +00:00
delete reflection ;
2021-10-31 17:19:26 +00:00
# endif
2021-10-28 21:26:53 +00:00
for ( int s = 0 ; s < EShLangCount ; + + s )
if ( newedIntermediate [ s ] )
delete intermediate [ s ] ;
delete pool ;
}
//
// Merge the compilation units within each stage into a single TIntermediate.
// All starting compilation units need to be the result of calling TShader::parse().
//
// Return true for success.
//
bool TProgram : : link ( EShMessages messages )
{
if ( linked )
return false ;
linked = true ;
bool error = false ;
SetThreadPoolAllocator ( pool ) ;
for ( int s = 0 ; s < EShLangCount ; + + s ) {
if ( ! linkStage ( ( EShLanguage ) s , messages ) )
error = true ;
}
2021-10-31 17:19:26 +00:00
if ( ! error ) {
if ( ! crossStageCheck ( messages ) )
error = true ;
}
2021-10-28 21:26:53 +00:00
return ! error ;
}
//
// Merge the compilation units within the given stage into a single TIntermediate.
//
// Return true for success.
//
bool TProgram : : linkStage ( EShLanguage stage , EShMessages messages )
{
if ( stages [ stage ] . size ( ) = = 0 )
return true ;
2021-10-31 17:19:26 +00:00
# if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
2021-10-28 21:26:53 +00:00
int numEsShaders = 0 , numNonEsShaders = 0 ;
for ( auto it = stages [ stage ] . begin ( ) ; it ! = stages [ stage ] . end ( ) ; + + it ) {
if ( ( * it ) - > intermediate - > getProfile ( ) = = EEsProfile ) {
numEsShaders + + ;
} else {
numNonEsShaders + + ;
}
}
if ( numEsShaders > 0 & & numNonEsShaders > 0 ) {
infoSink - > info . message ( EPrefixError , " Cannot mix ES profile with non-ES profile shaders " ) ;
return false ;
} else if ( numEsShaders > 1 ) {
infoSink - > info . message ( EPrefixError , " Cannot attach multiple ES shaders of the same type to a single program " ) ;
return false ;
}
//
// Be efficient for the common single compilation unit per stage case,
// reusing it's TIntermediate instead of merging into a new one.
//
TIntermediate * firstIntermediate = stages [ stage ] . front ( ) - > intermediate ;
if ( stages [ stage ] . size ( ) = = 1 )
intermediate [ stage ] = firstIntermediate ;
else {
intermediate [ stage ] = new TIntermediate ( stage ,
firstIntermediate - > getVersion ( ) ,
firstIntermediate - > getProfile ( ) ) ;
2021-10-31 17:19:26 +00:00
intermediate [ stage ] - > setLimits ( firstIntermediate - > getLimits ( ) ) ;
2021-10-28 21:26:53 +00:00
// The new TIntermediate must use the same origin as the original TIntermediates.
// Otherwise linking will fail due to different coordinate systems.
if ( firstIntermediate - > getOriginUpperLeft ( ) ) {
intermediate [ stage ] - > setOriginUpperLeft ( ) ;
}
intermediate [ stage ] - > setSpv ( firstIntermediate - > getSpv ( ) ) ;
newedIntermediate [ stage ] = true ;
}
if ( messages & EShMsgAST )
infoSink - > info < < " \n Linked " < < StageName ( stage ) < < " stage: \n \n " ;
if ( stages [ stage ] . size ( ) > 1 ) {
std : : list < TShader * > : : const_iterator it ;
for ( it = stages [ stage ] . begin ( ) ; it ! = stages [ stage ] . end ( ) ; + + it )
intermediate [ stage ] - > merge ( * infoSink , * ( * it ) - > intermediate ) ;
}
2021-10-31 17:19:26 +00:00
# else
intermediate [ stage ] = stages [ stage ] . front ( ) - > intermediate ;
# endif
2021-10-28 21:26:53 +00:00
intermediate [ stage ] - > finalCheck ( * infoSink , ( messages & EShMsgKeepUncalled ) ! = 0 ) ;
2021-10-31 17:19:26 +00:00
# ifndef GLSLANG_ANGLE
2021-10-28 21:26:53 +00:00
if ( messages & EShMsgAST )
intermediate [ stage ] - > output ( * infoSink , true ) ;
2021-10-31 17:19:26 +00:00
# endif
2021-10-28 21:26:53 +00:00
return intermediate [ stage ] - > getNumErrors ( ) = = 0 ;
}
2021-10-31 17:19:26 +00:00
//
// Check that there are no errors in linker objects accross stages
//
// Return true if no errors.
//
bool TProgram : : crossStageCheck ( EShMessages ) {
// make temporary intermediates to hold the linkage symbols for each linking interface
// while we do the checks
// Independent interfaces are:
// all uniform variables and blocks
// all buffer blocks
// all in/out on a stage boundary
TVector < TIntermediate * > activeStages ;
for ( int s = 0 ; s < EShLangCount ; + + s ) {
if ( intermediate [ s ] )
activeStages . push_back ( intermediate [ s ] ) ;
}
// no extra linking if there is only one stage
if ( ! ( activeStages . size ( ) > 1 ) )
return true ;
// setup temporary tree to hold unfirom objects from different stages
TIntermediate * firstIntermediate = activeStages . front ( ) ;
TIntermediate uniforms ( EShLangCount ,
firstIntermediate - > getVersion ( ) ,
firstIntermediate - > getProfile ( ) ) ;
uniforms . setSpv ( firstIntermediate - > getSpv ( ) ) ;
TIntermAggregate uniformObjects ( EOpLinkerObjects ) ;
TIntermAggregate root ( EOpSequence ) ;
root . getSequence ( ) . push_back ( & uniformObjects ) ;
uniforms . setTreeRoot ( & root ) ;
bool error = false ;
// merge uniforms from all stages into a single intermediate
for ( unsigned int i = 0 ; i < activeStages . size ( ) ; + + i ) {
uniforms . mergeUniformObjects ( * infoSink , * activeStages [ i ] ) ;
}
error | = uniforms . getNumErrors ( ) ! = 0 ;
// copy final definition of global block back into each stage
for ( unsigned int i = 0 ; i < activeStages . size ( ) ; + + i ) {
// We only want to merge into already existing global uniform blocks.
// A stage that doesn't already know about the global doesn't care about it's content.
// Otherwise we end up pointing to the same object between different stages
// and that will break binding/set remappings
bool mergeExistingOnly = true ;
activeStages [ i ] - > mergeGlobalUniformBlocks ( * infoSink , uniforms , mergeExistingOnly ) ;
}
// compare cross stage symbols for each stage boundary
for ( unsigned int i = 1 ; i < activeStages . size ( ) ; + + i ) {
activeStages [ i - 1 ] - > checkStageIO ( * infoSink , * activeStages [ i ] ) ;
error | = ( activeStages [ i - 1 ] - > getNumErrors ( ) ! = 0 ) ;
}
return ! error ;
}
2021-10-28 21:26:53 +00:00
const char * TProgram : : getInfoLog ( )
{
return infoSink - > info . c_str ( ) ;
}
const char * TProgram : : getInfoDebugLog ( )
{
return infoSink - > debug . c_str ( ) ;
}
2021-10-31 17:19:26 +00:00
# if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
2021-10-28 21:26:53 +00:00
//
// Reflection implementation.
//
2021-10-31 17:19:26 +00:00
bool TProgram : : buildReflection ( int opts )
2021-10-28 21:26:53 +00:00
{
2021-10-31 17:19:26 +00:00
if ( ! linked | | reflection ! = nullptr )
2021-10-28 21:26:53 +00:00
return false ;
2021-10-31 17:19:26 +00:00
int firstStage = EShLangVertex , lastStage = EShLangFragment ;
if ( opts & EShReflectionIntermediateIO ) {
// if we're reflecting intermediate I/O, determine the first and last stage linked and use those as the
// boundaries for which stages generate pipeline inputs/outputs
firstStage = EShLangCount ;
lastStage = 0 ;
for ( int s = 0 ; s < EShLangCount ; + + s ) {
if ( intermediate [ s ] ) {
firstStage = std : : min ( firstStage , s ) ;
lastStage = std : : max ( lastStage , s ) ;
}
}
}
reflection = new TReflection ( ( EShReflectionOptions ) opts , ( EShLanguage ) firstStage , ( EShLanguage ) lastStage ) ;
2021-10-28 21:26:53 +00:00
for ( int s = 0 ; s < EShLangCount ; + + s ) {
if ( intermediate [ s ] ) {
if ( ! reflection - > addStage ( ( EShLanguage ) s , * intermediate [ s ] ) )
return false ;
}
}
return true ;
}
2021-10-31 17:19:26 +00:00
unsigned TProgram : : getLocalSize ( int dim ) const { return reflection - > getLocalSize ( dim ) ; }
int TProgram : : getReflectionIndex ( const char * name ) const { return reflection - > getIndex ( name ) ; }
int TProgram : : getReflectionPipeIOIndex ( const char * name , const bool inOrOut ) const
{ return reflection - > getPipeIOIndex ( name , inOrOut ) ; }
int TProgram : : getNumUniformVariables ( ) const { return reflection - > getNumUniforms ( ) ; }
const TObjectReflection & TProgram : : getUniform ( int index ) const { return reflection - > getUniform ( index ) ; }
int TProgram : : getNumUniformBlocks ( ) const { return reflection - > getNumUniformBlocks ( ) ; }
const TObjectReflection & TProgram : : getUniformBlock ( int index ) const { return reflection - > getUniformBlock ( index ) ; }
int TProgram : : getNumPipeInputs ( ) const { return reflection - > getNumPipeInputs ( ) ; }
const TObjectReflection & TProgram : : getPipeInput ( int index ) const { return reflection - > getPipeInput ( index ) ; }
int TProgram : : getNumPipeOutputs ( ) const { return reflection - > getNumPipeOutputs ( ) ; }
const TObjectReflection & TProgram : : getPipeOutput ( int index ) const { return reflection - > getPipeOutput ( index ) ; }
int TProgram : : getNumBufferVariables ( ) const { return reflection - > getNumBufferVariables ( ) ; }
const TObjectReflection & TProgram : : getBufferVariable ( int index ) const { return reflection - > getBufferVariable ( index ) ; }
int TProgram : : getNumBufferBlocks ( ) const { return reflection - > getNumStorageBuffers ( ) ; }
const TObjectReflection & TProgram : : getBufferBlock ( int index ) const { return reflection - > getStorageBufferBlock ( index ) ; }
int TProgram : : getNumAtomicCounters ( ) const { return reflection - > getNumAtomicCounters ( ) ; }
const TObjectReflection & TProgram : : getAtomicCounter ( int index ) const { return reflection - > getAtomicCounter ( index ) ; }
void TProgram : : dumpReflection ( ) { if ( reflection ! = nullptr ) reflection - > dump ( ) ; }
2021-10-28 21:26:53 +00:00
//
// I/O mapping implementation.
//
2021-10-31 17:19:26 +00:00
bool TProgram : : mapIO ( TIoMapResolver * pResolver , TIoMapper * pIoMapper )
2021-10-28 21:26:53 +00:00
{
2021-10-31 17:19:26 +00:00
if ( ! linked )
2021-10-28 21:26:53 +00:00
return false ;
2021-10-31 17:19:26 +00:00
TIoMapper * ioMapper = nullptr ;
TIoMapper defaultIOMapper ;
if ( pIoMapper = = nullptr )
ioMapper = & defaultIOMapper ;
else
ioMapper = pIoMapper ;
2021-10-28 21:26:53 +00:00
for ( int s = 0 ; s < EShLangCount ; + + s ) {
if ( intermediate [ s ] ) {
2021-10-31 17:19:26 +00:00
if ( ! ioMapper - > addStage ( ( EShLanguage ) s , * intermediate [ s ] , * infoSink , pResolver ) )
2021-10-28 21:26:53 +00:00
return false ;
}
}
2021-10-31 17:19:26 +00:00
return ioMapper - > doMap ( pResolver , * infoSink ) ;
2021-10-28 21:26:53 +00:00
}
2021-10-31 17:19:26 +00:00
# endif // !GLSLANG_WEB && !GLSLANG_ANGLE
2021-10-28 21:26:53 +00:00
} // end namespace glslang