1 #include "pch.h"
2 #include "ShaderSet.hpp"
3 
4 #include <fstream>
5 #include <sstream>
6 
7 #include <boost/algorithm/string/predicate.hpp>
8 #include <boost/functional/hash.hpp>
9 #include <boost/lexical_cast.hpp>
10 #include <boost/filesystem.hpp>
11 
12 #include "Factory.hpp"
13 
14 namespace sh
15 {
ShaderSet(const std::string & type,const std::string & cgProfile,const std::string & hlslProfile,const std::string & sourceFile,const std::string & basePath,const std::string & name,PropertySetGet * globalSettingsPtr)16 	ShaderSet::ShaderSet (const std::string& type, const std::string& cgProfile, const std::string& hlslProfile, const std::string& sourceFile, const std::string& basePath,
17 						  const std::string& name, PropertySetGet* globalSettingsPtr)
18 		: mBasePath(basePath)
19 		, mName(name)
20 		, mCgProfile(cgProfile)
21 		, mHlslProfile(hlslProfile)
22 	{
23 		if (type == "vertex")
24 			mType = GPT_Vertex;
25 		else // if (type == "fragment")
26 			mType = GPT_Fragment;
27 
28 		std::ifstream stream(sourceFile.c_str(), std::ifstream::in);
29 		std::stringstream buffer;
30 
31 		boost::filesystem::path p (sourceFile);
32 		p = p.branch_path();
33 		mBasePath = p.string();
34 
35 		buffer << stream.rdbuf();
36 		stream.close();
37 		mSource = buffer.str();
38 		parse();
39 	}
40 
~ShaderSet()41 	ShaderSet::~ShaderSet()
42 	{
43 		for (ShaderInstanceMap::iterator it = mInstances.begin(); it != mInstances.end(); ++it)
44 		{
45 			sh::Factory::getInstance().getPlatform()->destroyGpuProgram(it->second.getName());
46 		}
47 	}
48 
parse()49 	void ShaderSet::parse()
50 	{
51 		std::string currentToken;
52 		bool tokenIsRecognized = false;
53 		bool isInBraces = false;
54 		for (std::string::const_iterator it = mSource.begin(); it != mSource.end(); ++it)
55 		{
56 			char c = *it;
57 			if (((c == ' ') && !isInBraces) || (c == '\n') ||
58 					(   ((c == '(') || (c == ')'))
59 						  && !tokenIsRecognized))
60 			{
61 				if (tokenIsRecognized)
62 				{
63 					if (boost::starts_with(currentToken, "@shGlobalSetting"))
64 					{
65 						assert ((currentToken.find('(') != std::string::npos) && (currentToken.find(')') != std::string::npos));
66 						size_t start = currentToken.find('(')+1;
67 						mGlobalSettings.push_back(currentToken.substr(start, currentToken.find(')')-start));
68 					}
69 					else if (boost::starts_with(currentToken, "@shPropertyHasValue"))
70 					{
71 						assert ((currentToken.find('(') != std::string::npos) && (currentToken.find(')') != std::string::npos));
72 						size_t start = currentToken.find('(')+1;
73 						mPropertiesToExist.push_back(currentToken.substr(start, currentToken.find(')')-start));
74 					}
75 					else if (boost::starts_with(currentToken, "@shPropertyEqual"))
76 					{
77 						assert ((currentToken.find('(') != std::string::npos) && (currentToken.find(')') != std::string::npos)
78 								&& (currentToken.find(',') != std::string::npos));
79 						size_t start = currentToken.find('(')+1;
80 						size_t end = currentToken.find(',');
81 						mProperties.push_back(currentToken.substr(start, end-start));
82 					}
83 					else if (boost::starts_with(currentToken, "@shProperty"))
84 					{
85 						assert ((currentToken.find('(') != std::string::npos) && (currentToken.find(')') != std::string::npos));
86 						size_t start = currentToken.find('(')+1;
87 						std::string propertyName = currentToken.substr(start, currentToken.find(')')-start);
88 						// if the property name is constructed dynamically (e.g. through an iterator) then there is nothing we can do
89 						if (propertyName.find("@") == std::string::npos)
90 							mProperties.push_back(propertyName);
91 					}
92 				}
93 
94 				currentToken = "";
95 			}
96 			else
97 			{
98 				if (currentToken == "")
99 				{
100 					if (c == '@')
101 						tokenIsRecognized = true;
102 					else
103 						tokenIsRecognized = false;
104 				}
105 				else
106 				{
107 					if (c == '@')
108 					{
109 						// ouch, there are nested macros
110 						// ( for example @shForeach(@shPropertyString(foobar)) )
111 						currentToken = "";
112 					}
113 				}
114 
115 				if (c == '(' && tokenIsRecognized)
116 					isInBraces = true;
117 				else if (c == ')' && tokenIsRecognized)
118 					isInBraces = false;
119 
120 				currentToken += c;
121 
122 			}
123 		}
124 	}
125 
getInstance(PropertySetGet * properties)126 	ShaderInstance* ShaderSet::getInstance (PropertySetGet* properties)
127 	{
128 		size_t h = buildHash (properties);
129 		if (std::find(mFailedToCompile.begin(), mFailedToCompile.end(), h) != mFailedToCompile.end())
130 			return NULL;
131 		if (mInstances.find(h) == mInstances.end())
132 		{
133 			ShaderInstance newInstance(this, mName + "_" + boost::lexical_cast<std::string>(h), properties);
134 			if (!newInstance.getSupported())
135 			{
136 				mFailedToCompile.push_back(h);
137 				return NULL;
138 			}
139 			mInstances.insert(std::make_pair(h, newInstance));
140 		}
141 		return &mInstances.find(h)->second;
142 	}
143 
buildHash(PropertySetGet * properties)144 	size_t ShaderSet::buildHash (PropertySetGet* properties)
145 	{
146 		size_t seed = 0;
147 		PropertySetGet* currentGlobalSettings = getCurrentGlobalSettings ();
148 
149 		for (std::vector<std::string>::iterator it = mProperties.begin(); it != mProperties.end(); ++it)
150 		{
151 			std::string v = retrieveValue<StringValue>(properties->getProperty(*it), properties->getContext()).get();
152 			boost::hash_combine(seed, v);
153 		}
154 		for (std::vector <std::string>::iterator it = mGlobalSettings.begin(); it != mGlobalSettings.end(); ++it)
155 		{
156 			boost::hash_combine(seed, retrieveValue<StringValue>(currentGlobalSettings->getProperty(*it), NULL).get());
157 		}
158 		for (std::vector<std::string>::iterator it = mPropertiesToExist.begin(); it != mPropertiesToExist.end(); ++it)
159 		{
160 			std::string v = retrieveValue<StringValue>(properties->getProperty(*it), properties->getContext()).get();
161 			boost::hash_combine(seed, static_cast<bool>(v != ""));
162 		}
163 		boost::hash_combine(seed, static_cast<int>(Factory::getInstance().getCurrentLanguage()));
164 		return seed;
165 	}
166 
getCurrentGlobalSettings() const167 	PropertySetGet* ShaderSet::getCurrentGlobalSettings() const
168 	{
169 		return Factory::getInstance ().getCurrentGlobalSettings ();
170 	}
171 
getBasePath() const172 	std::string ShaderSet::getBasePath() const
173 	{
174 		return mBasePath;
175 	}
176 
getSource() const177 	std::string ShaderSet::getSource() const
178 	{
179 		return mSource;
180 	}
181 
getCgProfile() const182 	std::string ShaderSet::getCgProfile() const
183 	{
184 		return mCgProfile;
185 	}
186 
getHlslProfile() const187 	std::string ShaderSet::getHlslProfile() const
188 	{
189 		return mHlslProfile;
190 	}
191 
getType() const192 	int ShaderSet::getType() const
193 	{
194 		return mType;
195 	}
196 }
197