| 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 | |