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
25extern "C" {
26#include "BsMMAlloc.h"
27
28#define YY_NO_UNISTD_H 1
29#include "BsParserFX.h"
30#include "BsLexerFX.h"
31}
32
33using namespace std;
34
35namespace 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
861cleanup:
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