private LoadedShaderClassType LoadShaderClass(ShaderClassCode classSource, string generics, LoggerResult log, Stride.Core.Shaders.Parser.ShaderMacro[] macros = null) { var type = classSource.ClassName; if (type == null) { throw new ArgumentNullException("type"); } var shaderSourceKey = new ShaderSourceKey(type, generics, macros); lock (loadedShaders) { // Already instantiated LoadedShaderClassType shaderClass; if (loadedShaders.TryGetValue(shaderSourceKey, out shaderClass)) { return(shaderClass); } ShaderSourceManager.ShaderSourceWithHash shaderSource; // Load shader source code if (classSource is ShaderClassString shaderClassString) { shaderSource = SourceManager.LoadShaderSource(type, shaderClassString.ShaderSourceCode); } else { shaderSource = SourceManager.LoadShaderSource(type); } string preprocessedSource; try { preprocessedSource = PreProcessor.Run(shaderSource.Source, shaderSource.Path, macros); } catch (Exception ex) { log.Error(MessageCode.ErrorUnexpectedException, new SourceSpan(new SourceLocation(shaderSource.Path, 0, 1, 1), 1), ex); return(null); } byte[] byteArray = Encoding.UTF8.GetBytes(preprocessedSource); var hashPreprocessSource = ObjectId.FromBytes(byteArray); // Compile var parsingResult = StrideShaderParser.TryParse(preprocessedSource, shaderSource.Path); parsingResult.CopyTo(log); if (parsingResult.HasErrors) { return(null); } var shader = parsingResult.Shader; // As shaders can be embedded in namespaces, get only the shader class and make sure there is only one in a sdsl. var shaderClassTypes = StrideShaderParser.GetShaderClassTypes(shader.Declarations).ToList(); if (shaderClassTypes.Count != 1) { var sourceSpan = new SourceSpan(new SourceLocation(shaderSource.Path, 0, 0, 0), 1); if (shaderClassTypes.Count > 1) { sourceSpan = shaderClassTypes[1].Span; } log.Error(StrideMessageCode.ShaderMustContainSingleClassDeclaration, sourceSpan, type); return(null); } shaderClass = new LoadedShaderClassType(); shaderClass.Type = shaderClassTypes.First(); shaderClass.SourcePath = shaderSource.Path; shaderClass.SourceHash = shaderSource.Hash; shaderClass.PreprocessedSourceHash = hashPreprocessSource; shaderClass.IsInstanciated = false; // TODO: We should not use Console. Change the way we log things here // Console.WriteLine("Loading Shader {0}{1}", type, macros != null && macros.Length > 0 ? String.Format("<{0}>", string.Join(", ", macros)) : string.Empty); // If the file name is not matching the class name, provide an error if (shaderClass.Type.Name.Text != type) { log.Error(StrideMessageCode.FileNameNotMatchingClassName, shaderClass.Type.Name.Span, type, shaderClass.Type.Name.Text); return(null); } loadedShaders.Add(shaderSourceKey, shaderClass); return(shaderClass); } }
/// <summary> /// Loads the <see cref="ShaderClassType" />. /// </summary> /// <param name="shaderClassSource">The shader class source.</param> /// <param name="shaderMacros">The shader macros.</param> /// <param name="log">The log to output error logs.</param> /// <param name="autoGenericInstances"></param> /// <returns>A ShaderClassType or null if there was some errors.</returns> /// <exception cref="System.ArgumentNullException">shaderClassSource</exception> public LoadedShaderClassType LoadClassSource(ShaderClassCode shaderClassSource, Stride.Core.Shaders.Parser.ShaderMacro[] shaderMacros, LoggerResult log, bool autoGenericInstances) { if (shaderClassSource == null) { throw new ArgumentNullException("shaderClassSource"); } string generics = null; if (shaderClassSource.GenericArguments != null) { generics = ""; foreach (var gen in shaderClassSource.GenericArguments) { generics += "___" + gen; } } var shaderClassType = LoadShaderClass(shaderClassSource, generics, log, shaderMacros); if (shaderClassType == null) { return(null); } // Instantiate generic class if (shaderClassSource.GenericArguments != null || (shaderClassType.Type.ShaderGenerics.Count > 0 && autoGenericInstances)) { if (shaderClassType.IsInstanciated) { return(shaderClassType); } // If we want to automatically generate a generic instance (in case we just want to parse and verify the generic) if (autoGenericInstances) { shaderClassSource.GenericArguments = new string[shaderClassType.Type.ShaderGenerics.Count]; for (int i = 0; i < shaderClassSource.GenericArguments.Length; i++) { var variableGeneric = shaderClassType.Type.ShaderGenerics[i]; shaderClassSource.GenericArguments[i] = GetDefaultConstValue(variableGeneric); } } if (shaderClassSource.GenericArguments.Length != shaderClassType.Type.ShaderGenerics.Count) { log.Error(StrideMessageCode.WrongGenericNumber, shaderClassType.Type.Span, shaderClassSource.ClassName); return(null); } // check the name of the generics foreach (var generic in shaderClassType.Type.ShaderGenerics) { foreach (var genericCompare in shaderClassType.Type.ShaderGenerics.Where(x => x != generic)) { if (generic.Name.Text == genericCompare.Name.Text) { log.Error(StrideMessageCode.SameNameGenerics, generic.Span, generic, genericCompare, shaderClassSource.ClassName); } } } if (log.HasErrors) { return(null); } // When we use an actual generic instance, we replace the name with the name of the class + a hash of the generic parameters if (!autoGenericInstances) { var className = GenerateGenericClassName(shaderClassSource); shaderClassType.Type.Name = new Identifier(className); } var genericAssociation = CreateGenericAssociation(shaderClassType.Type.ShaderGenerics, shaderClassSource.GenericArguments); var identifierGenerics = GenerateIdentifierFromGenerics(genericAssociation); var expressionGenerics = GenerateGenericsExpressionValues(shaderClassType.Type.ShaderGenerics, shaderClassSource.GenericArguments); StrideClassInstantiator.Instantiate(shaderClassType.Type, expressionGenerics, identifierGenerics, autoGenericInstances, log); shaderClassType.Type.ShaderGenerics.Clear(); shaderClassType.IsInstanciated = true; } return(shaderClassType); }