2016-02-16 17:18:48 +00:00
# include "shadermanager.hpp"
# include <fstream>
2016-02-20 18:26:59 +00:00
# include <algorithm>
2017-05-06 21:05:13 +00:00
# include <sstream>
2022-04-26 17:54:24 +00:00
# include <regex>
2022-06-27 19:32:46 +00:00
# include <filesystem>
2022-08-03 18:50:39 +00:00
# include <set>
# include <unordered_map>
2022-08-03 22:14:53 +00:00
# include <chrono>
2016-02-17 14:04:05 +00:00
# include <osg/Program>
2018-08-14 15:42:41 +00:00
# include <components/debug/debuglog.hpp>
2020-01-09 11:57:05 +00:00
# include <components/misc/stringops.hpp>
2022-06-06 20:40:38 +00:00
# include <components/settings/settings.hpp>
2018-08-14 15:42:41 +00:00
2016-02-16 17:18:48 +00:00
namespace Shader
{
2021-03-02 07:30:54 +00:00
ShaderManager : : ShaderManager ( )
2022-08-03 17:12:45 +00:00
{
mHotReloadManager = std : : make_unique < HotReloadManager > ( ) ;
}
ShaderManager : : ~ ShaderManager ( )
2021-02-21 18:38:15 +00:00
{
}
2016-02-16 17:18:48 +00:00
void ShaderManager : : setShaderPath ( const std : : string & path )
{
mPath = path ;
}
2017-11-21 16:53:12 +00:00
bool addLineDirectivesAfterConditionalBlocks ( std : : string & source )
{
for ( size_t position = 0 ; position < source . length ( ) ; )
{
size_t foundPos = source . find ( " #endif " , position ) ;
foundPos = std : : min ( foundPos , source . find ( " #elif " , position ) ) ;
foundPos = std : : min ( foundPos , source . find ( " #else " , position ) ) ;
if ( foundPos = = std : : string : : npos )
break ;
foundPos = source . find_first_of ( " \n \r " , foundPos ) ;
foundPos = source . find_first_not_of ( " \n \r " , foundPos ) ;
2020-08-04 01:46:54 +00:00
if ( foundPos = = std : : string : : npos )
break ;
2017-11-21 16:53:12 +00:00
size_t lineDirectivePosition = source . rfind ( " #line " , foundPos ) ;
int lineNumber ;
if ( lineDirectivePosition ! = std : : string : : npos )
{
size_t lineNumberStart = lineDirectivePosition + std : : string ( " #line " ) . length ( ) ;
size_t lineNumberEnd = source . find_first_not_of ( " 0123456789 " , lineNumberStart ) ;
std : : string lineNumberString = source . substr ( lineNumberStart , lineNumberEnd - lineNumberStart ) ;
lineNumber = std : : stoi ( lineNumberString ) - 1 ;
}
else
{
lineDirectivePosition = 0 ;
lineNumber = 1 ;
}
lineNumber + = std : : count ( source . begin ( ) + lineDirectivePosition , source . begin ( ) + foundPos , ' \n ' ) ;
source . replace ( foundPos , 0 , " #line " + std : : to_string ( lineNumber ) + " \n " ) ;
position = foundPos ;
}
return true ;
}
2020-08-05 22:58:18 +00:00
// Recursively replaces include statements with the actual source of the included files.
// Adjusts #line statements accordingly and detects cyclic includes.
2022-08-04 07:09:26 +00:00
// cycleIncludeChecker is the set of files that include this file directly or indirectly, and is intentionally not a reference to allow automatic cleanup.
static bool parseIncludes ( const std : : filesystem : : path & shaderPath , std : : string & source , const std : : string & fileName , int & fileNumber , std : : set < std : : filesystem : : path > cycleIncludeChecker , std : : set < std : : filesystem : : path > & includedFiles )
2016-02-17 22:29:07 +00:00
{
2022-08-04 07:09:26 +00:00
includedFiles . insert ( shaderPath / fileName ) ;
2020-08-05 22:58:18 +00:00
// An include is cyclic if it is being included by itself
2022-08-04 07:09:26 +00:00
if ( cycleIncludeChecker . insert ( shaderPath / fileName ) . second = = false )
2020-08-05 22:58:18 +00:00
{
Log ( Debug : : Error ) < < " Shader " < < fileName < < " error: Detected cyclic #includes " ;
return false ;
}
2020-01-09 11:57:05 +00:00
Misc : : StringUtils : : replaceAll ( source , " \r \n " , " \n " ) ;
2016-02-20 18:26:59 +00:00
2016-02-17 22:29:07 +00:00
size_t foundPos = 0 ;
while ( ( foundPos = source . find ( " #include " ) ) ! = std : : string : : npos )
{
size_t start = source . find ( ' " ' , foundPos ) ;
2020-08-05 22:58:18 +00:00
if ( start = = std : : string : : npos | | start = = source . size ( ) - 1 )
2016-02-17 22:29:07 +00:00
{
2020-08-05 22:58:18 +00:00
Log ( Debug : : Error ) < < " Shader " < < fileName < < " error: Invalid #include " ;
2016-02-17 22:29:07 +00:00
return false ;
}
2020-08-05 22:58:18 +00:00
size_t end = source . find ( ' " ' , start + 1 ) ;
2016-02-17 22:29:07 +00:00
if ( end = = std : : string : : npos )
{
2020-08-05 22:58:18 +00:00
Log ( Debug : : Error ) < < " Shader " < < fileName < < " error: Invalid #include " ;
2016-02-17 22:29:07 +00:00
return false ;
}
2020-08-05 22:58:18 +00:00
std : : string includeFilename = source . substr ( start + 1 , end - ( start + 1 ) ) ;
2022-05-26 14:13:07 +00:00
std : : filesystem : : path includePath = shaderPath / includeFilename ;
2016-02-20 18:26:59 +00:00
2020-08-05 22:58:18 +00:00
// Determine the line number that will be used for the #line directive following the included source
2017-11-21 16:53:12 +00:00
size_t lineDirectivePosition = source . rfind ( " #line " , foundPos ) ;
int lineNumber ;
if ( lineDirectivePosition ! = std : : string : : npos )
{
size_t lineNumberStart = lineDirectivePosition + std : : string ( " #line " ) . length ( ) ;
size_t lineNumberEnd = source . find_first_not_of ( " 0123456789 " , lineNumberStart ) ;
std : : string lineNumberString = source . substr ( lineNumberStart , lineNumberEnd - lineNumberStart ) ;
lineNumber = std : : stoi ( lineNumberString ) - 1 ;
}
else
{
lineDirectivePosition = 0 ;
2020-10-03 12:22:34 +00:00
lineNumber = 0 ;
2017-11-21 16:53:12 +00:00
}
lineNumber + = std : : count ( source . begin ( ) + lineDirectivePosition , source . begin ( ) + foundPos , ' \n ' ) ;
2016-02-20 18:26:59 +00:00
2020-08-05 22:58:18 +00:00
// Include the file recursively
2022-05-26 14:13:07 +00:00
std : : ifstream includeFstream ;
2020-08-05 22:58:18 +00:00
includeFstream . open ( includePath ) ;
if ( includeFstream . fail ( ) )
{
Log ( Debug : : Error ) < < " Shader " < < fileName < < " error: Failed to open include " < < includePath . string ( ) ;
return false ;
}
int includedFileNumber = fileNumber + + ;
2016-02-17 22:29:07 +00:00
2020-08-05 22:58:18 +00:00
std : : stringstream buffer ;
buffer < < includeFstream . rdbuf ( ) ;
std : : string stringRepresentation = buffer . str ( ) ;
if ( ! addLineDirectivesAfterConditionalBlocks ( stringRepresentation )
2022-08-04 07:09:26 +00:00
| | ! parseIncludes ( shaderPath , stringRepresentation , includeFilename , fileNumber , cycleIncludeChecker , includedFiles ) )
2016-02-17 22:29:07 +00:00
{
2020-08-05 22:58:18 +00:00
Log ( Debug : : Error ) < < " In file included from " < < fileName < < " . " < < lineNumber ;
2016-02-17 22:29:07 +00:00
return false ;
}
2020-08-05 22:58:18 +00:00
std : : stringstream toInsert ;
toInsert < < " #line 0 " < < includedFileNumber < < " \n " < < stringRepresentation < < " \n #line " < < lineNumber < < " 0 \n " ;
source . replace ( foundPos , ( end - foundPos + 1 ) , toInsert . str ( ) ) ;
2016-02-17 22:29:07 +00:00
}
return true ;
}
2022-04-26 17:54:24 +00:00
bool parseForeachDirective ( std : : string & source , const std : : string & templateName , size_t foundPos )
{
size_t iterNameStart = foundPos + strlen ( " $foreach " ) + 1 ;
size_t iterNameEnd = source . find_first_of ( " \n \r ()[].;, " , iterNameStart ) ;
if ( iterNameEnd = = std : : string : : npos )
{
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Unexpected EOF " ;
return false ;
}
std : : string iteratorName = " $ " + source . substr ( iterNameStart , iterNameEnd - iterNameStart ) ;
size_t listStart = iterNameEnd + 1 ;
size_t listEnd = source . find_first_of ( " \n \r " , listStart ) ;
if ( listEnd = = std : : string : : npos )
{
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Unexpected EOF " ;
return false ;
}
std : : string list = source . substr ( listStart , listEnd - listStart ) ;
std : : vector < std : : string > listElements ;
if ( list ! = " " )
Misc : : StringUtils : : split ( list , listElements , " , " ) ;
size_t contentStart = source . find_first_not_of ( " \n \r " , listEnd ) ;
size_t contentEnd = source . find ( " $endforeach " , contentStart ) ;
if ( contentEnd = = std : : string : : npos )
{
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Unexpected EOF " ;
return false ;
}
std : : string content = source . substr ( contentStart , contentEnd - contentStart ) ;
size_t overallEnd = contentEnd + std : : string ( " $endforeach " ) . length ( ) ;
size_t lineDirectivePosition = source . rfind ( " #line " , overallEnd ) ;
int lineNumber ;
if ( lineDirectivePosition ! = std : : string : : npos )
{
size_t lineNumberStart = lineDirectivePosition + std : : string ( " #line " ) . length ( ) ;
size_t lineNumberEnd = source . find_first_not_of ( " 0123456789 " , lineNumberStart ) ;
std : : string lineNumberString = source . substr ( lineNumberStart , lineNumberEnd - lineNumberStart ) ;
lineNumber = std : : stoi ( lineNumberString ) ;
}
else
{
lineDirectivePosition = 0 ;
lineNumber = 2 ;
}
lineNumber + = std : : count ( source . begin ( ) + lineDirectivePosition , source . begin ( ) + overallEnd , ' \n ' ) ;
2022-05-04 20:33:39 +00:00
std : : string replacement ;
2022-04-26 17:54:24 +00:00
for ( std : : vector < std : : string > : : const_iterator element = listElements . cbegin ( ) ; element ! = listElements . cend ( ) ; element + + )
{
std : : string contentInstance = content ;
size_t foundIterator ;
while ( ( foundIterator = contentInstance . find ( iteratorName ) ) ! = std : : string : : npos )
contentInstance . replace ( foundIterator , iteratorName . length ( ) , * element ) ;
replacement + = contentInstance ;
}
replacement + = " \n #line " + std : : to_string ( lineNumber ) ;
source . replace ( foundPos , overallEnd - foundPos , replacement ) ;
return true ;
}
bool parseLinkDirective ( std : : string & source , std : : string & linkTarget , const std : : string & templateName , size_t foundPos )
{
size_t endPos = foundPos + 5 ;
size_t lineEnd = source . find_first_of ( ' \n ' , endPos ) ;
// If lineEnd = npos, this is the last line, so no need to check
std : : string linkStatement = source . substr ( endPos , lineEnd - endPos ) ;
std : : regex linkRegex (
R " r( \ s* " ( [ ^ " ]+) " \ s * ) r " // Find any quoted string as the link name -> match[1]
R " r((if \ s+)r " // Begin optional condition -> match[2]
R " r((!)? \ s*)r " // Optional ! -> match[3]
R " r(([_a-zA-Z0-9]+)?)r " // The condition -> match[4]
R " r()? \ s*)r " // End optional condition -> match[2]
) ;
std : : smatch linkMatch ;
bool hasCondition = false ;
std : : string linkConditionExpression ;
if ( std : : regex_match ( linkStatement , linkMatch , linkRegex ) )
{
linkTarget = linkMatch [ 1 ] . str ( ) ;
hasCondition = ! linkMatch [ 2 ] . str ( ) . empty ( ) ;
linkConditionExpression = linkMatch [ 4 ] . str ( ) ;
}
else
{
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Expected a shader filename to link " ;
return false ;
}
if ( linkTarget . empty ( ) )
{
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Empty link name " ;
return false ;
}
if ( hasCondition )
{
bool condition = ! ( linkConditionExpression . empty ( ) | | linkConditionExpression = = " 0 " ) ;
if ( linkMatch [ 3 ] . str ( ) = = " ! " )
condition = ! condition ;
if ( ! condition )
2022-05-04 20:33:39 +00:00
linkTarget . clear ( ) ;
2022-04-26 17:54:24 +00:00
}
source . replace ( foundPos , lineEnd - foundPos , " " ) ;
return true ;
}
bool parseDirectives ( std : : string & source , std : : vector < std : : string > & linkedShaderTemplateNames , const ShaderManager : : DefineMap & defines , const ShaderManager : : DefineMap & globalDefines , const std : : string & templateName )
2017-11-10 02:02:27 +00:00
{
const char escapeCharacter = ' $ ' ;
size_t foundPos = 0 ;
2022-04-26 17:54:24 +00:00
while ( ( foundPos = source . find ( escapeCharacter , foundPos ) ) ! = std : : string : : npos )
2017-11-10 02:02:27 +00:00
{
size_t endPos = source . find_first_of ( " \n \r ()[].;, " , foundPos ) ;
if ( endPos = = std : : string : : npos )
{
2020-05-01 17:11:04 +00:00
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Unexpected EOF " ;
2017-11-10 02:02:27 +00:00
return false ;
}
2022-04-26 17:54:24 +00:00
std : : string directive = source . substr ( foundPos + 1 , endPos - ( foundPos + 1 ) ) ;
if ( directive = = " foreach " )
2017-11-10 02:02:27 +00:00
{
2022-04-26 17:54:24 +00:00
if ( ! parseForeachDirective ( source , templateName , foundPos ) )
return false ;
2017-11-10 02:02:27 +00:00
}
2022-04-26 17:54:24 +00:00
else if ( directive = = " link " )
2017-11-10 02:02:27 +00:00
{
2022-04-26 17:54:24 +00:00
std : : string linkTarget ;
if ( ! parseLinkDirective ( source , linkTarget , templateName , foundPos ) )
return false ;
if ( ! linkTarget . empty ( ) & & linkTarget ! = templateName )
linkedShaderTemplateNames . push_back ( linkTarget ) ;
2017-11-17 17:18:33 +00:00
}
else
{
2022-04-26 17:54:24 +00:00
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Unknown shader directive: $ " < < directive ;
return false ;
2017-11-10 02:02:27 +00:00
}
}
return true ;
}
2020-04-23 17:16:32 +00:00
bool parseDefines ( std : : string & source , const ShaderManager : : DefineMap & defines ,
2020-05-01 17:11:04 +00:00
const ShaderManager : : DefineMap & globalDefines , const std : : string & templateName )
2016-02-17 22:29:07 +00:00
{
const char escapeCharacter = ' @ ' ;
size_t foundPos = 0 ;
2017-11-10 02:02:27 +00:00
std : : vector < std : : string > forIterators ;
2016-02-17 22:29:07 +00:00
while ( ( foundPos = source . find ( escapeCharacter ) ) ! = std : : string : : npos )
{
2017-11-10 02:02:27 +00:00
size_t endPos = source . find_first_of ( " \n \r ()[].;, " , foundPos ) ;
2016-02-17 22:29:07 +00:00
if ( endPos = = std : : string : : npos )
{
2020-05-01 17:11:04 +00:00
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Unexpected EOF " ;
2016-02-17 22:29:07 +00:00
return false ;
}
std : : string define = source . substr ( foundPos + 1 , endPos - ( foundPos + 1 ) ) ;
ShaderManager : : DefineMap : : const_iterator defineFound = defines . find ( define ) ;
2017-11-22 20:07:07 +00:00
ShaderManager : : DefineMap : : const_iterator globalDefineFound = globalDefines . find ( define ) ;
2017-11-10 02:02:27 +00:00
if ( define = = " foreach " )
{
source . replace ( foundPos , 1 , " $ " ) ;
size_t iterNameStart = endPos + 1 ;
size_t iterNameEnd = source . find_first_of ( " \n \r ()[].;, " , iterNameStart ) ;
if ( iterNameEnd = = std : : string : : npos )
{
2020-05-01 17:11:04 +00:00
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Unexpected EOF " ;
2017-11-10 02:02:27 +00:00
return false ;
}
forIterators . push_back ( source . substr ( iterNameStart , iterNameEnd - iterNameStart ) ) ;
}
else if ( define = = " endforeach " )
{
source . replace ( foundPos , 1 , " $ " ) ;
if ( forIterators . empty ( ) )
{
2020-05-01 17:11:04 +00:00
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: endforeach without foreach " ;
2017-11-10 02:02:27 +00:00
return false ;
}
else
forIterators . pop_back ( ) ;
}
2022-04-26 17:54:24 +00:00
else if ( define = = " link " )
{
source . replace ( foundPos , 1 , " $ " ) ;
}
2017-11-10 02:02:27 +00:00
else if ( std : : find ( forIterators . begin ( ) , forIterators . end ( ) , define ) ! = forIterators . end ( ) )
{
source . replace ( foundPos , 1 , " $ " ) ;
}
2017-11-22 20:07:07 +00:00
else if ( defineFound ! = defines . end ( ) )
2016-02-17 22:29:07 +00:00
{
2017-11-22 20:07:07 +00:00
source . replace ( foundPos , endPos - foundPos , defineFound - > second ) ;
}
else if ( globalDefineFound ! = globalDefines . end ( ) )
{
source . replace ( foundPos , endPos - foundPos , globalDefineFound - > second ) ;
2016-02-17 22:29:07 +00:00
}
else
{
2020-05-01 17:11:04 +00:00
Log ( Debug : : Error ) < < " Shader " < < templateName < < " error: Undefined " < < define ;
2017-11-22 20:07:07 +00:00
return false ;
2016-02-17 22:29:07 +00:00
}
}
return true ;
}
2022-08-03 17:12:45 +00:00
struct HotReloadManager
{
std : : filesystem : : file_time_type mLastAutoRecompileTime ;
using KeysHolder = std : : set < ShaderManager : : MapKey > ;
std : : unordered_map < std : : string , KeysHolder > mShaderFiles ;
2022-08-03 17:56:19 +00:00
bool mHotReloadEnabled ;
2022-08-03 17:12:45 +00:00
HotReloadManager ( )
{
2022-08-03 17:56:19 +00:00
mHotReloadEnabled = false ;
2022-08-03 17:12:45 +00:00
mLastAutoRecompileTime = std : : chrono : : file_clock : : now ( ) ;
}
void addShaderFiles ( std : : set < std : : filesystem : : path > & shaderFiles , const osg : : ref_ptr < osg : : Shader > & shader , const std : : string & templateName , const ShaderManager : : DefineMap & defines )
{
for ( const std : : filesystem : : path & file : shaderFiles )
{
KeysHolder * shaderSet_ptr ;
auto found = mShaderFiles . find ( file . string ( ) ) ;
if ( found ! = mShaderFiles . end ( ) ) //Apparently there is an issue that prevents me from using operator[]
{
shaderSet_ptr = & found - > second ;
}
else
{
shaderSet_ptr = & mShaderFiles . insert ( std : : make_pair < > ( file . string ( ) , KeysHolder ( ) ) ) . first - > second ;
}
auto & shaderSet = * shaderSet_ptr ;
shaderSet . insert ( std : : make_pair ( templateName , defines ) ) ;
}
}
void update ( ShaderManager & Manager )
2022-08-03 17:56:19 +00:00
{
if ( mHotReloadEnabled )
reloadTouchedShaders ( Manager ) ;
}
void reloadTouchedShaders ( ShaderManager & Manager )
2022-08-03 17:12:45 +00:00
{
for ( auto & shader : mShaderFiles )
{
std : : filesystem : : path pathShaderToTest = ( shader . first ) ;
std : : filesystem : : file_time_type write_time = std : : filesystem : : last_write_time ( pathShaderToTest ) ;
if ( write_time . time_since_epoch ( ) > mLastAutoRecompileTime . time_since_epoch ( ) )
{
for ( const ShaderManager : : MapKey & descriptor : shader . second )
{
const std : : string & templateName = descriptor . first ;
ShaderManager : : ShaderMap : : iterator shaderIt = Manager . mShaders . find ( std : : make_pair ( templateName , descriptor . second ) ) ;
ShaderManager : : TemplateMap : : iterator templateIt = Manager . mShaderTemplates . find ( templateName ) ; //Can't be Null, if we're here it means the template was added
std : : set < std : : filesystem : : path > insertedPaths ;
2022-08-03 18:50:39 +00:00
std : : filesystem : : path path = ( std : : filesystem : : path ( Manager . mPath ) / templateName ) ;
std : : ifstream stream ;
stream . open ( path ) ;
if ( stream . fail ( ) )
2022-08-03 17:12:45 +00:00
{
2022-08-03 18:50:39 +00:00
Log ( Debug : : Error ) < < " Failed to open " < < path . string ( ) ;
2022-08-03 17:12:45 +00:00
}
2022-08-03 18:50:39 +00:00
std : : stringstream buffer ;
buffer < < stream . rdbuf ( ) ;
// parse includes
int fileNumber = 1 ;
std : : string source = buffer . str ( ) ;
if ( ! addLineDirectivesAfterConditionalBlocks ( source )
2022-08-04 07:09:26 +00:00
| | ! parseIncludes ( std : : filesystem : : path ( Manager . mPath ) , source , templateName , fileNumber , { } , insertedPaths ) )
2022-08-03 18:50:39 +00:00
{
break ;
}
templateIt - > second = source ;
std : : string shaderSource = templateIt - > second ;
std : : vector < std : : string > linkedShaderNames ;
if ( ! Manager . createSourceFromTemplate ( shaderSource , linkedShaderNames , templateName , descriptor . second ) )
{
break ;
}
shaderIt - > second - > setShaderSource ( shaderSource ) ;
2022-08-03 17:12:45 +00:00
}
}
}
mLastAutoRecompileTime = std : : chrono : : file_clock : : now ( ) ;
}
} ;
2020-05-01 17:11:04 +00:00
osg : : ref_ptr < osg : : Shader > ShaderManager : : getShader ( const std : : string & templateName , const ShaderManager : : DefineMap & defines , osg : : Shader : : Type shaderType )
2016-02-16 17:18:48 +00:00
{
2022-04-26 17:54:24 +00:00
std : : unique_lock < std : : mutex > lock ( mMutex ) ;
2016-02-16 17:18:48 +00:00
// read the template if we haven't already
2020-05-01 17:11:04 +00:00
TemplateMap : : iterator templateIt = mShaderTemplates . find ( templateName ) ;
2022-08-03 17:12:45 +00:00
std : : set < std : : filesystem : : path > insertedPaths ;
2016-02-16 17:18:48 +00:00
if ( templateIt = = mShaderTemplates . end ( ) )
{
2022-05-26 14:13:07 +00:00
std : : filesystem : : path path = ( std : : filesystem : : path ( mPath ) / templateName ) ;
std : : ifstream stream ;
2020-08-05 22:58:18 +00:00
stream . open ( path ) ;
2016-02-16 17:18:48 +00:00
if ( stream . fail ( ) )
{
2020-08-05 22:58:18 +00:00
Log ( Debug : : Error ) < < " Failed to open " < < path . string ( ) ;
2018-10-09 06:21:12 +00:00
return nullptr ;
2016-02-16 17:18:48 +00:00
}
std : : stringstream buffer ;
buffer < < stream . rdbuf ( ) ;
2016-02-17 22:29:07 +00:00
// parse includes
2020-08-05 22:58:18 +00:00
int fileNumber = 1 ;
2016-02-17 22:29:07 +00:00
std : : string source = buffer . str ( ) ;
2020-04-23 17:16:32 +00:00
if ( ! addLineDirectivesAfterConditionalBlocks ( source )
2022-08-04 07:09:26 +00:00
| | ! parseIncludes ( std : : filesystem : : path ( mPath ) , source , templateName , fileNumber , { } , insertedPaths ) )
2018-10-09 06:21:12 +00:00
return nullptr ;
2016-02-17 22:29:07 +00:00
2020-05-01 17:11:04 +00:00
templateIt = mShaderTemplates . insert ( std : : make_pair ( templateName , source ) ) . first ;
2016-02-16 17:18:48 +00:00
}
2020-05-01 17:11:04 +00:00
ShaderMap : : iterator shaderIt = mShaders . find ( std : : make_pair ( templateName , defines ) ) ;
2016-02-16 17:18:48 +00:00
if ( shaderIt = = mShaders . end ( ) )
{
std : : string shaderSource = templateIt - > second ;
2022-04-26 17:54:24 +00:00
std : : vector < std : : string > linkedShaderNames ;
if ( ! createSourceFromTemplate ( shaderSource , linkedShaderNames , templateName , defines ) )
2017-11-07 22:54:31 +00:00
{
// Add to the cache anyway to avoid logging the same error over and over.
2020-05-01 17:11:04 +00:00
mShaders . insert ( std : : make_pair ( std : : make_pair ( templateName , defines ) , nullptr ) ) ;
2018-10-09 06:21:12 +00:00
return nullptr ;
2017-11-07 22:54:31 +00:00
}
2016-02-16 17:18:48 +00:00
osg : : ref_ptr < osg : : Shader > shader ( new osg : : Shader ( shaderType ) ) ;
shader - > setShaderSource ( shaderSource ) ;
2021-06-06 01:21:03 +00:00
// Assign a unique prefix to allow the SharedStateManager to compare shaders efficiently.
// Append shader source filename for debugging.
2016-02-16 17:18:48 +00:00
static unsigned int counter = 0 ;
2021-06-06 01:21:03 +00:00
shader - > setName ( Misc : : StringUtils : : format ( " %u %s " , counter + + , templateName ) ) ;
2022-08-03 17:12:45 +00:00
mHotReloadManager - > addShaderFiles ( insertedPaths , shader , templateName , defines ) ;
2016-02-16 17:18:48 +00:00
2022-04-26 17:54:24 +00:00
lock . unlock ( ) ;
getLinkedShaders ( shader , linkedShaderNames , defines ) ;
lock . lock ( ) ;
2020-05-01 17:11:04 +00:00
shaderIt = mShaders . insert ( std : : make_pair ( std : : make_pair ( templateName , defines ) , shader ) ) . first ;
2016-02-16 17:18:48 +00:00
}
return shaderIt - > second ;
}
2021-09-29 13:40:37 +00:00
osg : : ref_ptr < osg : : Program > ShaderManager : : getProgram ( osg : : ref_ptr < osg : : Shader > vertexShader , osg : : ref_ptr < osg : : Shader > fragmentShader , const osg : : Program * programTemplate )
2016-02-17 14:04:05 +00:00
{
2020-06-25 19:46:07 +00:00
std : : lock_guard < std : : mutex > lock ( mMutex ) ;
2016-02-17 14:04:05 +00:00
ProgramMap : : iterator found = mPrograms . find ( std : : make_pair ( vertexShader , fragmentShader ) ) ;
if ( found = = mPrograms . end ( ) )
{
2021-09-29 13:40:37 +00:00
if ( ! programTemplate ) programTemplate = mProgramTemplate ;
2021-11-07 17:26:02 +00:00
osg : : ref_ptr < osg : : Program > program = programTemplate ? cloneProgram ( programTemplate ) : osg : : ref_ptr < osg : : Program > ( new osg : : Program ) ;
2016-02-17 14:04:05 +00:00
program - > addShader ( vertexShader ) ;
program - > addShader ( fragmentShader ) ;
2022-04-26 17:54:24 +00:00
addLinkedShaders ( vertexShader , program ) ;
addLinkedShaders ( fragmentShader , program ) ;
2016-02-17 14:04:05 +00:00
found = mPrograms . insert ( std : : make_pair ( std : : make_pair ( vertexShader , fragmentShader ) , program ) ) . first ;
}
return found - > second ;
}
2021-11-07 17:26:02 +00:00
osg : : ref_ptr < osg : : Program > ShaderManager : : cloneProgram ( const osg : : Program * src )
{
osg : : ref_ptr < osg : : Program > program = static_cast < osg : : Program * > ( src - > clone ( osg : : CopyOp : : SHALLOW_COPY ) ) ;
for ( auto [ name , idx ] : src - > getUniformBlockBindingList ( ) )
program - > addBindUniformBlock ( name , idx ) ;
return program ;
}
2017-11-22 20:07:07 +00:00
ShaderManager : : DefineMap ShaderManager : : getGlobalDefines ( )
{
return DefineMap ( mGlobalDefines ) ;
}
2017-12-17 23:55:19 +00:00
void ShaderManager : : setGlobalDefines ( DefineMap & globalDefines )
2017-11-22 20:07:07 +00:00
{
2017-12-17 23:55:19 +00:00
mGlobalDefines = globalDefines ;
2021-08-29 13:15:45 +00:00
for ( const auto & [ key , shader ] : mShaders )
2017-12-17 01:57:53 +00:00
{
2021-08-29 13:15:45 +00:00
std : : string templateId = key . first ;
ShaderManager : : DefineMap defines = key . second ;
2017-12-17 01:57:53 +00:00
if ( shader = = nullptr )
// I'm not sure how to handle a shader that was already broken as there's no way to get a potential replacement to the nodes that need it.
continue ;
std : : string shaderSource = mShaderTemplates [ templateId ] ;
2022-04-26 17:54:24 +00:00
std : : vector < std : : string > linkedShaderNames ;
if ( ! createSourceFromTemplate ( shaderSource , linkedShaderNames , templateId , defines ) )
2017-12-17 01:57:53 +00:00
// We just broke the shader and there's no way to force existing objects back to fixed-function mode as we would when creating the shader.
// If we put a nullptr in the shader map, we just lose the ability to put a working one in later.
continue ;
shader - > setShaderSource ( shaderSource ) ;
2022-04-26 17:54:24 +00:00
getLinkedShaders ( shader , linkedShaderNames , defines ) ;
2017-12-17 01:57:53 +00:00
}
2017-11-22 20:07:07 +00:00
}
2017-08-26 19:28:23 +00:00
void ShaderManager : : releaseGLObjects ( osg : : State * state )
{
2020-06-25 19:46:07 +00:00
std : : lock_guard < std : : mutex > lock ( mMutex ) ;
2021-08-29 13:15:45 +00:00
for ( const auto & [ _ , shader ] : mShaders )
2017-11-24 22:43:40 +00:00
{
2021-08-29 13:15:45 +00:00
if ( shader ! = nullptr )
shader - > releaseGLObjects ( state ) ;
2017-11-24 22:43:40 +00:00
}
2021-08-29 13:15:45 +00:00
for ( const auto & [ _ , program ] : mPrograms )
program - > releaseGLObjects ( state ) ;
2017-08-26 19:28:23 +00:00
}
2022-04-26 17:54:24 +00:00
bool ShaderManager : : createSourceFromTemplate ( std : : string & source , std : : vector < std : : string > & linkedShaderTemplateNames , const std : : string & templateName , const ShaderManager : : DefineMap & defines )
{
if ( ! parseDefines ( source , defines , mGlobalDefines , templateName ) )
return false ;
if ( ! parseDirectives ( source , linkedShaderTemplateNames , defines , mGlobalDefines , templateName ) )
return false ;
return true ;
}
void ShaderManager : : getLinkedShaders ( osg : : ref_ptr < osg : : Shader > shader , const std : : vector < std : : string > & linkedShaderNames , const DefineMap & defines )
{
mLinkedShaders . erase ( shader ) ;
if ( linkedShaderNames . empty ( ) )
return ;
for ( auto & linkedShaderName : linkedShaderNames )
{
auto linkedShader = getShader ( linkedShaderName , defines , shader - > getType ( ) ) ;
if ( linkedShader )
mLinkedShaders [ shader ] . emplace_back ( linkedShader ) ;
}
}
void ShaderManager : : addLinkedShaders ( osg : : ref_ptr < osg : : Shader > shader , osg : : ref_ptr < osg : : Program > program )
{
auto linkedIt = mLinkedShaders . find ( shader ) ;
if ( linkedIt ! = mLinkedShaders . end ( ) )
for ( const auto & linkedShader : linkedIt - > second )
program - > addShader ( linkedShader ) ;
}
2022-06-30 01:15:12 +00:00
int ShaderManager : : reserveGlobalTextureUnits ( Slot slot )
2022-06-06 20:40:38 +00:00
{
2022-06-30 01:15:12 +00:00
int unit = mReservedTextureUnitsBySlot [ static_cast < int > ( slot ) ] ;
if ( unit > = 0 )
return unit ;
2022-06-06 20:40:38 +00:00
{
// Texture units from `8 - numberOfShadowMaps` to `8` are used for shadows, so we skip them here.
// TODO: Maybe instead of fixed texture units use `reserveGlobalTextureUnits` for shadows as well.
static const int numberOfShadowMaps = Settings : : Manager : : getBool ( " enable shadows " , " Shadows " ) ?
std : : clamp ( Settings : : Manager : : getInt ( " number of shadow maps " , " Shadows " ) , 1 , 8 ) :
0 ;
2022-06-30 01:15:12 +00:00
if ( getAvailableTextureUnits ( ) > = 8 & & getAvailableTextureUnits ( ) - 1 < 8 )
2022-06-06 20:40:38 +00:00
mReservedTextureUnits = mMaxTextureUnits - ( 8 - numberOfShadowMaps ) ;
}
2022-06-30 01:15:12 +00:00
if ( getAvailableTextureUnits ( ) < 2 )
2022-06-06 20:40:38 +00:00
throw std : : runtime_error ( " Can't reserve texture unit; no available units " ) ;
2022-06-30 01:15:12 +00:00
mReservedTextureUnits + + ;
unit = mMaxTextureUnits - mReservedTextureUnits ;
mReservedTextureUnitsBySlot [ static_cast < int > ( slot ) ] = unit ;
return unit ;
2022-06-06 20:40:38 +00:00
}
2022-08-03 17:12:45 +00:00
void ShaderManager : : update ( )
{
mHotReloadManager - > update ( * this ) ;
}
2022-08-03 17:56:19 +00:00
void ShaderManager : : setHotReloadEnabled ( bool value )
{
mHotReloadManager - > mHotReloadEnabled = value ;
}
void ShaderManager : : triggerShaderReload ( )
{
mHotReloadManager - > reloadTouchedShaders ( * this ) ;
}
2016-02-16 17:18:48 +00:00
}