1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #include "BsSLFXCompiler.h" |
4 | #include "RenderAPI/BsGpuProgram.h" |
5 | #include <regex> |
6 | #include "Material/BsShader.h" |
7 | #include "Material/BsTechnique.h" |
8 | #include "Material/BsPass.h" |
9 | #include "RenderAPI/BsSamplerState.h" |
10 | #include "RenderAPI/BsRenderAPI.h" |
11 | #include "Debug/BsDebug.h" |
12 | #include "Material/BsShaderManager.h" |
13 | #include "Material/BsShaderInclude.h" |
14 | #include "Math/BsMatrix4.h" |
15 | #include "Resources/BsBuiltinResources.h" |
16 | #include "Material/BsShaderVariation.h" |
17 | #include "Renderer/BsRenderer.h" |
18 | #include "Renderer/BsRendererManager.h" |
19 | #include "FileSystem/BsFileSystem.h" |
20 | #include "FileSystem/BsDataStream.h" |
21 | |
22 | #define XSC_ENABLE_LANGUAGE_EXT 1 |
23 | #include "Xsc/Xsc.h" |
24 | |
25 | extern "C" { |
26 | #include "BsMMAlloc.h" |
27 | |
28 | #define YY_NO_UNISTD_H 1 |
29 | #include "BsParserFX.h" |
30 | #include "BsLexerFX.h" |
31 | } |
32 | |
33 | using namespace std; |
34 | |
35 | namespace bs |
36 | { |
37 | // Print out the FX AST, only for debug purposes |
38 | void SLFXDebugPrint(ASTFXNode* node, String indent) |
39 | { |
40 | LOGDBG(indent + "NODE " + toString(node->type)); |
41 | |
42 | for (int i = 0; i < node->options->count; i++) |
43 | { |
44 | OptionDataType odt = OPTION_LOOKUP[(int)node->options->entries[i].type].dataType; |
45 | if (odt == ODT_Complex) |
46 | { |
47 | LOGDBG(indent + toString(i) + ". " + toString(node->options->entries[i].type)); |
48 | SLFXDebugPrint(node->options->entries[i].value.nodePtr, indent + "\t" ); |
49 | continue; |
50 | } |
51 | |
52 | String value; |
53 | switch (odt) |
54 | { |
55 | case ODT_Bool: |
56 | value = toString(node->options->entries[i].value.intValue != 0); |
57 | break; |
58 | case ODT_Int: |
59 | value = toString(node->options->entries[i].value.intValue); |
60 | break; |
61 | case ODT_Float: |
62 | value = toString(node->options->entries[i].value.floatValue); |
63 | break; |
64 | case ODT_String: |
65 | value = node->options->entries[i].value.strValue; |
66 | break; |
67 | case ODT_Matrix: |
68 | { |
69 | Matrix4 mat4 = *(Matrix4*)(node->options->entries[i].value.matrixValue); |
70 | value = toString(mat4); |
71 | } |
72 | break; |
73 | default: |
74 | break; |
75 | } |
76 | |
77 | LOGDBG(indent + toString(i) + ". " + toString(node->options->entries[i].type) + " = " + value); |
78 | } |
79 | } |
80 | |
81 | class XscLog : public Xsc::Log |
82 | { |
83 | public: |
84 | void SubmitReport(const Xsc::Report& report) override |
85 | { |
86 | switch (report.Type()) |
87 | { |
88 | case Xsc::ReportTypes::Info: |
89 | mInfos.push_back({ FullIndent(), report }); |
90 | break; |
91 | case Xsc::ReportTypes::Warning: |
92 | mWarnings.push_back({ FullIndent(), report }); |
93 | break; |
94 | case Xsc::ReportTypes::Error: |
95 | mErrors.push_back({ FullIndent(), report }); |
96 | break; |
97 | } |
98 | } |
99 | |
100 | void getMessages(StringStream& output) |
101 | { |
102 | printAndClearReports(output, mInfos); |
103 | printAndClearReports(output, mWarnings, (mWarnings.size() == 1 ? "WARNING" : "WARNINGS" )); |
104 | printAndClearReports(output, mErrors, (mErrors.size() == 1 ? "ERROR" : "ERRORS" )); |
105 | } |
106 | |
107 | private: |
108 | struct IndentReport |
109 | { |
110 | std::string indent; |
111 | Xsc::Report report; |
112 | }; |
113 | |
114 | static void printMultiLineString(StringStream& output, const std::string& str, const std::string& indent) |
115 | { |
116 | // Determine at which position the actual text begins (excluding the "error (X:Y) : " or the like) |
117 | auto textStartPos = str.find(" : " ); |
118 | if (textStartPos != std::string::npos) |
119 | textStartPos += 3; |
120 | else |
121 | textStartPos = 0; |
122 | |
123 | std::string newLineIndent(textStartPos, ' '); |
124 | |
125 | size_t start = 0; |
126 | bool useNewLineIndent = false; |
127 | while (start < str.size()) |
128 | { |
129 | output << indent; |
130 | |
131 | if (useNewLineIndent) |
132 | output << newLineIndent; |
133 | |
134 | // Print next line |
135 | auto end = str.find('\n', start); |
136 | |
137 | if (end != std::string::npos) |
138 | { |
139 | output << str.substr(start, end - start); |
140 | start = end + 1; |
141 | } |
142 | else |
143 | { |
144 | output << str.substr(start); |
145 | start = end; |
146 | } |
147 | |
148 | output << std::endl; |
149 | useNewLineIndent = true; |
150 | } |
151 | } |
152 | |
153 | void printReport(StringStream& output, const IndentReport& r) |
154 | { |
155 | // Print optional context description |
156 | if (!r.report.Context().empty()) |
157 | printMultiLineString(output, r.report.Context(), r.indent); |
158 | |
159 | // Print report message |
160 | const auto& msg = r.report.Message(); |
161 | printMultiLineString(output, msg, r.indent); |
162 | |
163 | // Print optional line and line-marker |
164 | if (r.report.HasLine()) |
165 | { |
166 | const auto& line = r.report.Line(); |
167 | const auto& marker = r.report.Marker(); |
168 | |
169 | // Print line |
170 | output << r.indent << line << std::endl; |
171 | |
172 | // Print line marker |
173 | output << r.indent << marker << std::endl; |
174 | } |
175 | |
176 | // Print optional hints |
177 | for (const auto& hint : r.report.GetHints()) |
178 | output << r.indent << hint << std::endl; |
179 | } |
180 | |
181 | void printAndClearReports(StringStream& output, Vector<IndentReport>& reports, const String& headline = "" ) |
182 | { |
183 | if (!reports.empty()) |
184 | { |
185 | if (!headline.empty()) |
186 | { |
187 | String s = toString((UINT32)reports.size()) + " " + headline; |
188 | output << s << std::endl; |
189 | output << String(s.size(), '-') << std::endl; |
190 | } |
191 | |
192 | for (const auto& r : reports) |
193 | printReport(output, r); |
194 | |
195 | reports.clear(); |
196 | } |
197 | } |
198 | |
199 | Vector<IndentReport> mInfos; |
200 | Vector<IndentReport> mWarnings; |
201 | Vector<IndentReport> mErrors; |
202 | |
203 | }; |
204 | |
205 | GpuParamObjectType ReflTypeToTextureType(Xsc::Reflection::BufferType type) |
206 | { |
207 | switch(type) |
208 | { |
209 | case Xsc::Reflection::BufferType::RWTexture1D: return GPOT_RWTEXTURE1D; |
210 | case Xsc::Reflection::BufferType::RWTexture1DArray: return GPOT_RWTEXTURE1DARRAY; |
211 | case Xsc::Reflection::BufferType::RWTexture2D: return GPOT_RWTEXTURE2D; |
212 | case Xsc::Reflection::BufferType::RWTexture2DArray: return GPOT_RWTEXTURE2DARRAY; |
213 | case Xsc::Reflection::BufferType::RWTexture3D: return GPOT_RWTEXTURE3D; |
214 | case Xsc::Reflection::BufferType::Texture1D: return GPOT_TEXTURE1D; |
215 | case Xsc::Reflection::BufferType::Texture1DArray: return GPOT_TEXTURE1DARRAY; |
216 | case Xsc::Reflection::BufferType::Texture2D: return GPOT_TEXTURE2D; |
217 | case Xsc::Reflection::BufferType::Texture2DArray: return GPOT_TEXTURE2DARRAY; |
218 | case Xsc::Reflection::BufferType::Texture3D: return GPOT_TEXTURE3D; |
219 | case Xsc::Reflection::BufferType::TextureCube: return GPOT_TEXTURECUBE; |
220 | case Xsc::Reflection::BufferType::TextureCubeArray: return GPOT_TEXTURECUBEARRAY; |
221 | case Xsc::Reflection::BufferType::Texture2DMS: return GPOT_TEXTURE2DMS; |
222 | case Xsc::Reflection::BufferType::Texture2DMSArray: return GPOT_TEXTURE2DMSARRAY; |
223 | default: return GPOT_UNKNOWN; |
224 | } |
225 | } |
226 | |
227 | GpuParamObjectType ReflTypeToBufferType(Xsc::Reflection::BufferType type) |
228 | { |
229 | switch(type) |
230 | { |
231 | case Xsc::Reflection::BufferType::Buffer: return GPOT_RWTYPED_BUFFER; |
232 | case Xsc::Reflection::BufferType::StructuredBuffer: return GPOT_STRUCTURED_BUFFER; |
233 | case Xsc::Reflection::BufferType::ByteAddressBuffer: return GPOT_BYTE_BUFFER; |
234 | case Xsc::Reflection::BufferType::RWBuffer: return GPOT_RWTYPED_BUFFER; |
235 | case Xsc::Reflection::BufferType::RWStructuredBuffer: return GPOT_RWSTRUCTURED_BUFFER; |
236 | case Xsc::Reflection::BufferType::RWByteAddressBuffer: return GPOT_RWBYTE_BUFFER; |
237 | case Xsc::Reflection::BufferType::AppendStructuredBuffer: return GPOT_RWAPPEND_BUFFER; |
238 | case Xsc::Reflection::BufferType::ConsumeStructuredBuffer: return GPOT_RWCONSUME_BUFFER; |
239 | default: return GPOT_UNKNOWN; |
240 | } |
241 | } |
242 | |
243 | GpuParamDataType ReflTypeToDataType(Xsc::Reflection::DataType type) |
244 | { |
245 | switch (type) |
246 | { |
247 | case Xsc::Reflection::DataType::Bool: return GPDT_BOOL; |
248 | case Xsc::Reflection::DataType::Float: return GPDT_FLOAT1; |
249 | case Xsc::Reflection::DataType::Float2: return GPDT_FLOAT2; |
250 | case Xsc::Reflection::DataType::Float3: return GPDT_FLOAT3; |
251 | case Xsc::Reflection::DataType::Float4: return GPDT_FLOAT4; |
252 | case Xsc::Reflection::DataType::UInt: |
253 | case Xsc::Reflection::DataType::Int: |
254 | return GPDT_INT1; |
255 | case Xsc::Reflection::DataType::UInt2: |
256 | case Xsc::Reflection::DataType::Int2: |
257 | return GPDT_INT2; |
258 | case Xsc::Reflection::DataType::UInt3: |
259 | case Xsc::Reflection::DataType::Int3: |
260 | return GPDT_INT3; |
261 | case Xsc::Reflection::DataType::UInt4: |
262 | case Xsc::Reflection::DataType::Int4: |
263 | return GPDT_INT4; |
264 | case Xsc::Reflection::DataType::Float2x2: return GPDT_MATRIX_2X2; |
265 | case Xsc::Reflection::DataType::Float2x3: return GPDT_MATRIX_2X3; |
266 | case Xsc::Reflection::DataType::Float2x4: return GPDT_MATRIX_2X4; |
267 | case Xsc::Reflection::DataType::Float3x2: return GPDT_MATRIX_3X4; |
268 | case Xsc::Reflection::DataType::Float3x3: return GPDT_MATRIX_3X3; |
269 | case Xsc::Reflection::DataType::Float3x4: return GPDT_MATRIX_3X4; |
270 | case Xsc::Reflection::DataType::Float4x2: return GPDT_MATRIX_4X2; |
271 | case Xsc::Reflection::DataType::Float4x3: return GPDT_MATRIX_4X3; |
272 | case Xsc::Reflection::DataType::Float4x4: return GPDT_MATRIX_4X4; |
273 | default: return GPDT_UNKNOWN; |
274 | } |
275 | } |
276 | |
277 | HTexture getBuiltinTexture(UINT32 idx) |
278 | { |
279 | if (idx == 1) |
280 | return BuiltinResources::getTexture(BuiltinTexture::White); |
281 | else if (idx == 2) |
282 | return BuiltinResources::getTexture(BuiltinTexture::Black); |
283 | else if (idx == 3) |
284 | return BuiltinResources::getTexture(BuiltinTexture::Normal); |
285 | |
286 | return HTexture(); |
287 | } |
288 | |
289 | UINT32 getStructSize(INT32 structIdx, const std::vector<Xsc::Reflection::Struct>& structLookup) |
290 | { |
291 | if(structIdx < 0 || structIdx >= (INT32)structLookup.size()) |
292 | return 0; |
293 | |
294 | UINT32 size = 0; |
295 | |
296 | const Xsc::Reflection::Struct& structInfo = structLookup[structIdx]; |
297 | for(auto& entry : structInfo.members) |
298 | { |
299 | if(entry.type == Xsc::Reflection::VariableType::Variable) |
300 | { |
301 | // Note: We're ignoring any padding. Since we can't guarantee the padding will be same for structs across |
302 | // different render backends it's expected for the user to set up structs in such a way so padding is not |
303 | // needed (i.e. add padding variables manually). |
304 | GpuParamDataType type = ReflTypeToDataType((Xsc::Reflection::DataType)entry.baseType); |
305 | |
306 | const GpuParamDataTypeInfo& typeInfo = GpuParams::PARAM_SIZES.lookup[(int)type]; |
307 | size += typeInfo.numColumns * typeInfo.numRows * typeInfo.baseTypeSize * entry.arraySize; |
308 | |
309 | } |
310 | else if(entry.type == Xsc::Reflection::VariableType::Struct) |
311 | size += getStructSize(entry.baseType, structLookup); |
312 | } |
313 | |
314 | return size; |
315 | } |
316 | |
317 | TextureAddressingMode parseTexAddrMode(Xsc::Reflection::TextureAddressMode addrMode) |
318 | { |
319 | switch (addrMode) |
320 | { |
321 | case Xsc::Reflection::TextureAddressMode::Border: |
322 | return TAM_BORDER; |
323 | case Xsc::Reflection::TextureAddressMode::Clamp: |
324 | return TAM_CLAMP; |
325 | case Xsc::Reflection::TextureAddressMode::Mirror: |
326 | case Xsc::Reflection::TextureAddressMode::MirrorOnce: |
327 | return TAM_MIRROR; |
328 | case Xsc::Reflection::TextureAddressMode::Wrap: |
329 | default: |
330 | return TAM_WRAP; |
331 | } |
332 | } |
333 | |
334 | CompareFunction parseCompFunction(Xsc::Reflection::ComparisonFunc compFunc) |
335 | { |
336 | switch(compFunc) |
337 | { |
338 | case Xsc::Reflection::ComparisonFunc::Always: |
339 | default: |
340 | return CMPF_ALWAYS_PASS; |
341 | case Xsc::Reflection::ComparisonFunc::Never: |
342 | return CMPF_ALWAYS_FAIL; |
343 | case Xsc::Reflection::ComparisonFunc::Equal: |
344 | return CMPF_EQUAL; |
345 | case Xsc::Reflection::ComparisonFunc::Greater: |
346 | return CMPF_GREATER; |
347 | case Xsc::Reflection::ComparisonFunc::GreaterEqual: |
348 | return CMPF_GREATER_EQUAL; |
349 | case Xsc::Reflection::ComparisonFunc::Less: |
350 | return CMPF_LESS; |
351 | case Xsc::Reflection::ComparisonFunc::LessEqual: |
352 | return CMPF_LESS_EQUAL; |
353 | case Xsc::Reflection::ComparisonFunc::NotEqual: |
354 | return CMPF_NOT_EQUAL; |
355 | } |
356 | } |
357 | |
358 | SPtr<SamplerState> parseSamplerState(const Xsc::Reflection::SamplerState& sampState) |
359 | { |
360 | SAMPLER_STATE_DESC desc; |
361 | |
362 | desc.addressMode.u = parseTexAddrMode(sampState.addressU); |
363 | desc.addressMode.v = parseTexAddrMode(sampState.addressV); |
364 | desc.addressMode.w = parseTexAddrMode(sampState.addressW); |
365 | |
366 | desc.borderColor[0] = sampState.borderColor[0]; |
367 | desc.borderColor[1] = sampState.borderColor[1]; |
368 | desc.borderColor[2] = sampState.borderColor[2]; |
369 | desc.borderColor[3] = sampState.borderColor[3]; |
370 | |
371 | desc.comparisonFunc = parseCompFunction(sampState.comparisonFunc); |
372 | desc.maxAniso = sampState.maxAnisotropy; |
373 | desc.mipMax = sampState.maxLOD; |
374 | desc.mipMin = sampState.minLOD; |
375 | desc.mipmapBias = sampState.mipLODBias; |
376 | |
377 | switch (sampState.filter) |
378 | { |
379 | case Xsc::Reflection::Filter::MinMagMipPoint: |
380 | case Xsc::Reflection::Filter::ComparisonMinMagMipPoint: |
381 | desc.minFilter = FO_POINT; |
382 | desc.magFilter = FO_POINT; |
383 | desc.mipFilter = FO_POINT; |
384 | break; |
385 | case Xsc::Reflection::Filter::MinMagPointMipLinear: |
386 | case Xsc::Reflection::Filter::ComparisonMinMagPointMipLinear: |
387 | desc.minFilter = FO_POINT; |
388 | desc.magFilter = FO_POINT; |
389 | desc.mipFilter = FO_LINEAR; |
390 | break; |
391 | case Xsc::Reflection::Filter::MinPointMagLinearMipPoint: |
392 | case Xsc::Reflection::Filter::ComparisonMinPointMagLinearMipPoint: |
393 | desc.minFilter = FO_POINT; |
394 | desc.magFilter = FO_LINEAR; |
395 | desc.mipFilter = FO_POINT; |
396 | break; |
397 | case Xsc::Reflection::Filter::MinPointMagMipLinear: |
398 | case Xsc::Reflection::Filter::ComparisonMinPointMagMipLinear: |
399 | desc.minFilter = FO_POINT; |
400 | desc.magFilter = FO_LINEAR; |
401 | desc.mipFilter = FO_LINEAR; |
402 | break; |
403 | case Xsc::Reflection::Filter::MinLinearMagMipPoint: |
404 | case Xsc::Reflection::Filter::ComparisonMinLinearMagMipPoint: |
405 | desc.minFilter = FO_LINEAR; |
406 | desc.magFilter = FO_POINT; |
407 | desc.mipFilter = FO_POINT; |
408 | break; |
409 | case Xsc::Reflection::Filter::MinLinearMagPointMipLinear: |
410 | case Xsc::Reflection::Filter::ComparisonMinLinearMagPointMipLinear: |
411 | desc.minFilter = FO_LINEAR; |
412 | desc.magFilter = FO_POINT; |
413 | desc.mipFilter = FO_LINEAR; |
414 | break; |
415 | case Xsc::Reflection::Filter::MinMagLinearMipPoint: |
416 | case Xsc::Reflection::Filter::ComparisonMinMagLinearMipPoint: |
417 | desc.minFilter = FO_LINEAR; |
418 | desc.magFilter = FO_LINEAR; |
419 | desc.mipFilter = FO_POINT; |
420 | break; |
421 | case Xsc::Reflection::Filter::MinMagMipLinear: |
422 | case Xsc::Reflection::Filter::ComparisonMinMagMipLinear: |
423 | desc.minFilter = FO_LINEAR; |
424 | desc.magFilter = FO_LINEAR; |
425 | desc.mipFilter = FO_LINEAR; |
426 | break; |
427 | case Xsc::Reflection::Filter::Anisotropic: |
428 | case Xsc::Reflection::Filter::ComparisonAnisotropic: |
429 | desc.minFilter = FO_ANISOTROPIC; |
430 | desc.magFilter = FO_ANISOTROPIC; |
431 | desc.mipFilter = FO_ANISOTROPIC; |
432 | break; |
433 | default: |
434 | break; |
435 | } |
436 | |
437 | return SamplerState::create(desc); |
438 | } |
439 | |
440 | void parseParameters(const Xsc::Reflection::ReflectionData& reflData, SHADER_DESC& desc) |
441 | { |
442 | for(auto& entry : reflData.uniforms) |
443 | { |
444 | if ((entry.flags & Xsc::Reflection::Uniform::Flags::Internal) != 0) |
445 | continue; |
446 | |
447 | String ident = entry.ident.c_str(); |
448 | auto parseCommonAttributes = [&entry, &ident, &desc]() |
449 | { |
450 | if (!entry.readableName.empty()) |
451 | { |
452 | SHADER_PARAM_ATTRIBUTE attribute; |
453 | attribute.value.assign(entry.readableName.data(), entry.readableName.size()); |
454 | attribute.nextParamIdx = (UINT32)-1; |
455 | attribute.type = ShaderParamAttributeType::Name; |
456 | |
457 | desc.setParameterAttribute(ident, attribute); |
458 | } |
459 | |
460 | if ((entry.flags & Xsc::Reflection::Uniform::Flags::HideInInspector) != 0) |
461 | { |
462 | SHADER_PARAM_ATTRIBUTE attribute; |
463 | attribute.nextParamIdx = (UINT32)-1; |
464 | attribute.type = ShaderParamAttributeType::HideInInspector; |
465 | |
466 | desc.setParameterAttribute(ident, attribute); |
467 | } |
468 | }; |
469 | |
470 | switch(entry.type) |
471 | { |
472 | case Xsc::Reflection::VariableType::UniformBuffer: |
473 | desc.setParamBlockAttribs(entry.ident.c_str(), false, GBU_STATIC); |
474 | break; |
475 | case Xsc::Reflection::VariableType::Buffer: |
476 | { |
477 | GpuParamObjectType objType = ReflTypeToTextureType((Xsc::Reflection::BufferType)entry.baseType); |
478 | if(objType != GPOT_UNKNOWN) |
479 | { |
480 | // Ignore parameters that were already registered in some previous variation. Note that this implies |
481 | // you cannot have same names for different parameters in different variations. |
482 | if (desc.textureParams.find(ident) != desc.textureParams.end()) |
483 | continue; |
484 | |
485 | if (entry.defaultValue == -1) |
486 | desc.addParameter(SHADER_OBJECT_PARAM_DESC(ident, ident, objType)); |
487 | else |
488 | { |
489 | const Xsc::Reflection::DefaultValue& defVal = reflData.defaultValues[entry.defaultValue]; |
490 | desc.addParameter(SHADER_OBJECT_PARAM_DESC(ident, ident, objType), |
491 | getBuiltinTexture(defVal.integer)); |
492 | } |
493 | |
494 | parseCommonAttributes(); |
495 | } |
496 | else |
497 | { |
498 | // Ignore parameters that were already registered in some previous variation. Note that this implies |
499 | // you cannot have same names for different parameters in different variations. |
500 | if (desc.bufferParams.find(ident) != desc.bufferParams.end()) |
501 | continue; |
502 | |
503 | objType = ReflTypeToBufferType((Xsc::Reflection::BufferType)entry.baseType); |
504 | desc.addParameter(SHADER_OBJECT_PARAM_DESC(ident, ident, objType)); |
505 | |
506 | parseCommonAttributes(); |
507 | } |
508 | } |
509 | break; |
510 | case Xsc::Reflection::VariableType::Sampler: |
511 | { |
512 | auto findIter = reflData.samplerStates.find(entry.ident); |
513 | if (findIter != reflData.samplerStates.end()) |
514 | { |
515 | // Ignore parameters that were already registered in some previous variation. Note that this implies |
516 | // you cannot have same names for different parameters in different variations. |
517 | if(desc.samplerParams.find(ident) != desc.samplerParams.end()) |
518 | continue; |
519 | |
520 | String alias = findIter->second.alias.c_str(); |
521 | |
522 | if(findIter->second.isNonDefault) |
523 | { |
524 | SPtr<SamplerState> defaultVal = parseSamplerState(findIter->second); |
525 | desc.addParameter(SHADER_OBJECT_PARAM_DESC(ident, ident, GPOT_SAMPLER2D), defaultVal); |
526 | |
527 | if (!alias.empty()) |
528 | desc.addParameter(SHADER_OBJECT_PARAM_DESC(ident, alias, GPOT_SAMPLER2D), defaultVal); |
529 | } |
530 | else |
531 | { |
532 | desc.addParameter(SHADER_OBJECT_PARAM_DESC(ident, ident, GPOT_SAMPLER2D)); |
533 | |
534 | if (!alias.empty()) |
535 | desc.addParameter(SHADER_OBJECT_PARAM_DESC(ident, alias, GPOT_SAMPLER2D)); |
536 | } |
537 | } |
538 | else |
539 | { |
540 | desc.addParameter(SHADER_OBJECT_PARAM_DESC(ident, ident, GPOT_SAMPLER2D)); |
541 | } |
542 | break; |
543 | } |
544 | case Xsc::Reflection::VariableType::Variable: |
545 | { |
546 | bool isBlockInternal = false; |
547 | if(entry.uniformBlock != -1) |
548 | { |
549 | std::string blockName = reflData.constantBuffers[entry.uniformBlock].ident; |
550 | for (auto& uniform : reflData.uniforms) |
551 | { |
552 | if (uniform.type == Xsc::Reflection::VariableType::UniformBuffer && uniform.ident == blockName) |
553 | { |
554 | isBlockInternal = (uniform.flags & Xsc::Reflection::Uniform::Flags::Internal) != 0; |
555 | break; |
556 | } |
557 | } |
558 | } |
559 | |
560 | if (!isBlockInternal) |
561 | { |
562 | GpuParamDataType type = ReflTypeToDataType((Xsc::Reflection::DataType)entry.baseType); |
563 | if ((entry.flags & Xsc::Reflection::Uniform::Flags::Color) != 0 && |
564 | (type == GPDT_FLOAT3 || type == GPDT_FLOAT4)) |
565 | { |
566 | type = GPDT_COLOR; |
567 | } |
568 | |
569 | UINT32 arraySize = entry.arraySize; |
570 | |
571 | if (entry.defaultValue == -1) |
572 | desc.addParameter(SHADER_DATA_PARAM_DESC(ident, ident, type, StringID::NONE, arraySize)); |
573 | else |
574 | { |
575 | const Xsc::Reflection::DefaultValue& defVal = reflData.defaultValues[entry.defaultValue]; |
576 | |
577 | desc.addParameter(SHADER_DATA_PARAM_DESC(ident, ident, type, StringID::NONE, arraySize, 0), |
578 | (UINT8*)defVal.matrix); |
579 | } |
580 | |
581 | if(!entry.spriteUVRef.empty() && (type == GPDT_FLOAT4)) |
582 | { |
583 | SHADER_PARAM_ATTRIBUTE attribute; |
584 | attribute.value.assign(entry.spriteUVRef.data(), entry.spriteUVRef.size()); |
585 | attribute.nextParamIdx = (UINT32)-1; |
586 | attribute.type = ShaderParamAttributeType::SpriteUV; |
587 | |
588 | desc.setParameterAttribute(ident, attribute); |
589 | } |
590 | |
591 | parseCommonAttributes(); |
592 | } |
593 | } |
594 | break; |
595 | case Xsc::Reflection::VariableType::Struct: |
596 | { |
597 | INT32 structIdx = entry.baseType; |
598 | UINT32 structSize = getStructSize(structIdx, reflData.structs); |
599 | |
600 | desc.addParameter(SHADER_DATA_PARAM_DESC(ident, ident, GPDT_STRUCT, StringID::NONE, entry.arraySize, |
601 | structSize)); |
602 | } |
603 | break; |
604 | default: ; |
605 | } |
606 | } |
607 | } |
608 | |
609 | /** Types of supported code output when cross compiling HLSL to GLSL. */ |
610 | enum class CrossCompileOutput |
611 | { |
612 | GLSL45, |
613 | GLSL41, |
614 | VKSL45, |
615 | MVKSL |
616 | }; |
617 | |
618 | String crossCompile(const String& hlsl, GpuProgramType type, CrossCompileOutput outputType, bool optionalEntry, |
619 | UINT32& startBindingSlot, SHADER_DESC* shaderDesc = nullptr, Vector<GpuProgramType>* detectedTypes = nullptr) |
620 | { |
621 | SPtr<StringStream> input = bs_shared_ptr_new<StringStream>(); |
622 | |
623 | bool isVKSL = outputType == CrossCompileOutput::VKSL45 || outputType == CrossCompileOutput::MVKSL; |
624 | switch(outputType) |
625 | { |
626 | case CrossCompileOutput::GLSL41: |
627 | case CrossCompileOutput::GLSL45: |
628 | *input << "#define OPENGL 1" << std::endl; |
629 | break; |
630 | case CrossCompileOutput::VKSL45: |
631 | *input << "#define VULKAN 1" << std::endl; |
632 | break; |
633 | case CrossCompileOutput::MVKSL: |
634 | *input << "#define METAL 1" << std::endl; |
635 | break; |
636 | } |
637 | |
638 | *input << hlsl; |
639 | |
640 | Xsc::ShaderInput inputDesc; |
641 | inputDesc.shaderVersion = Xsc::InputShaderVersion::HLSL5; |
642 | inputDesc.sourceCode = input; |
643 | inputDesc.extensions = Xsc::Extensions::LayoutAttribute; |
644 | |
645 | switch (type) |
646 | { |
647 | case GPT_VERTEX_PROGRAM: |
648 | inputDesc.shaderTarget = Xsc::ShaderTarget::VertexShader; |
649 | inputDesc.entryPoint = "vsmain" ; |
650 | break; |
651 | case GPT_GEOMETRY_PROGRAM: |
652 | inputDesc.shaderTarget = Xsc::ShaderTarget::GeometryShader; |
653 | inputDesc.entryPoint = "gsmain" ; |
654 | break; |
655 | case GPT_HULL_PROGRAM: |
656 | inputDesc.shaderTarget = Xsc::ShaderTarget::TessellationControlShader; |
657 | inputDesc.entryPoint = "hsmain" ; |
658 | break; |
659 | case GPT_DOMAIN_PROGRAM: |
660 | inputDesc.shaderTarget = Xsc::ShaderTarget::TessellationEvaluationShader; |
661 | inputDesc.entryPoint = "dsmain" ; |
662 | break; |
663 | case GPT_FRAGMENT_PROGRAM: |
664 | inputDesc.shaderTarget = Xsc::ShaderTarget::FragmentShader; |
665 | inputDesc.entryPoint = "fsmain" ; |
666 | break; |
667 | case GPT_COMPUTE_PROGRAM: |
668 | inputDesc.shaderTarget = Xsc::ShaderTarget::ComputeShader; |
669 | inputDesc.entryPoint = "csmain" ; |
670 | break; |
671 | default: |
672 | break; |
673 | } |
674 | |
675 | StringStream output; |
676 | |
677 | Xsc::ShaderOutput outputDesc; |
678 | outputDesc.sourceCode = &output; |
679 | outputDesc.options.autoBinding = isVKSL; |
680 | outputDesc.options.autoBindingStartSlot = startBindingSlot; |
681 | outputDesc.options.fragmentLocations = true; |
682 | outputDesc.options.separateShaders = true; |
683 | outputDesc.options.separateSamplers = isVKSL; |
684 | outputDesc.options.allowExtensions = true; |
685 | outputDesc.nameMangling.inputPrefix = "bs_" ; |
686 | outputDesc.nameMangling.outputPrefix = "bs_" ; |
687 | outputDesc.nameMangling.useAlwaysSemantics = true; |
688 | outputDesc.nameMangling.renameBufferFields = true; |
689 | |
690 | switch(outputType) |
691 | { |
692 | case CrossCompileOutput::GLSL45: |
693 | outputDesc.shaderVersion = Xsc::OutputShaderVersion::GLSL450; |
694 | break; |
695 | case CrossCompileOutput::GLSL41: |
696 | outputDesc.shaderVersion = Xsc::OutputShaderVersion::GLSL410; |
697 | break; |
698 | case CrossCompileOutput::VKSL45: |
699 | outputDesc.shaderVersion = Xsc::OutputShaderVersion::VKSL450; |
700 | break; |
701 | case CrossCompileOutput::MVKSL: |
702 | outputDesc.shaderVersion = Xsc::OutputShaderVersion::VKSL450; |
703 | break; |
704 | } |
705 | |
706 | XscLog log; |
707 | Xsc::Reflection::ReflectionData reflectionData; |
708 | bool compileSuccess = Xsc::CompileShader(inputDesc, outputDesc, &log, &reflectionData); |
709 | if (!compileSuccess) |
710 | { |
711 | // If enabled, don't fail if entry point isn't found |
712 | bool done = true; |
713 | if(optionalEntry) |
714 | { |
715 | bool entryFound = false; |
716 | for (auto& entry : reflectionData.functions) |
717 | { |
718 | if(entry.ident == inputDesc.entryPoint) |
719 | { |
720 | entryFound = true; |
721 | break; |
722 | } |
723 | } |
724 | |
725 | if (!entryFound) |
726 | done = false; |
727 | } |
728 | |
729 | if (done) |
730 | { |
731 | StringStream logOutput; |
732 | log.getMessages(logOutput); |
733 | |
734 | LOGERR("Shader cross compilation failed. Log: \n\n" + logOutput.str()); |
735 | return "" ; |
736 | } |
737 | } |
738 | |
739 | for (auto& entry : reflectionData.constantBuffers) |
740 | startBindingSlot = std::max(startBindingSlot, entry.location + 1u); |
741 | |
742 | for (auto& entry : reflectionData.textures) |
743 | startBindingSlot = std::max(startBindingSlot, entry.location + 1u); |
744 | |
745 | for (auto& entry : reflectionData.storageBuffers) |
746 | startBindingSlot = std::max(startBindingSlot, entry.location + 1u); |
747 | |
748 | if(detectedTypes != nullptr) |
749 | { |
750 | for(auto& entry : reflectionData.functions) |
751 | { |
752 | if (entry.ident == "vsmain" ) |
753 | detectedTypes->push_back(GPT_VERTEX_PROGRAM); |
754 | else if (entry.ident == "fsmain" ) |
755 | detectedTypes->push_back(GPT_FRAGMENT_PROGRAM); |
756 | else if (entry.ident == "gsmain" ) |
757 | detectedTypes->push_back(GPT_GEOMETRY_PROGRAM); |
758 | else if (entry.ident == "dsmain" ) |
759 | detectedTypes->push_back(GPT_DOMAIN_PROGRAM); |
760 | else if (entry.ident == "hsmain" ) |
761 | detectedTypes->push_back(GPT_HULL_PROGRAM); |
762 | else if (entry.ident == "csmain" ) |
763 | detectedTypes->push_back(GPT_COMPUTE_PROGRAM); |
764 | } |
765 | |
766 | // If no entry points found, and error occurred, report error |
767 | if(!compileSuccess && detectedTypes->empty()) |
768 | { |
769 | StringStream logOutput; |
770 | log.getMessages(logOutput); |
771 | |
772 | LOGERR("Shader cross compilation failed. Log: \n\n" + logOutput.str()); |
773 | return "" ; |
774 | } |
775 | } |
776 | |
777 | if (shaderDesc != nullptr) |
778 | parseParameters(reflectionData, *shaderDesc); |
779 | |
780 | return output.str(); |
781 | } |
782 | |
783 | String crossCompile(const String& hlsl, GpuProgramType type, CrossCompileOutput outputType, UINT32& startBindingSlot) |
784 | { |
785 | return crossCompile(hlsl, type, outputType, false, startBindingSlot); |
786 | } |
787 | |
788 | void reflectHLSL(const String& hlsl, SHADER_DESC& shaderDesc, Vector<GpuProgramType>& entryPoints) |
789 | { |
790 | UINT32 dummy = 0; |
791 | crossCompile(hlsl, GPT_VERTEX_PROGRAM, CrossCompileOutput::GLSL45, true, dummy, &shaderDesc, &entryPoints); |
792 | } |
793 | |
794 | BSLFXCompileResult BSLFXCompiler::compile(const String& name, const String& source, |
795 | const UnorderedMap<String, String>& defines, ShadingLanguageFlags languages) |
796 | { |
797 | // Parse global shader options & shader meta-data |
798 | SHADER_DESC shaderDesc; |
799 | Vector<String> includes; |
800 | |
801 | BSLFXCompileResult output = compileShader(source, defines, languages, shaderDesc, includes); |
802 | |
803 | // Generate a shader from the parsed information |
804 | output.shader = Shader::_createPtr(name, shaderDesc); |
805 | output.shader->setIncludeFiles(includes); |
806 | |
807 | return output; |
808 | } |
809 | |
810 | BSLFXCompileResult BSLFXCompiler::parseFX(ParseState* parseState, const char* source, const UnorderedMap<String, String>& defines) |
811 | { |
812 | for(auto& define : defines) |
813 | { |
814 | if (define.first.size() == 0) |
815 | continue; |
816 | |
817 | addDefine(parseState, define.first.c_str()); |
818 | |
819 | if(define.second.size() > 0) |
820 | addDefineExpr(parseState, define.second.c_str()); |
821 | } |
822 | |
823 | yyscan_t scanner; |
824 | YY_BUFFER_STATE state; |
825 | |
826 | BSLFXCompileResult output; |
827 | |
828 | if (yylex_init_extra(parseState, &scanner)) |
829 | { |
830 | output.errorMessage = "Internal error: Lexer failed to initialize." ; |
831 | return output; |
832 | } |
833 | |
834 | // If debug output from lexer is needed uncomment this and add %debug option to lexer file |
835 | //yyset_debug(true, scanner); |
836 | |
837 | // If debug output from parser is needed uncomment this and add %debug option to parser file |
838 | //yydebug = true; |
839 | |
840 | state = yy_scan_string(source, scanner); |
841 | |
842 | bool parsingFailed = yyparse(parseState, scanner) > 0; |
843 | |
844 | if (parseState->hasError > 0) |
845 | { |
846 | output.errorMessage = parseState->errorMessage; |
847 | output.errorLine = parseState->errorLine; |
848 | output.errorColumn = parseState->errorColumn; |
849 | |
850 | if (parseState->errorFile != nullptr) |
851 | output.errorFile = parseState->errorFile; |
852 | |
853 | goto cleanup; |
854 | } |
855 | else if(parsingFailed) |
856 | { |
857 | output.errorMessage = "Internal error: Parsing failed." ; |
858 | goto cleanup; |
859 | } |
860 | |
861 | cleanup: |
862 | yy_delete_buffer(state, scanner); |
863 | yylex_destroy(scanner); |
864 | |
865 | return output; |
866 | } |
867 | |
868 | BSLFXCompiler::ShaderMetaData BSLFXCompiler::parseShaderMetaData(ASTFXNode* shader) |
869 | { |
870 | ShaderMetaData metaData; |
871 | |
872 | metaData.language = "hlsl" ; |
873 | metaData.isMixin = shader->type == NT_Mixin; |
874 | |
875 | for (int i = 0; i < shader->options->count; i++) |
876 | { |
877 | NodeOption* option = &shader->options->entries[i]; |
878 | |
879 | switch (option->type) |
880 | { |
881 | case OT_Tags: |
882 | { |
883 | ASTFXNode* tagsNode = option->value.nodePtr; |
884 | for (int j = 0; j < tagsNode->options->count; j++) |
885 | { |
886 | NodeOption* tagOption = &tagsNode->options->entries[j]; |
887 | |
888 | if (tagOption->type == OT_TagValue) |
889 | metaData.tags.push_back(removeQuotes(tagOption->value.strValue)); |
890 | } |
891 | } |
892 | break; |
893 | case OT_Variations: |
894 | { |
895 | ASTFXNode* variationsNode = option->value.nodePtr; |
896 | for (int j = 0; j < variationsNode->options->count; j++) |
897 | { |
898 | NodeOption* variationOption = &variationsNode->options->entries[j]; |
899 | |
900 | if(variationOption->type == OT_Variation) |
901 | parseVariations(metaData, variationOption->value.nodePtr); |
902 | } |
903 | } |
904 | break; |
905 | case OT_Identifier: |
906 | metaData.name = option->value.strValue; |
907 | break; |
908 | case OT_Mixin: |
909 | metaData.includes.push_back(option->value.strValue); |
910 | break; |
911 | default: |
912 | break; |
913 | } |
914 | } |
915 | |
916 | return metaData; |
917 | } |
918 | |
919 | BSLFXCompileResult BSLFXCompiler::parseMetaDataAndOptions(ASTFXNode* rootNode, |
920 | Vector<std::pair<ASTFXNode*, ShaderMetaData>>& shaderMetaData, |
921 | Vector<SubShaderData>& subShaderData, SHADER_DESC& shaderDesc) |
922 | { |
923 | BSLFXCompileResult output; |
924 | |
925 | // Only enable for debug purposes |
926 | //SLFXDebugPrint(parseState->rootNode, ""); |
927 | |
928 | if (rootNode == nullptr || rootNode->type != NT_Root) |
929 | { |
930 | output.errorMessage = "Root is null or not a shader." ; |
931 | return output; |
932 | } |
933 | |
934 | // Parse global shader options & shader meta-data |
935 | //// Go in reverse because options are added in reverse order during parsing |
936 | for (int i = rootNode->options->count - 1; i >= 0; i--) |
937 | { |
938 | NodeOption* option = &rootNode->options->entries[i]; |
939 | |
940 | switch (option->type) |
941 | { |
942 | case OT_Options: |
943 | parseOptions(option->value.nodePtr, shaderDesc); |
944 | break; |
945 | case OT_Shader: |
946 | { |
947 | // We initially parse only meta-data, so we can handle out-of-order mixin/shader definitions |
948 | ShaderMetaData metaData = parseShaderMetaData(option->value.nodePtr); |
949 | shaderMetaData.push_back(std::make_pair(option->value.nodePtr, metaData)); |
950 | |
951 | break; |
952 | } |
953 | case OT_SubShader: |
954 | { |
955 | SubShaderData data = parseSubShader(option->value.nodePtr); |
956 | subShaderData.push_back(data); |
957 | |
958 | break; |
959 | } |
960 | default: |
961 | break; |
962 | } |
963 | } |
964 | |
965 | return output; |
966 | } |
967 | |
968 | |
969 | void BSLFXCompiler::parseVariations(ShaderMetaData& metaData, ASTFXNode* variations) |
970 | { |
971 | assert(variations->type == NT_Variation); |
972 | |
973 | VariationData variationData; |
974 | for (int i = 0; i < variations->options->count; i++) |
975 | { |
976 | NodeOption* option = &variations->options->entries[i]; |
977 | |
978 | switch (option->type) |
979 | { |
980 | case OT_Identifier: |
981 | variationData.identifier = option->value.strValue; |
982 | break; |
983 | case OT_VariationOption: |
984 | variationData.values.push_back(parseVariationOption(option->value.nodePtr)); |
985 | break; |
986 | case OT_Attributes: |
987 | { |
988 | AttributeData attribs = parseAttributes(option->value.nodePtr); |
989 | |
990 | for (auto& entry : attribs.attributes) |
991 | { |
992 | if (entry.first == OT_AttrName) |
993 | variationData.name = entry.second; |
994 | else if(entry.first == OT_AttrShow) |
995 | variationData.internal = false; |
996 | } |
997 | } |
998 | default: |
999 | break; |
1000 | } |
1001 | } |
1002 | |
1003 | if (!variationData.identifier.empty()) |
1004 | metaData.variations.push_back(variationData); |
1005 | } |
1006 | |
1007 | BSLFXCompiler::VariationOption BSLFXCompiler::parseVariationOption(ASTFXNode* variationOption) |
1008 | { |
1009 | assert(variationOption->type == NT_VariationOption); |
1010 | |
1011 | VariationOption output; |
1012 | for (int i = 0; i < variationOption->options->count; i++) |
1013 | { |
1014 | NodeOption* option = &variationOption->options->entries[i]; |
1015 | |
1016 | switch (option->type) |
1017 | { |
1018 | case OT_VariationValue: |
1019 | output.value = option->value.intValue; |
1020 | break; |
1021 | case OT_Attributes: |
1022 | { |
1023 | AttributeData attribs = parseAttributes(option->value.nodePtr); |
1024 | |
1025 | for(auto& entry : attribs.attributes) |
1026 | { |
1027 | if(entry.first == OT_AttrName) |
1028 | output.name = entry.second; |
1029 | } |
1030 | } |
1031 | default: |
1032 | break; |
1033 | } |
1034 | } |
1035 | |
1036 | return output; |
1037 | } |
1038 | |
1039 | BSLFXCompiler::AttributeData BSLFXCompiler::parseAttributes(ASTFXNode* attributes) |
1040 | { |
1041 | assert(attributes->type == NT_Attributes); |
1042 | |
1043 | AttributeData attributeData; |
1044 | for (int i = 0; i < attributes->options->count; i++) |
1045 | { |
1046 | NodeOption* option = &attributes->options->entries[i]; |
1047 | |
1048 | switch (option->type) |
1049 | { |
1050 | case OT_AttrName: |
1051 | attributeData.attributes.push_back(std::pair<INT32, String>(OT_AttrName, removeQuotes(option->value.strValue))); |
1052 | break; |
1053 | case OT_AttrShow: |
1054 | attributeData.attributes.push_back(std::pair<INT32, String>(OT_AttrShow, "" )); |
1055 | break; |
1056 | default: |
1057 | break; |
1058 | } |
1059 | } |
1060 | |
1061 | return attributeData; |
1062 | } |
1063 | |
1064 | QueueSortType BSLFXCompiler::parseSortType(CullAndSortModeValue sortType) |
1065 | { |
1066 | switch (sortType) |
1067 | { |
1068 | case CASV_BackToFront: |
1069 | return QueueSortType::BackToFront; |
1070 | case CASV_FrontToBack: |
1071 | return QueueSortType::FrontToBack; |
1072 | case CASV_None: |
1073 | return QueueSortType::None; |
1074 | default: |
1075 | break; |
1076 | } |
1077 | |
1078 | return QueueSortType::None; |
1079 | } |
1080 | |
1081 | CompareFunction BSLFXCompiler::parseCompFunc(CompFuncValue compFunc) |
1082 | { |
1083 | switch (compFunc) |
1084 | { |
1085 | case CFV_Pass: |
1086 | return CMPF_ALWAYS_PASS; |
1087 | case CFV_Fail: |
1088 | return CMPF_ALWAYS_FAIL; |
1089 | case CFV_LT: |
1090 | return CMPF_LESS; |
1091 | case CFV_LTE: |
1092 | return CMPF_LESS_EQUAL; |
1093 | case CFV_EQ: |
1094 | return CMPF_EQUAL; |
1095 | case CFV_NEQ: |
1096 | return CMPF_NOT_EQUAL; |
1097 | case CFV_GT: |
1098 | return CMPF_GREATER; |
1099 | case CFV_GTE: |
1100 | return CMPF_GREATER_EQUAL; |
1101 | } |
1102 | |
1103 | return CMPF_ALWAYS_PASS; |
1104 | } |
1105 | |
1106 | BlendFactor BSLFXCompiler::parseBlendFactor(OpValue factor) |
1107 | { |
1108 | switch (factor) |
1109 | { |
1110 | case OV_One: |
1111 | return BF_ONE; |
1112 | case OV_Zero: |
1113 | return BF_ZERO; |
1114 | case OV_DestColor: |
1115 | return BF_DEST_COLOR; |
1116 | case OV_SrcColor: |
1117 | return BF_SOURCE_COLOR; |
1118 | case OV_InvDestColor: |
1119 | return BF_INV_DEST_COLOR; |
1120 | case OV_InvSrcColor: |
1121 | return BF_INV_SOURCE_COLOR; |
1122 | case OV_DestAlpha: |
1123 | return BF_DEST_ALPHA; |
1124 | case OV_SrcAlpha: |
1125 | return BF_SOURCE_ALPHA; |
1126 | case OV_InvDestAlpha: |
1127 | return BF_INV_DEST_ALPHA; |
1128 | case OV_InvSrcAlpha: |
1129 | return BF_INV_SOURCE_ALPHA; |
1130 | default: |
1131 | break; |
1132 | } |
1133 | |
1134 | return BF_ONE; |
1135 | } |
1136 | |
1137 | BlendOperation BSLFXCompiler::parseBlendOp(BlendOpValue op) |
1138 | { |
1139 | switch (op) |
1140 | { |
1141 | case BOV_Add: |
1142 | return BO_ADD; |
1143 | case BOV_Max: |
1144 | return BO_MAX; |
1145 | case BOV_Min: |
1146 | return BO_MIN; |
1147 | case BOV_Subtract: |
1148 | return BO_SUBTRACT; |
1149 | case BOV_RevSubtract: |
1150 | return BO_REVERSE_SUBTRACT; |
1151 | } |
1152 | |
1153 | return BO_ADD; |
1154 | } |
1155 | |
1156 | StencilOperation BSLFXCompiler::parseStencilOp(OpValue op) |
1157 | { |
1158 | switch (op) |
1159 | { |
1160 | case OV_Keep: |
1161 | return SOP_KEEP; |
1162 | case OV_Zero: |
1163 | return SOP_ZERO; |
1164 | case OV_Replace: |
1165 | return SOP_REPLACE; |
1166 | case OV_Incr: |
1167 | return SOP_INCREMENT; |
1168 | case OV_Decr: |
1169 | return SOP_DECREMENT; |
1170 | case OV_IncrWrap: |
1171 | return SOP_INCREMENT_WRAP; |
1172 | case OV_DecrWrap: |
1173 | return SOP_DECREMENT_WRAP; |
1174 | case OV_Invert: |
1175 | return SOP_INVERT; |
1176 | default: |
1177 | break; |
1178 | } |
1179 | |
1180 | return SOP_KEEP; |
1181 | } |
1182 | |
1183 | CullingMode BSLFXCompiler::parseCullMode(CullAndSortModeValue cm) |
1184 | { |
1185 | switch (cm) |
1186 | { |
1187 | case CASV_None: |
1188 | return CULL_NONE; |
1189 | case CASV_CW: |
1190 | return CULL_CLOCKWISE; |
1191 | case CASV_CCW: |
1192 | return CULL_COUNTERCLOCKWISE; |
1193 | default: |
1194 | break; |
1195 | } |
1196 | |
1197 | return CULL_COUNTERCLOCKWISE; |
1198 | } |
1199 | |
1200 | PolygonMode BSLFXCompiler::parseFillMode(FillModeValue fm) |
1201 | { |
1202 | if (fm == FMV_Wire) |
1203 | return PM_WIREFRAME; |
1204 | |
1205 | return PM_SOLID; |
1206 | } |
1207 | |
1208 | void BSLFXCompiler::parseStencilFront(DEPTH_STENCIL_STATE_DESC& desc, ASTFXNode* stencilOpNode) |
1209 | { |
1210 | if (stencilOpNode == nullptr || stencilOpNode->type != NT_StencilOp) |
1211 | return; |
1212 | |
1213 | for (int i = 0; i < stencilOpNode->options->count; i++) |
1214 | { |
1215 | NodeOption* option = &stencilOpNode->options->entries[i]; |
1216 | |
1217 | switch (option->type) |
1218 | { |
1219 | case OT_Fail: |
1220 | desc.frontStencilFailOp = parseStencilOp((OpValue)option->value.intValue); |
1221 | break; |
1222 | case OT_ZFail: |
1223 | desc.frontStencilZFailOp = parseStencilOp((OpValue)option->value.intValue); |
1224 | break; |
1225 | case OT_PassOp: |
1226 | desc.frontStencilPassOp = parseStencilOp((OpValue)option->value.intValue); |
1227 | break; |
1228 | case OT_CompareFunc: |
1229 | desc.frontStencilComparisonFunc = parseCompFunc((CompFuncValue)option->value.intValue); |
1230 | break; |
1231 | default: |
1232 | break; |
1233 | } |
1234 | } |
1235 | } |
1236 | |
1237 | void BSLFXCompiler::parseStencilBack(DEPTH_STENCIL_STATE_DESC& desc, ASTFXNode* stencilOpNode) |
1238 | { |
1239 | if (stencilOpNode == nullptr || stencilOpNode->type != NT_StencilOp) |
1240 | return; |
1241 | |
1242 | for (int i = 0; i < stencilOpNode->options->count; i++) |
1243 | { |
1244 | NodeOption* option = &stencilOpNode->options->entries[i]; |
1245 | |
1246 | switch (option->type) |
1247 | { |
1248 | case OT_Fail: |
1249 | desc.backStencilFailOp = parseStencilOp((OpValue)option->value.intValue); |
1250 | break; |
1251 | case OT_ZFail: |
1252 | desc.backStencilZFailOp = parseStencilOp((OpValue)option->value.intValue); |
1253 | break; |
1254 | case OT_PassOp: |
1255 | desc.backStencilPassOp = parseStencilOp((OpValue)option->value.intValue); |
1256 | break; |
1257 | case OT_CompareFunc: |
1258 | desc.backStencilComparisonFunc = parseCompFunc((CompFuncValue)option->value.intValue); |
1259 | break; |
1260 | default: |
1261 | break; |
1262 | } |
1263 | } |
1264 | } |
1265 | |
1266 | void BSLFXCompiler::parseColorBlendDef(RENDER_TARGET_BLEND_STATE_DESC& desc, ASTFXNode* blendDefNode) |
1267 | { |
1268 | if (blendDefNode == nullptr || blendDefNode->type != NT_BlendDef) |
1269 | return; |
1270 | |
1271 | for (int i = 0; i < blendDefNode->options->count; i++) |
1272 | { |
1273 | NodeOption* option = &blendDefNode->options->entries[i]; |
1274 | |
1275 | switch (option->type) |
1276 | { |
1277 | case OT_Source: |
1278 | desc.srcBlend = parseBlendFactor((OpValue)option->value.intValue); |
1279 | break; |
1280 | case OT_Dest: |
1281 | desc.dstBlend = parseBlendFactor((OpValue)option->value.intValue); |
1282 | break; |
1283 | case OT_Op: |
1284 | desc.blendOp = parseBlendOp((BlendOpValue)option->value.intValue); |
1285 | break; |
1286 | default: |
1287 | break; |
1288 | } |
1289 | } |
1290 | } |
1291 | |
1292 | void BSLFXCompiler::parseAlphaBlendDef(RENDER_TARGET_BLEND_STATE_DESC& desc, ASTFXNode* blendDefNode) |
1293 | { |
1294 | if (blendDefNode == nullptr || blendDefNode->type != NT_BlendDef) |
1295 | return; |
1296 | |
1297 | for (int i = 0; i < blendDefNode->options->count; i++) |
1298 | { |
1299 | NodeOption* option = &blendDefNode->options->entries[i]; |
1300 | |
1301 | switch (option->type) |
1302 | { |
1303 | case OT_Source: |
1304 | desc.srcBlendAlpha = parseBlendFactor((OpValue)option->value.intValue); |
1305 | break; |
1306 | case OT_Dest: |
1307 | desc.dstBlendAlpha = parseBlendFactor((OpValue)option->value.intValue); |
1308 | break; |
1309 | case OT_Op: |
1310 | desc.blendOpAlpha = parseBlendOp((BlendOpValue)option->value.intValue); |
1311 | break; |
1312 | default: |
1313 | break; |
1314 | } |
1315 | } |
1316 | } |
1317 | |
1318 | void BSLFXCompiler::parseRenderTargetBlendState(BLEND_STATE_DESC& desc, ASTFXNode* targetNode, UINT32& index) |
1319 | { |
1320 | if (targetNode == nullptr || targetNode->type != NT_Target) |
1321 | return; |
1322 | |
1323 | for (int i = 0; i < targetNode->options->count; i++) |
1324 | { |
1325 | NodeOption* option = &targetNode->options->entries[i]; |
1326 | |
1327 | switch (option->type) |
1328 | { |
1329 | case OT_Index: |
1330 | index = option->value.intValue; |
1331 | break; |
1332 | default: |
1333 | break; |
1334 | } |
1335 | } |
1336 | |
1337 | if (index >= BS_MAX_MULTIPLE_RENDER_TARGETS) |
1338 | return; |
1339 | |
1340 | RENDER_TARGET_BLEND_STATE_DESC& rtDesc = desc.renderTargetDesc[index]; |
1341 | for (int i = 0; i < targetNode->options->count; i++) |
1342 | { |
1343 | NodeOption* option = &targetNode->options->entries[i]; |
1344 | |
1345 | switch (option->type) |
1346 | { |
1347 | case OT_Enabled: |
1348 | rtDesc.blendEnable = option->value.intValue > 0; |
1349 | break; |
1350 | case OT_Color: |
1351 | parseColorBlendDef(rtDesc, option->value.nodePtr); |
1352 | break; |
1353 | case OT_Alpha: |
1354 | parseAlphaBlendDef(rtDesc, option->value.nodePtr); |
1355 | break; |
1356 | case OT_WriteMask: |
1357 | rtDesc.renderTargetWriteMask = option->value.intValue; |
1358 | break; |
1359 | default: |
1360 | break; |
1361 | } |
1362 | } |
1363 | |
1364 | index++; |
1365 | } |
1366 | |
1367 | bool BSLFXCompiler::parseBlendState(PassData& desc, ASTFXNode* blendNode) |
1368 | { |
1369 | if (blendNode == nullptr || blendNode->type != NT_Blend) |
1370 | return false; |
1371 | |
1372 | bool isDefault = true; |
1373 | SmallVector<ASTFXNode*, 8> targets; |
1374 | |
1375 | for (int i = 0; i < blendNode->options->count; i++) |
1376 | { |
1377 | NodeOption* option = &blendNode->options->entries[i]; |
1378 | |
1379 | switch (option->type) |
1380 | { |
1381 | case OT_AlphaToCoverage: |
1382 | desc.blendDesc.alphaToCoverageEnable = option->value.intValue > 0; |
1383 | isDefault = false; |
1384 | break; |
1385 | case OT_IndependantBlend: |
1386 | desc.blendDesc.independantBlendEnable = option->value.intValue > 0; |
1387 | isDefault = false; |
1388 | break; |
1389 | case OT_Target: |
1390 | targets.add(option->value.nodePtr); |
1391 | isDefault = false; |
1392 | break; |
1393 | default: |
1394 | break; |
1395 | } |
1396 | } |
1397 | |
1398 | // Parse targets in reverse as their order matters and we want to visit them in the top-down order as defined in |
1399 | // the source code |
1400 | UINT32 index = 0; |
1401 | for(auto iter = targets.rbegin(); iter != targets.rend(); ++iter) |
1402 | parseRenderTargetBlendState(desc.blendDesc, *iter, index); |
1403 | |
1404 | return !isDefault; |
1405 | } |
1406 | |
1407 | bool BSLFXCompiler::parseRasterizerState(PassData& desc, ASTFXNode* rasterNode) |
1408 | { |
1409 | if (rasterNode == nullptr || rasterNode->type != NT_Raster) |
1410 | return false; |
1411 | |
1412 | bool isDefault = true; |
1413 | |
1414 | for (int i = 0; i < rasterNode->options->count; i++) |
1415 | { |
1416 | NodeOption* option = &rasterNode->options->entries[i]; |
1417 | |
1418 | switch (option->type) |
1419 | { |
1420 | case OT_FillMode: |
1421 | desc.rasterizerDesc.polygonMode = parseFillMode((FillModeValue)option->value.intValue); |
1422 | isDefault = false; |
1423 | break; |
1424 | case OT_CullMode: |
1425 | desc.rasterizerDesc.cullMode = parseCullMode((CullAndSortModeValue)option->value.intValue); |
1426 | isDefault = false; |
1427 | break; |
1428 | case OT_DepthBias: |
1429 | desc.rasterizerDesc.depthBias = option->value.floatValue; |
1430 | isDefault = false; |
1431 | break; |
1432 | case OT_SDepthBias: |
1433 | desc.rasterizerDesc.slopeScaledDepthBias = option->value.floatValue; |
1434 | isDefault = false; |
1435 | break; |
1436 | case OT_DepthClip: |
1437 | desc.rasterizerDesc.depthClipEnable = option->value.intValue > 0; |
1438 | isDefault = false; |
1439 | break; |
1440 | case OT_Scissor: |
1441 | desc.rasterizerDesc.scissorEnable = option->value.intValue > 0; |
1442 | isDefault = false; |
1443 | break; |
1444 | case OT_Multisample: |
1445 | desc.rasterizerDesc.multisampleEnable = option->value.intValue > 0; |
1446 | isDefault = false; |
1447 | break; |
1448 | case OT_AALine: |
1449 | desc.rasterizerDesc.antialiasedLineEnable = option->value.intValue > 0; |
1450 | isDefault = false; |
1451 | break; |
1452 | default: |
1453 | break; |
1454 | } |
1455 | } |
1456 | |
1457 | return !isDefault; |
1458 | } |
1459 | |
1460 | bool BSLFXCompiler::parseDepthState(PassData& passData, ASTFXNode* depthNode) |
1461 | { |
1462 | if (depthNode == nullptr || depthNode->type != NT_Depth) |
1463 | return false; |
1464 | |
1465 | bool isDefault = true; |
1466 | |
1467 | for (int i = 0; i < depthNode->options->count; i++) |
1468 | { |
1469 | NodeOption* option = &depthNode->options->entries[i]; |
1470 | |
1471 | switch (option->type) |
1472 | { |
1473 | case OT_DepthRead: |
1474 | passData.depthStencilDesc.depthReadEnable = option->value.intValue > 0; |
1475 | isDefault = false; |
1476 | break; |
1477 | case OT_DepthWrite: |
1478 | passData.depthStencilDesc.depthWriteEnable = option->value.intValue > 0; |
1479 | isDefault = false; |
1480 | break; |
1481 | case OT_CompareFunc: |
1482 | passData.depthStencilDesc.depthComparisonFunc = parseCompFunc((CompFuncValue)option->value.intValue); |
1483 | isDefault = false; |
1484 | break; |
1485 | default: |
1486 | break; |
1487 | } |
1488 | } |
1489 | |
1490 | return !isDefault; |
1491 | } |
1492 | |
1493 | bool BSLFXCompiler::parseStencilState(PassData& passData, ASTFXNode* stencilNode) |
1494 | { |
1495 | if (stencilNode == nullptr || stencilNode->type != NT_Stencil) |
1496 | return false; |
1497 | |
1498 | bool isDefault = true; |
1499 | |
1500 | for (int i = 0; i < stencilNode->options->count; i++) |
1501 | { |
1502 | NodeOption* option = &stencilNode->options->entries[i]; |
1503 | |
1504 | switch (option->type) |
1505 | { |
1506 | case OT_Enabled: |
1507 | passData.depthStencilDesc.stencilEnable = option->value.intValue > 0; |
1508 | isDefault = false; |
1509 | break; |
1510 | case OT_StencilReadMask: |
1511 | passData.depthStencilDesc.stencilReadMask = (UINT8)option->value.intValue; |
1512 | isDefault = false; |
1513 | break; |
1514 | case OT_StencilWriteMask: |
1515 | passData.depthStencilDesc.stencilWriteMask = (UINT8)option->value.intValue; |
1516 | isDefault = false; |
1517 | break; |
1518 | case OT_StencilOpFront: |
1519 | parseStencilFront(passData.depthStencilDesc, option->value.nodePtr); |
1520 | isDefault = false; |
1521 | break; |
1522 | case OT_StencilOpBack: |
1523 | parseStencilBack(passData.depthStencilDesc, option->value.nodePtr); |
1524 | isDefault = false; |
1525 | break; |
1526 | case OT_StencilRef: |
1527 | passData.stencilRefValue = option->value.intValue; |
1528 | break; |
1529 | default: |
1530 | break; |
1531 | } |
1532 | } |
1533 | |
1534 | return !isDefault; |
1535 | } |
1536 | |
1537 | void BSLFXCompiler::parseCodeBlock(ASTFXNode* codeNode, const Vector<String>& codeBlocks, PassData& passData) |
1538 | { |
1539 | if (codeNode == nullptr || (codeNode->type != NT_Code)) |
1540 | { |
1541 | return; |
1542 | } |
1543 | |
1544 | UINT32 index = (UINT32)-1; |
1545 | for (int j = 0; j < codeNode->options->count; j++) |
1546 | { |
1547 | if (codeNode->options->entries[j].type == OT_Index) |
1548 | index = codeNode->options->entries[j].value.intValue; |
1549 | } |
1550 | |
1551 | if (index != (UINT32)-1 && index < (UINT32)codeBlocks.size()) |
1552 | { |
1553 | passData.code += codeBlocks[index]; |
1554 | } |
1555 | } |
1556 | |
1557 | void BSLFXCompiler::parsePass(ASTFXNode* passNode, const Vector<String>& codeBlocks, PassData& passData) |
1558 | { |
1559 | if (passNode == nullptr || passNode->type != NT_Pass) |
1560 | return; |
1561 | |
1562 | for (int i = 0; i < passNode->options->count; i++) |
1563 | { |
1564 | NodeOption* option = &passNode->options->entries[i]; |
1565 | |
1566 | switch (option->type) |
1567 | { |
1568 | case OT_Blend: |
1569 | passData.blendIsDefault &= !parseBlendState(passData, option->value.nodePtr); |
1570 | break; |
1571 | case OT_Raster: |
1572 | passData.rasterizerIsDefault &= !parseRasterizerState(passData, option->value.nodePtr); |
1573 | break; |
1574 | case OT_Depth: |
1575 | passData.depthStencilIsDefault &= !parseDepthState(passData, option->value.nodePtr); |
1576 | break; |
1577 | case OT_Stencil: |
1578 | passData.depthStencilIsDefault &= !parseStencilState(passData, option->value.nodePtr); |
1579 | break; |
1580 | case OT_Code: |
1581 | parseCodeBlock(option->value.nodePtr, codeBlocks, passData); |
1582 | break; |
1583 | default: |
1584 | break; |
1585 | } |
1586 | } |
1587 | } |
1588 | |
1589 | void BSLFXCompiler::parseShader(ASTFXNode* shaderNode, const Vector<String>& codeBlocks, ShaderData& shaderData) |
1590 | { |
1591 | if (shaderNode == nullptr || (shaderNode->type != NT_Shader && shaderNode->type != NT_Mixin)) |
1592 | return; |
1593 | |
1594 | // There must always be at least one pass |
1595 | if(shaderData.passes.empty()) |
1596 | { |
1597 | shaderData.passes.push_back(PassData()); |
1598 | shaderData.passes.back().seqIdx = 0; |
1599 | } |
1600 | |
1601 | PassData combinedCommonPassData; |
1602 | |
1603 | UINT32 nextPassIdx = 0; |
1604 | // Go in reverse because options are added in reverse order during parsing |
1605 | for (int i = shaderNode->options->count - 1; i >= 0; i--) |
1606 | { |
1607 | NodeOption* option = &shaderNode->options->entries[i]; |
1608 | |
1609 | switch (option->type) |
1610 | { |
1611 | case OT_Pass: |
1612 | { |
1613 | UINT32 passIdx = nextPassIdx; |
1614 | PassData* passData = nullptr; |
1615 | for (auto& entry : shaderData.passes) |
1616 | { |
1617 | if (entry.seqIdx == passIdx) |
1618 | passData = &entry; |
1619 | } |
1620 | |
1621 | if (passData == nullptr) |
1622 | { |
1623 | shaderData.passes.push_back(PassData()); |
1624 | passData = &shaderData.passes.back(); |
1625 | |
1626 | passData->seqIdx = passIdx; |
1627 | } |
1628 | |
1629 | nextPassIdx = std::max(nextPassIdx, passIdx) + 1; |
1630 | passData->code = combinedCommonPassData.code + passData->code; |
1631 | |
1632 | ASTFXNode* passNode = option->value.nodePtr; |
1633 | parsePass(passNode, codeBlocks, *passData); |
1634 | } |
1635 | break; |
1636 | case OT_Code: |
1637 | { |
1638 | PassData commonPassData; |
1639 | parseCodeBlock(option->value.nodePtr, codeBlocks, commonPassData); |
1640 | |
1641 | for (auto& passData : shaderData.passes) |
1642 | passData.code += commonPassData.code; |
1643 | |
1644 | combinedCommonPassData.code += commonPassData.code; |
1645 | } |
1646 | break; |
1647 | case OT_FeatureSet: |
1648 | shaderData.metaData.featureSet = option->value.strValue; |
1649 | break; |
1650 | default: |
1651 | break; |
1652 | } |
1653 | } |
1654 | |
1655 | // Parse common pass states |
1656 | for (int i = 0; i < shaderNode->options->count; i++) |
1657 | { |
1658 | NodeOption* option = &shaderNode->options->entries[i]; |
1659 | |
1660 | switch (option->type) |
1661 | { |
1662 | case OT_Blend: |
1663 | for (auto& passData : shaderData.passes) |
1664 | passData.blendIsDefault &= !parseBlendState(passData, option->value.nodePtr); |
1665 | break; |
1666 | case OT_Raster: |
1667 | for (auto& passData : shaderData.passes) |
1668 | passData.rasterizerIsDefault &= !parseRasterizerState(passData, option->value.nodePtr); |
1669 | break; |
1670 | case OT_Depth: |
1671 | for (auto& passData : shaderData.passes) |
1672 | passData.depthStencilIsDefault &= !parseDepthState(passData, option->value.nodePtr); |
1673 | break; |
1674 | case OT_Stencil: |
1675 | for (auto& passData : shaderData.passes) |
1676 | passData.depthStencilIsDefault &= !parseStencilState(passData, option->value.nodePtr); |
1677 | break; |
1678 | default: |
1679 | break; |
1680 | } |
1681 | } |
1682 | } |
1683 | |
1684 | BSLFXCompiler::SubShaderData BSLFXCompiler::parseSubShader(ASTFXNode* subShader) |
1685 | { |
1686 | SubShaderData subShaderData; |
1687 | |
1688 | //// Go in reverse because options are added in reverse order during parsing |
1689 | for (int i = subShader->options->count - 1; i >= 0; i--) |
1690 | { |
1691 | NodeOption* option = &subShader->options->entries[i]; |
1692 | |
1693 | switch (option->type) |
1694 | { |
1695 | case OT_Identifier: |
1696 | subShaderData.name = option->value.strValue; |
1697 | break; |
1698 | case OT_Index: |
1699 | subShaderData.codeBlockIndex = option->value.intValue; |
1700 | default: |
1701 | break; |
1702 | } |
1703 | } |
1704 | |
1705 | return subShaderData; |
1706 | } |
1707 | |
1708 | void BSLFXCompiler::parseOptions(ASTFXNode* optionsNode, SHADER_DESC& shaderDesc) |
1709 | { |
1710 | if (optionsNode == nullptr || optionsNode->type != NT_Options) |
1711 | return; |
1712 | |
1713 | for (int i = optionsNode->options->count - 1; i >= 0; i--) |
1714 | { |
1715 | NodeOption* option = &optionsNode->options->entries[i]; |
1716 | |
1717 | switch (option->type) |
1718 | { |
1719 | case OT_Separable: |
1720 | shaderDesc.separablePasses = option->value.intValue > 1; |
1721 | break; |
1722 | case OT_Sort: |
1723 | shaderDesc.queueSortType = parseSortType((CullAndSortModeValue)option->value.intValue); |
1724 | break; |
1725 | case OT_Priority: |
1726 | shaderDesc.queuePriority = option->value.intValue; |
1727 | break; |
1728 | case OT_Transparent: |
1729 | shaderDesc.flags |= ShaderFlag::Transparent; |
1730 | break; |
1731 | case OT_Forward: |
1732 | shaderDesc.flags |= ShaderFlag::Forward; |
1733 | break; |
1734 | default: |
1735 | break; |
1736 | } |
1737 | } |
1738 | } |
1739 | |
1740 | BSLFXCompileResult BSLFXCompiler::populateVariations(Vector<std::pair<ASTFXNode*, ShaderMetaData>>& shaderMetaData) |
1741 | { |
1742 | BSLFXCompileResult output; |
1743 | |
1744 | // Inherit variations from mixins |
1745 | bool* mixinWasParsed = bs_stack_alloc<bool>((UINT32)shaderMetaData.size()); |
1746 | |
1747 | std::function<bool(const ShaderMetaData&, ShaderMetaData&)> parseInherited = |
1748 | [&](const ShaderMetaData& metaData, ShaderMetaData& combinedMetaData) |
1749 | { |
1750 | for (auto riter = metaData.includes.rbegin(); riter != metaData.includes.rend(); ++riter) |
1751 | { |
1752 | const String& include = *riter; |
1753 | |
1754 | UINT32 baseIdx = -1; |
1755 | for (UINT32 i = 0; i < (UINT32)shaderMetaData.size(); i++) |
1756 | { |
1757 | auto& entry = shaderMetaData[i]; |
1758 | if (!entry.second.isMixin) |
1759 | continue; |
1760 | |
1761 | if (entry.second.name == include) |
1762 | { |
1763 | bool matches = entry.second.language == metaData.language || entry.second.language == "Any" ; |
1764 | |
1765 | // We want the last matching mixin, in order to allow mixins to override each other |
1766 | if (matches) |
1767 | baseIdx = i; |
1768 | } |
1769 | } |
1770 | |
1771 | if (baseIdx != (UINT32)-1) |
1772 | { |
1773 | auto& entry = shaderMetaData[baseIdx]; |
1774 | |
1775 | // Was already parsed previously, don't parse it multiple times (happens when multiple mixins |
1776 | // include the same mixin) |
1777 | if (mixinWasParsed[baseIdx]) |
1778 | continue; |
1779 | |
1780 | if (!parseInherited(entry.second, combinedMetaData)) |
1781 | return false; |
1782 | |
1783 | for (auto& variation : entry.second.variations) |
1784 | combinedMetaData.variations.push_back(variation); |
1785 | |
1786 | mixinWasParsed[baseIdx] = true; |
1787 | } |
1788 | else |
1789 | { |
1790 | output.errorMessage = "Mixin \"" + include + "\" cannot be found." ; |
1791 | return false; |
1792 | } |
1793 | } |
1794 | |
1795 | return true; |
1796 | }; |
1797 | |
1798 | for (auto& entry : shaderMetaData) |
1799 | { |
1800 | const ShaderMetaData& metaData = entry.second; |
1801 | if (metaData.isMixin) |
1802 | continue; |
1803 | |
1804 | bs_zero_out(mixinWasParsed, shaderMetaData.size()); |
1805 | ShaderMetaData combinedMetaData = metaData; |
1806 | if (!parseInherited(metaData, combinedMetaData)) |
1807 | { |
1808 | bs_stack_free(mixinWasParsed); |
1809 | return output; |
1810 | } |
1811 | |
1812 | entry.second = combinedMetaData; |
1813 | } |
1814 | |
1815 | bs_stack_free(mixinWasParsed); |
1816 | |
1817 | return output; |
1818 | } |
1819 | |
1820 | void BSLFXCompiler::populateVariationParamInfos(const ShaderMetaData& shaderMetaData, SHADER_DESC& desc) |
1821 | { |
1822 | for(auto& entry : shaderMetaData.variations) |
1823 | { |
1824 | ShaderVariationParamInfo paramInfo; |
1825 | paramInfo.isInternal = entry.internal; |
1826 | paramInfo.name = entry.name; |
1827 | paramInfo.identifier = entry.identifier; |
1828 | |
1829 | for(auto& value : entry.values) |
1830 | { |
1831 | ShaderVariationParamValue paramValue; |
1832 | paramValue.name = value.name; |
1833 | paramValue.value = value.value; |
1834 | |
1835 | paramInfo.values.add(paramValue); |
1836 | } |
1837 | |
1838 | desc.variationParams.push_back(paramInfo); |
1839 | } |
1840 | } |
1841 | |
1842 | BSLFXCompileResult BSLFXCompiler::compileTechniques( |
1843 | const Vector<std::pair<ASTFXNode*, ShaderMetaData>>& shaderMetaData, const String& source, |
1844 | const UnorderedMap<String, String>& defines, ShadingLanguageFlags languages, SHADER_DESC& shaderDesc, |
1845 | Vector<String>& includes) |
1846 | { |
1847 | BSLFXCompileResult output; |
1848 | |
1849 | // Build a list of different variations and re-parse the source using the relevant defines |
1850 | UnorderedSet<String> includeSet; |
1851 | for (auto& entry : shaderMetaData) |
1852 | { |
1853 | const ShaderMetaData& metaData = entry.second; |
1854 | if (metaData.isMixin) |
1855 | continue; |
1856 | |
1857 | // Generate a list of variations |
1858 | Vector<ShaderVariation> variations; |
1859 | |
1860 | if (metaData.variations.empty()) |
1861 | variations.push_back(ShaderVariation()); |
1862 | else |
1863 | { |
1864 | Vector<const VariationData*> todo; |
1865 | for (UINT32 i = 0; i < (UINT32)metaData.variations.size(); i++) |
1866 | todo.push_back(&metaData.variations[i]); |
1867 | |
1868 | while (!todo.empty()) |
1869 | { |
1870 | const VariationData* current = todo.back(); |
1871 | todo.erase(todo.end() - 1); |
1872 | |
1873 | // Variation parameter that's either defined or isn't |
1874 | if (current->values.empty()) |
1875 | { |
1876 | // This is the first variation parameter, register new variations |
1877 | if (variations.empty()) |
1878 | { |
1879 | ShaderVariation a; |
1880 | ShaderVariation b; |
1881 | |
1882 | b.addParam(ShaderVariation::Param(current->identifier, 1)); |
1883 | |
1884 | variations.push_back(a); |
1885 | variations.push_back(b); |
1886 | } |
1887 | else // Duplicate existing variations, and add the parameter |
1888 | { |
1889 | UINT32 numVariations = (UINT32)variations.size(); |
1890 | for (UINT32 i = 0; i < numVariations; i++) |
1891 | { |
1892 | // Make a copy |
1893 | variations.push_back(variations[i]); |
1894 | |
1895 | // Add the parameter to existing variation |
1896 | variations[i].addParam(ShaderVariation::Param(current->identifier, 1)); |
1897 | } |
1898 | } |
1899 | } |
1900 | else // Variation parameter with multiple values |
1901 | { |
1902 | // This is the first variation parameter, register new variations |
1903 | if (variations.empty()) |
1904 | { |
1905 | for (UINT32 i = 0; i < (UINT32)current->values.size(); i++) |
1906 | { |
1907 | ShaderVariation variation; |
1908 | variation.addParam(ShaderVariation::Param(current->identifier, current->values[i].value)); |
1909 | |
1910 | variations.push_back(variation); |
1911 | } |
1912 | } |
1913 | else // Duplicate existing variations, and add the parameter |
1914 | { |
1915 | UINT32 numVariations = (UINT32)variations.size(); |
1916 | for (UINT32 i = 0; i < numVariations; i++) |
1917 | { |
1918 | for (UINT32 j = 1; j < (UINT32)current->values.size(); j++) |
1919 | { |
1920 | ShaderVariation copy = variations[i]; |
1921 | copy.addParam(ShaderVariation::Param(current->identifier, current->values[j].value)); |
1922 | |
1923 | variations.push_back(copy); |
1924 | } |
1925 | |
1926 | variations[i].addParam(ShaderVariation::Param(current->identifier, current->values[0].value)); |
1927 | } |
1928 | } |
1929 | } |
1930 | } |
1931 | } |
1932 | |
1933 | // For every variation, re-parse the file with relevant defines |
1934 | for (auto& variation : variations) |
1935 | { |
1936 | UnorderedMap<String, String> globalDefines = defines; |
1937 | UnorderedMap<String, String> variationDefines = variation.getDefines().getAll(); |
1938 | |
1939 | for (auto& define : variationDefines) |
1940 | globalDefines[define.first] = define.second; |
1941 | |
1942 | ParseState* variationParseState = parseStateCreate(); |
1943 | output = parseFX(variationParseState, source.c_str(), globalDefines); |
1944 | |
1945 | if (!output.errorMessage.empty()) |
1946 | parseStateDelete(variationParseState); |
1947 | else |
1948 | { |
1949 | Vector<String> codeBlocks; |
1950 | RawCode* rawCode = variationParseState->rawCodeBlock[RCT_CodeBlock]; |
1951 | while (rawCode != nullptr) |
1952 | { |
1953 | while ((INT32)codeBlocks.size() <= rawCode->index) |
1954 | codeBlocks.push_back(String()); |
1955 | |
1956 | codeBlocks[rawCode->index] = String(rawCode->code, rawCode->size); |
1957 | rawCode = rawCode->next; |
1958 | } |
1959 | |
1960 | output = compileTechniques(variationParseState, entry.second.name, codeBlocks, variation, languages, |
1961 | includeSet, shaderDesc); |
1962 | |
1963 | if (!output.errorMessage.empty()) |
1964 | return output; |
1965 | } |
1966 | } |
1967 | } |
1968 | |
1969 | // Generate a shader from the parsed techniques |
1970 | for (auto& entry : includeSet) |
1971 | includes.push_back(entry); |
1972 | |
1973 | // Verify techniques compile correctly |
1974 | bool hasError = false; |
1975 | StringStream gpuProgError; |
1976 | for (auto& technique : shaderDesc.techniques) |
1977 | { |
1978 | if(!technique->isSupported()) |
1979 | continue; |
1980 | |
1981 | UINT32 numPasses = technique->getNumPasses(); |
1982 | technique->compile(); |
1983 | |
1984 | for (UINT32 i = 0; i < numPasses; i++) |
1985 | { |
1986 | SPtr<Pass> pass = technique->getPass(i); |
1987 | |
1988 | auto checkCompileStatus = [&](const String& prefix, const SPtr<GpuProgram>& prog) |
1989 | { |
1990 | if (prog != nullptr) |
1991 | { |
1992 | prog->blockUntilCoreInitialized(); |
1993 | |
1994 | if (!prog->isCompiled()) |
1995 | { |
1996 | hasError = true; |
1997 | gpuProgError << prefix << ": " << prog->getCompileErrorMessage() << std::endl; |
1998 | } |
1999 | } |
2000 | }; |
2001 | |
2002 | const SPtr<GraphicsPipelineState>& graphicsPipeline = pass->getGraphicsPipelineState(); |
2003 | if (graphicsPipeline) |
2004 | { |
2005 | checkCompileStatus("Vertex program" , graphicsPipeline->getVertexProgram()); |
2006 | checkCompileStatus("Fragment program" , graphicsPipeline->getFragmentProgram()); |
2007 | checkCompileStatus("Geometry program" , graphicsPipeline->getGeometryProgram()); |
2008 | checkCompileStatus("Hull program" , graphicsPipeline->getHullProgram()); |
2009 | checkCompileStatus("Domain program" , graphicsPipeline->getDomainProgram()); |
2010 | } |
2011 | |
2012 | const SPtr<ComputePipelineState>& computePipeline = pass->getComputePipelineState(); |
2013 | if (computePipeline) |
2014 | checkCompileStatus("Compute program" , computePipeline->getProgram()); |
2015 | } |
2016 | } |
2017 | |
2018 | if (hasError) |
2019 | { |
2020 | output.errorMessage = "Failed compiling GPU program(s): " + gpuProgError.str(); |
2021 | output.errorLine = 0; |
2022 | output.errorColumn = 0; |
2023 | } |
2024 | |
2025 | return output; |
2026 | } |
2027 | |
2028 | BSLFXCompileResult BSLFXCompiler::compileShader(String source, const UnorderedMap<String, String>& defines, |
2029 | ShadingLanguageFlags languages, SHADER_DESC& shaderDesc, Vector<String>& includes) |
2030 | { |
2031 | SPtr<ct::Renderer> renderer = RendererManager::instance().getActive(); |
2032 | |
2033 | // Run the lexer/parser and generate the AST |
2034 | ParseState* parseState = parseStateCreate(); |
2035 | BSLFXCompileResult output = parseFX(parseState, source.c_str(), defines); |
2036 | |
2037 | if (!output.errorMessage.empty()) |
2038 | { |
2039 | parseStateDelete(parseState); |
2040 | return output; |
2041 | } |
2042 | |
2043 | // Parse global shader options & shader meta-data |
2044 | Vector<pair<ASTFXNode*, ShaderMetaData>> shaderMetaData; |
2045 | Vector<SubShaderData> subShaderData; |
2046 | |
2047 | output = parseMetaDataAndOptions(parseState->rootNode, shaderMetaData, subShaderData, shaderDesc); |
2048 | |
2049 | if (!output.errorMessage.empty()) |
2050 | { |
2051 | parseStateDelete(parseState); |
2052 | return output; |
2053 | } |
2054 | |
2055 | // Parse sub-shader code blocks |
2056 | Vector<String> subShaderCodeBlocks; |
2057 | RawCode* rawCode = parseState->rawCodeBlock[RCT_SubShaderBlock]; |
2058 | while (rawCode != nullptr) |
2059 | { |
2060 | while ((INT32)subShaderCodeBlocks.size() <= rawCode->index) |
2061 | subShaderCodeBlocks.push_back(String()); |
2062 | |
2063 | subShaderCodeBlocks[rawCode->index] = String(rawCode->code, rawCode->size); |
2064 | rawCode = rawCode->next; |
2065 | } |
2066 | |
2067 | parseStateDelete(parseState); |
2068 | |
2069 | output = populateVariations(shaderMetaData); |
2070 | |
2071 | if (!output.errorMessage.empty()) |
2072 | return output; |
2073 | |
2074 | // Note: Must be called after populateVariations, to ensure variations from mixins are inherited |
2075 | for(auto& entry : shaderMetaData) |
2076 | { |
2077 | if(entry.second.isMixin) |
2078 | continue; |
2079 | |
2080 | populateVariationParamInfos(entry.second, shaderDesc); |
2081 | } |
2082 | |
2083 | output = compileTechniques(shaderMetaData, source, defines, languages, shaderDesc, includes); |
2084 | |
2085 | if (!output.errorMessage.empty()) |
2086 | return output; |
2087 | |
2088 | // Parse sub-shaders |
2089 | for (auto& entry : subShaderData) |
2090 | { |
2091 | if(entry.codeBlockIndex > (UINT32)subShaderCodeBlocks.size()) |
2092 | continue; |
2093 | |
2094 | const String& subShaderCode = subShaderCodeBlocks[entry.codeBlockIndex]; |
2095 | |
2096 | ct::ShaderExtensionPointInfo extPointInfo = renderer->getShaderExtensionPointInfo(entry.name); |
2097 | for (auto& extPointShader : extPointInfo.shaders) |
2098 | { |
2099 | Path path = gBuiltinResources().getRawShaderFolder(); |
2100 | path.append(extPointShader.path); |
2101 | path.setExtension(path.getExtension()); |
2102 | |
2103 | StringStream subShaderSource; |
2104 | const UnorderedMap<String, String> subShaderDefines = extPointShader.defines.getAll(); |
2105 | { |
2106 | Lock fileLock = FileScheduler::getLock(path); |
2107 | |
2108 | SPtr<DataStream> stream = FileSystem::openFile(path); |
2109 | if(stream) |
2110 | subShaderSource << stream->getAsString(); |
2111 | } |
2112 | |
2113 | subShaderSource << "\n" ; |
2114 | subShaderSource << subShaderCode; |
2115 | |
2116 | SHADER_DESC subShaderDesc; |
2117 | Vector<String> subShaderIncludes; |
2118 | BSLFXCompileResult subShaderOutput = compileShader(subShaderSource.str(), subShaderDefines, languages, |
2119 | subShaderDesc, subShaderIncludes); |
2120 | |
2121 | if (!subShaderOutput.errorMessage.empty()) |
2122 | return subShaderOutput; |
2123 | |
2124 | // Clear the sub-shader descriptor of any data other than techniques |
2125 | Vector<SPtr<Technique>> techniques = subShaderDesc.techniques; |
2126 | subShaderDesc = SHADER_DESC(); |
2127 | subShaderDesc.techniques = techniques; |
2128 | |
2129 | SubShader subShader; |
2130 | subShader.name = extPointShader.name; |
2131 | subShader.shader = Shader::_createPtr(subShader.name, subShaderDesc); |
2132 | |
2133 | shaderDesc.subShaders.push_back(subShader); |
2134 | } |
2135 | } |
2136 | |
2137 | return output; |
2138 | } |
2139 | |
2140 | BSLFXCompileResult BSLFXCompiler::compileTechniques(ParseState* parseState, const String& name, |
2141 | const Vector<String>& codeBlocks, const ShaderVariation& variation, ShadingLanguageFlags languages, |
2142 | UnorderedSet<String>& includes, SHADER_DESC& shaderDesc) |
2143 | { |
2144 | BSLFXCompileResult output; |
2145 | |
2146 | if (parseState->rootNode == nullptr || parseState->rootNode->type != NT_Root) |
2147 | { |
2148 | parseStateDelete(parseState); |
2149 | |
2150 | output.errorMessage = "Root is null or not a shader." ; |
2151 | return output; |
2152 | } |
2153 | |
2154 | Vector<pair<ASTFXNode*, ShaderData>> shaderData; |
2155 | |
2156 | // Go in reverse because options are added in reverse order during parsing |
2157 | for (int i = parseState->rootNode->options->count - 1; i >= 0; i--) |
2158 | { |
2159 | NodeOption* option = &parseState->rootNode->options->entries[i]; |
2160 | |
2161 | switch (option->type) |
2162 | { |
2163 | case OT_Shader: |
2164 | { |
2165 | // We initially parse only meta-data, so we can handle out-of-order technique definitions |
2166 | ShaderMetaData metaData = parseShaderMetaData(option->value.nodePtr); |
2167 | |
2168 | // Skip all techniques except the one we're parsing |
2169 | if(metaData.name != name && !metaData.isMixin) |
2170 | continue; |
2171 | |
2172 | shaderData.push_back(std::make_pair(option->value.nodePtr, ShaderData())); |
2173 | ShaderData& data = shaderData.back().second; |
2174 | data.metaData = metaData; |
2175 | |
2176 | break; |
2177 | } |
2178 | default: |
2179 | break; |
2180 | } |
2181 | } |
2182 | |
2183 | bool* mixinWasParsed = bs_stack_alloc<bool>((UINT32)shaderData.size()); |
2184 | std::function<bool(const ShaderMetaData&, ShaderData&)> parseInherited = |
2185 | [&](const ShaderMetaData& metaData, ShaderData& outShader) |
2186 | { |
2187 | for (auto riter = metaData.includes.rbegin(); riter != metaData.includes.rend(); ++riter) |
2188 | { |
2189 | const String& includes = *riter; |
2190 | |
2191 | UINT32 baseIdx = -1; |
2192 | for(UINT32 i = 0; i < (UINT32)shaderData.size(); i++) |
2193 | { |
2194 | auto& entry = shaderData[i]; |
2195 | if (!entry.second.metaData.isMixin) |
2196 | continue; |
2197 | |
2198 | if (entry.second.metaData.name == includes) |
2199 | { |
2200 | bool matches = |
2201 | (entry.second.metaData.language == metaData.language || |
2202 | entry.second.metaData.language == "Any" ); |
2203 | |
2204 | // We want the last matching mixin, in order to allow mixins to override each other |
2205 | if (matches) |
2206 | baseIdx = i; |
2207 | } |
2208 | } |
2209 | |
2210 | if (baseIdx != (UINT32)-1) |
2211 | { |
2212 | auto& entry = shaderData[baseIdx]; |
2213 | |
2214 | // Was already parsed previously, don't parse it multiple times (happens when multiple mixins |
2215 | // include the same mixin) |
2216 | if (mixinWasParsed[baseIdx]) |
2217 | continue; |
2218 | |
2219 | if (!parseInherited(entry.second.metaData, outShader)) |
2220 | return false; |
2221 | |
2222 | parseShader(entry.first, codeBlocks, outShader); |
2223 | mixinWasParsed[baseIdx] = true; |
2224 | |
2225 | } |
2226 | else |
2227 | { |
2228 | output.errorMessage = "Mixin \"" + includes + "\" cannot be found." ; |
2229 | return false; |
2230 | } |
2231 | } |
2232 | |
2233 | return true; |
2234 | }; |
2235 | |
2236 | // Actually parse shaders |
2237 | for (auto& entry : shaderData) |
2238 | { |
2239 | const ShaderMetaData& metaData = entry.second.metaData; |
2240 | if (metaData.isMixin) |
2241 | continue; |
2242 | |
2243 | bs_zero_out(mixinWasParsed, shaderData.size()); |
2244 | if (!parseInherited(metaData, entry.second)) |
2245 | { |
2246 | parseStateDelete(parseState); |
2247 | bs_stack_free(mixinWasParsed); |
2248 | return output; |
2249 | } |
2250 | |
2251 | parseShader(entry.first, codeBlocks, entry.second); |
2252 | } |
2253 | |
2254 | bs_stack_free(mixinWasParsed); |
2255 | |
2256 | IncludeLink* includeLink = parseState->includes; |
2257 | while(includeLink != nullptr) |
2258 | { |
2259 | String includeFilename = includeLink->data->filename; |
2260 | |
2261 | auto iterFind = std::find(includes.begin(), includes.end(), includeFilename); |
2262 | if (iterFind == includes.end()) |
2263 | includes.insert(includeFilename); |
2264 | |
2265 | includeLink = includeLink->next; |
2266 | } |
2267 | |
2268 | parseStateDelete(parseState); |
2269 | |
2270 | // Parse extended HLSL code and generate per-program code, also convert to GLSL/VKSL/MSL |
2271 | const auto end = (UINT32)shaderData.size(); |
2272 | Vector<pair<ASTFXNode*, ShaderData>> outputShaderData; |
2273 | for(UINT32 i = 0; i < end; i++) |
2274 | { |
2275 | const ShaderMetaData& metaData = shaderData[i].second.metaData; |
2276 | if (metaData.isMixin) |
2277 | continue; |
2278 | |
2279 | ShaderData& shaderDataEntry = shaderData[i].second; |
2280 | |
2281 | ShaderData hlslShaderData = shaderData[i].second; |
2282 | ShaderData glslShaderData = shaderData[i].second; |
2283 | |
2284 | // When working with OpenGL, lower-end feature sets are supported. For other backends, high-end is always assumed. |
2285 | CrossCompileOutput glslVersion = CrossCompileOutput::GLSL41; |
2286 | if(glslShaderData.metaData.featureSet == "HighEnd" ) |
2287 | { |
2288 | glslShaderData.metaData.language = "glsl" ; |
2289 | glslVersion = CrossCompileOutput::GLSL45; |
2290 | } |
2291 | else |
2292 | glslShaderData.metaData.language = "glsl4_1" ; |
2293 | |
2294 | ShaderData vkslShaderData = shaderData[i].second; |
2295 | vkslShaderData.metaData.language = "vksl" ; |
2296 | |
2297 | ShaderData mvksl = shaderData[i].second; |
2298 | mvksl.metaData.language = "mvksl" ; |
2299 | |
2300 | const auto numPasses = (UINT32)shaderDataEntry.passes.size(); |
2301 | for(UINT32 j = 0; j < numPasses; j++) |
2302 | { |
2303 | PassData& passData = shaderDataEntry.passes[j]; |
2304 | |
2305 | // Find valid entry points and parameters |
2306 | // Note: XShaderCompiler needs to do a full pass when doing reflection, and for each individual program |
2307 | // type. If performance is ever important here it could be good to update XShaderCompiler so it can |
2308 | // somehow save the AST and then re-use it for multiple actions. |
2309 | Vector<GpuProgramType> types; |
2310 | reflectHLSL(passData.code, shaderDesc, types); |
2311 | |
2312 | auto crossCompilePass = [&types](PassData& passData, CrossCompileOutput language) |
2313 | { |
2314 | UINT32 binding = 0; |
2315 | |
2316 | for (auto& type : types) |
2317 | { |
2318 | switch (type) |
2319 | { |
2320 | case GPT_VERTEX_PROGRAM: |
2321 | passData.vertexCode = crossCompile(passData.code, GPT_VERTEX_PROGRAM, language, binding); |
2322 | break; |
2323 | case GPT_FRAGMENT_PROGRAM: |
2324 | passData.fragmentCode = crossCompile(passData.code, GPT_FRAGMENT_PROGRAM, language, binding); |
2325 | break; |
2326 | case GPT_GEOMETRY_PROGRAM: |
2327 | passData.geometryCode = crossCompile(passData.code, GPT_GEOMETRY_PROGRAM, language, binding); |
2328 | break; |
2329 | case GPT_HULL_PROGRAM: |
2330 | passData.hullCode = crossCompile(passData.code, GPT_HULL_PROGRAM, language, binding); |
2331 | break; |
2332 | case GPT_DOMAIN_PROGRAM: |
2333 | passData.domainCode = crossCompile(passData.code, GPT_DOMAIN_PROGRAM, language, binding); |
2334 | break; |
2335 | case GPT_COMPUTE_PROGRAM: |
2336 | passData.computeCode = crossCompile(passData.code, GPT_COMPUTE_PROGRAM, language, binding); |
2337 | break; |
2338 | default: |
2339 | break; |
2340 | } |
2341 | } |
2342 | }; |
2343 | |
2344 | if(languages.isSet(ShadingLanguageFlag::GLSL)) |
2345 | crossCompilePass(glslShaderData.passes[j], glslVersion); |
2346 | |
2347 | if(languages.isSet(ShadingLanguageFlag::VKSL)) |
2348 | crossCompilePass(vkslShaderData.passes[j], CrossCompileOutput::VKSL45); |
2349 | |
2350 | if(languages.isSet(ShadingLanguageFlag::MSL)) |
2351 | crossCompilePass(mvksl.passes[j], CrossCompileOutput::MVKSL); |
2352 | |
2353 | if(languages.isSet(ShadingLanguageFlag::HLSL)) |
2354 | { |
2355 | PassData& hlslPassData = hlslShaderData.passes[j]; |
2356 | |
2357 | // Clean non-standard HLSL |
2358 | // Note: Ideally we add a full HLSL output module to XShaderCompiler, instead of using simple regex. This |
2359 | // way the syntax could be enhanced with more complex features, while still being able to output pure |
2360 | // HLSL. |
2361 | static const std::regex attrRegex( |
2362 | R"(\[\s*layout\s*\(.*\)\s*\]|\[\s*internal\s*\]|\[\s*color\s*\]|\[\s*alias\s*\(.*\)\s*\]|\[\s*spriteuv\s*\(.*\)\s*\])" ); |
2363 | hlslPassData.code = regex_replace(hlslPassData.code, attrRegex, "" ); |
2364 | |
2365 | static const std::regex attr2Regex( |
2366 | R"(\[\s*hideInInspector\s*\]|\[\s*name\s*\(".*"\)\s*\])" ); |
2367 | hlslPassData.code = regex_replace(hlslPassData.code, attr2Regex, "" ); |
2368 | |
2369 | static const std::regex initializerRegex( |
2370 | R"(Texture2D\s*(\S*)\s*=.*;)" ); |
2371 | hlslPassData.code = regex_replace(hlslPassData.code, initializerRegex, "Texture2D $1;" ); |
2372 | |
2373 | static const std::regex warpWithSyncRegex( |
2374 | R"(Warp(Group|Device|All)MemoryBarrierWithWarpSync)" ); |
2375 | hlslPassData.code = regex_replace(hlslPassData.code, warpWithSyncRegex, "$1MemoryBarrierWithGroupSync" ); |
2376 | |
2377 | static const std::regex warpNoSyncRegex( |
2378 | R"(Warp(Group|Device|All)MemoryBarrier)" ); |
2379 | hlslPassData.code = regex_replace(hlslPassData.code, warpNoSyncRegex, "$1MemoryBarrier" ); |
2380 | |
2381 | // Note: I'm just copying HLSL code as-is. This code will contain all entry points which could have |
2382 | // an effect on compile time. It would be ideal to remove dead code depending on program type. This would |
2383 | // involve adding a HLSL code generator to XShaderCompiler. |
2384 | for (auto& type : types) |
2385 | { |
2386 | switch (type) |
2387 | { |
2388 | case GPT_VERTEX_PROGRAM: |
2389 | hlslPassData.vertexCode = hlslPassData.code; |
2390 | break; |
2391 | case GPT_FRAGMENT_PROGRAM: |
2392 | hlslPassData.fragmentCode = hlslPassData.code; |
2393 | break; |
2394 | case GPT_GEOMETRY_PROGRAM: |
2395 | hlslPassData.geometryCode = hlslPassData.code; |
2396 | break; |
2397 | case GPT_HULL_PROGRAM: |
2398 | hlslPassData.hullCode = hlslPassData.code; |
2399 | break; |
2400 | case GPT_DOMAIN_PROGRAM: |
2401 | hlslPassData.domainCode = hlslPassData.code; |
2402 | break; |
2403 | case GPT_COMPUTE_PROGRAM: |
2404 | hlslPassData.computeCode = hlslPassData.code; |
2405 | break; |
2406 | default: |
2407 | break; |
2408 | } |
2409 | } |
2410 | } |
2411 | } |
2412 | |
2413 | outputShaderData.push_back(std::make_pair(nullptr, hlslShaderData)); |
2414 | outputShaderData.push_back(std::make_pair(nullptr, glslShaderData)); |
2415 | outputShaderData.push_back(std::make_pair(nullptr, vkslShaderData)); |
2416 | outputShaderData.push_back(std::make_pair(nullptr, mvksl)); |
2417 | } |
2418 | |
2419 | for(auto& entry : outputShaderData) |
2420 | { |
2421 | const ShaderMetaData& metaData = entry.second.metaData; |
2422 | if (metaData.isMixin) |
2423 | continue; |
2424 | |
2425 | Map<UINT32, SPtr<Pass>, std::greater<UINT32>> passes; |
2426 | for (auto& passData : entry.second.passes) |
2427 | { |
2428 | PASS_DESC passDesc; |
2429 | passDesc.blendStateDesc = passData.blendDesc; |
2430 | passDesc.rasterizerStateDesc = passData.rasterizerDesc; |
2431 | passDesc.depthStencilStateDesc = passData.depthStencilDesc; |
2432 | |
2433 | auto createProgram = |
2434 | [](const String& language, const String& entry, const String& code, GpuProgramType type) -> GPU_PROGRAM_DESC |
2435 | { |
2436 | GPU_PROGRAM_DESC desc; |
2437 | desc.language = language; |
2438 | desc.entryPoint = entry; |
2439 | desc.source = code; |
2440 | desc.type = type; |
2441 | |
2442 | return desc; |
2443 | }; |
2444 | |
2445 | bool isHLSL = metaData.language == "hlsl" ; |
2446 | passDesc.vertexProgramDesc = createProgram( |
2447 | metaData.language, |
2448 | isHLSL ? "vsmain" : "main" , |
2449 | passData.vertexCode, |
2450 | GPT_VERTEX_PROGRAM); |
2451 | |
2452 | passDesc.fragmentProgramDesc = createProgram( |
2453 | metaData.language, |
2454 | isHLSL ? "fsmain" : "main" , |
2455 | passData.fragmentCode, |
2456 | GPT_FRAGMENT_PROGRAM); |
2457 | |
2458 | passDesc.geometryProgramDesc = createProgram( |
2459 | metaData.language, |
2460 | isHLSL ? "gsmain" : "main" , |
2461 | passData.geometryCode, |
2462 | GPT_GEOMETRY_PROGRAM); |
2463 | |
2464 | passDesc.hullProgramDesc = createProgram( |
2465 | metaData.language, |
2466 | isHLSL ? "hsmain" : "main" , |
2467 | passData.hullCode, |
2468 | GPT_HULL_PROGRAM); |
2469 | |
2470 | passDesc.domainProgramDesc = createProgram( |
2471 | metaData.language, |
2472 | isHLSL ? "dsmain" : "main" , |
2473 | passData.domainCode, |
2474 | GPT_DOMAIN_PROGRAM); |
2475 | |
2476 | passDesc.computeProgramDesc = createProgram( |
2477 | metaData.language, |
2478 | isHLSL ? "csmain" : "main" , |
2479 | passData.computeCode, |
2480 | GPT_COMPUTE_PROGRAM); |
2481 | |
2482 | passDesc.stencilRefValue = passData.stencilRefValue; |
2483 | |
2484 | SPtr<Pass> pass = Pass::create(passDesc); |
2485 | if (pass != nullptr) |
2486 | passes[passData.seqIdx] = pass; |
2487 | } |
2488 | |
2489 | Vector<SPtr<Pass>> orderedPasses; |
2490 | for (auto& KVP : passes) |
2491 | orderedPasses.push_back(KVP.second); |
2492 | |
2493 | if (!orderedPasses.empty()) |
2494 | { |
2495 | SPtr<Technique> technique = Technique::create(metaData.language, metaData.tags, variation, orderedPasses); |
2496 | shaderDesc.techniques.push_back(technique); |
2497 | } |
2498 | } |
2499 | |
2500 | return output; |
2501 | } |
2502 | |
2503 | String BSLFXCompiler::removeQuotes(const char* input) |
2504 | { |
2505 | UINT32 len = (UINT32)strlen(input); |
2506 | String output(len - 2, ' '); |
2507 | |
2508 | for (UINT32 i = 0; i < (len - 2); i++) |
2509 | output[i] = input[i + 1]; |
2510 | |
2511 | return output; |
2512 | } |
2513 | } |
2514 | |