static private StringBuilder Build(VFXContext context, string templatePath, VFXCompilationMode compilationMode, VFXContextCompiledData contextData, HashSet <string> dependencies)
        {
            if (!context.SetupCompilation())
            {
                return(null);
            }
            var stringBuilder = GetFlattenedTemplateContent(templatePath, new List <string>(), context.additionalDefines, dependencies);

            var allCurrentAttributes = context.GetData().GetAttributes().Where(a =>
                                                                               (context.GetData().IsCurrentAttributeUsed(a.attrib, context)) ||
                                                                               (context.contextType == VFXContextType.Init && context.GetData().IsAttributeStored(a.attrib))); // In init, needs to declare all stored attributes for intialization

            var allSourceAttributes = context.GetData().GetAttributes().Where(a => (context.GetData().IsSourceAttributeUsed(a.attrib, context)));

            var globalDeclaration = new VFXShaderWriter();

            globalDeclaration.WriteCBuffer(contextData.uniformMapper, "parameters");
            globalDeclaration.WriteLine();
            globalDeclaration.WriteAttributeStruct(allCurrentAttributes.Select(a => a.attrib), "Attributes");
            globalDeclaration.WriteLine();
            globalDeclaration.WriteAttributeStruct(allSourceAttributes.Select(a => a.attrib), "SourceAttributes");
            globalDeclaration.WriteLine();
            globalDeclaration.WriteTexture(contextData.uniformMapper);

            var linkedEventOut = context.allLinkedOutputSlot.Where(s => ((VFXModel)s.owner).GetFirstOfType <VFXContext>().CanBeCompiled()).ToList();

            globalDeclaration.WriteEventBuffer(eventListOutName, linkedEventOut.Count);

            //< Block processor
            var blockFunction     = new VFXShaderWriter();
            var blockCallFunction = new VFXShaderWriter();
            var blockDeclared     = new HashSet <string>();
            var expressionToName  = context.GetData().GetAttributes().ToDictionary(o => new VFXAttributeExpression(o.attrib) as VFXExpression, o => (new VFXAttributeExpression(o.attrib)).GetCodeString(null));

            expressionToName = expressionToName.Union(contextData.uniformMapper.expressionToCode).ToDictionary(s => s.Key, s => s.Value);

            int cpt = 0;

            foreach (var current in context.activeFlattenedChildrenWithImplicit)
            {
                BuildBlock(contextData, linkedEventOut, blockFunction, blockCallFunction, blockDeclared, expressionToName, current, ref cpt);
            }

            //< Final composition
            var    renderTemplatePipePath = VFXLibrary.currentSRPBinder.templatePath;
            var    renderRuntimePipePath  = VFXLibrary.currentSRPBinder.runtimePath;
            string renderPipeCommon       = context.doesIncludeCommonCompute ? "Packages/com.unity.visualeffectgraph/Shaders/Common/VFXCommonCompute.hlsl" : VFXLibrary.currentSRPBinder.runtimePath + "/VFXCommon.hlsl";
            string renderPipePasses       = null;

            if (!context.codeGeneratorCompute && !string.IsNullOrEmpty(renderTemplatePipePath))
            {
                renderPipePasses = renderTemplatePipePath + "/VFXPasses.template";
            }

            var globalIncludeContent = new VFXShaderWriter();

            globalIncludeContent.WriteLine("#define NB_THREADS_PER_GROUP 64");
            globalIncludeContent.WriteLine("#define HAS_ATTRIBUTES 1");
            globalIncludeContent.WriteLine("#define VFX_PASSDEPTH_ACTUAL (0)");
            globalIncludeContent.WriteLine("#define VFX_PASSDEPTH_MOTION_VECTOR (1)");
            globalIncludeContent.WriteLine("#define VFX_PASSDEPTH_SELECTION (2)");
            globalIncludeContent.WriteLine("#define VFX_PASSDEPTH_SHADOW (3)");

            foreach (var attribute in allCurrentAttributes)
            {
                globalIncludeContent.WriteLineFormat("#define VFX_USE_{0}_{1} 1", attribute.attrib.name.ToUpper(CultureInfo.InvariantCulture), "CURRENT");
            }
            foreach (var attribute in allSourceAttributes)
            {
                globalIncludeContent.WriteLineFormat("#define VFX_USE_{0}_{1} 1", attribute.attrib.name.ToUpper(CultureInfo.InvariantCulture), "SOURCE");
            }

            foreach (var additionnalHeader in context.additionalDataHeaders)
            {
                globalIncludeContent.WriteLine(additionnalHeader);
            }

            foreach (var additionnalDefine in context.additionalDefines)
            {
                globalIncludeContent.WriteLineFormat("#define {0} 1", additionnalDefine);
            }

            if (renderPipePasses != null)
            {
                globalIncludeContent.Write(GetFlattenedTemplateContent(renderPipePasses, new List <string>(), context.additionalDefines, dependencies));
            }

            if (context.GetData() is ISpaceable)
            {
                var spaceable = context.GetData() as ISpaceable;
                globalIncludeContent.WriteLineFormat("#define {0} 1", spaceable.space == VFXCoordinateSpace.World ? "VFX_WORLD_SPACE" : "VFX_LOCAL_SPACE");
            }
            globalIncludeContent.WriteLineFormat("#include \"{0}/VFXDefines.hlsl\"", renderRuntimePipePath);

            var perPassIncludeContent = new VFXShaderWriter();

            perPassIncludeContent.WriteLine("#include \"" + renderPipeCommon + "\"");
            perPassIncludeContent.WriteLine("#include \"Packages/com.unity.visualeffectgraph/Shaders/VFXCommon.hlsl\"");

            // Per-block includes
            var includes = Enumerable.Empty <string>();

            foreach (var block in context.activeFlattenedChildrenWithImplicit)
            {
                includes = includes.Concat(block.includes);
            }
            var uniqueIncludes = new HashSet <string>(includes);

            foreach (var includePath in uniqueIncludes)
            {
                perPassIncludeContent.WriteLine(string.Format("#include \"{0}\"", includePath));
            }


            ReplaceMultiline(stringBuilder, "${VFXGlobalInclude}", globalIncludeContent.builder);
            ReplaceMultiline(stringBuilder, "${VFXGlobalDeclaration}", globalDeclaration.builder);
            ReplaceMultiline(stringBuilder, "${VFXPerPassInclude}", perPassIncludeContent.builder);
            ReplaceMultiline(stringBuilder, "${VFXGeneratedBlockFunction}", blockFunction.builder);
            ReplaceMultiline(stringBuilder, "${VFXProcessBlocks}", blockCallFunction.builder);

            var mainParameters = contextData.gpuMapper.CollectExpression(-1).ToArray();

            foreach (var match in GetUniqueMatches("\\${VFXLoadParameter:{(.*?)}}", stringBuilder.ToString()))
            {
                var str            = match.Groups[0].Value;
                var pattern        = match.Groups[1].Value;
                var loadParameters = GenerateLoadParameter(pattern, mainParameters, expressionToName);
                ReplaceMultiline(stringBuilder, str, loadParameters.builder);
            }
            var additionalInterpolantsGeneration  = new VFXShaderWriter();
            var additionalInterpolantsDeclaration = new VFXShaderWriter();
            var additionalInterpolantsPreparation = new VFXShaderWriter();


            int normSemantic = 0;

            foreach (string fragmentParameter in context.fragmentParameters)
            {
                var filteredNamedExpression = mainParameters.FirstOrDefault(o => fragmentParameter == o.name &&
                                                                            !(expressionToName.ContainsKey(o.exp) && expressionToName[o.exp] == o.name)); // if parameter already in the global scope, there's nothing to do

                if (filteredNamedExpression.exp != null)
                {
                    additionalInterpolantsDeclaration.WriteDeclaration(filteredNamedExpression.exp.valueType, filteredNamedExpression.name, $"NORMAL{normSemantic++}");
                    additionalInterpolantsGeneration.WriteVariable(filteredNamedExpression.exp.valueType, filteredNamedExpression.name + "__", "0");
                    var expressionToNameLocal = new Dictionary <VFXExpression, string>(expressionToName);
                    additionalInterpolantsGeneration.EnterScope();
                    {
                        if (!expressionToNameLocal.ContainsKey(filteredNamedExpression.exp))
                        {
                            additionalInterpolantsGeneration.WriteVariable(filteredNamedExpression.exp, expressionToNameLocal);
                            additionalInterpolantsGeneration.WriteLine();
                        }
                        additionalInterpolantsGeneration.WriteAssignement(filteredNamedExpression.exp.valueType, filteredNamedExpression.name + "__", expressionToNameLocal[filteredNamedExpression.exp]);
                        additionalInterpolantsGeneration.WriteLine();
                    }
                    additionalInterpolantsGeneration.ExitScope();
                    additionalInterpolantsGeneration.WriteAssignement(filteredNamedExpression.exp.valueType, "o." + filteredNamedExpression.name, filteredNamedExpression.name + "__");
                    additionalInterpolantsPreparation.WriteVariable(filteredNamedExpression.exp.valueType, filteredNamedExpression.name, "i." + filteredNamedExpression.name);
                }
            }
            ReplaceMultiline(stringBuilder, "${VFXAdditionalInterpolantsGeneration}", additionalInterpolantsGeneration.builder);
            ReplaceMultiline(stringBuilder, "${VFXAdditionalInterpolantsDeclaration}", additionalInterpolantsDeclaration.builder);
            ReplaceMultiline(stringBuilder, "${VFXAdditionalInterpolantsPreparation}", additionalInterpolantsPreparation.builder);

            //< Compute sourceIndex
            if (stringBuilder.ToString().Contains("${VFXComputeSourceIndex}"))
            {
                var r = GenerateComputeSourceIndex(context);
                ReplaceMultiline(stringBuilder, "${VFXComputeSourceIndex}", r.builder);
            }

            //< Load Attribute
            if (stringBuilder.ToString().Contains("${VFXLoadAttributes}"))
            {
                var loadAttributes = GenerateLoadAttribute(".*", context);
                ReplaceMultiline(stringBuilder, "${VFXLoadAttributes}", loadAttributes.builder);
            }

            foreach (var match in GetUniqueMatches("\\${VFXLoadAttributes:{(.*?)}}", stringBuilder.ToString()))
            {
                var str            = match.Groups[0].Value;
                var pattern        = match.Groups[1].Value;
                var loadAttributes = GenerateLoadAttribute(pattern, context);
                ReplaceMultiline(stringBuilder, str, loadAttributes.builder);
            }

            //< Store Attribute
            if (stringBuilder.ToString().Contains("${VFXStoreAttributes}"))
            {
                var storeAttribute = GenerateStoreAttribute(".*", context, (uint)linkedEventOut.Count);
                ReplaceMultiline(stringBuilder, "${VFXStoreAttributes}", storeAttribute.builder);
            }

            foreach (var match in GetUniqueMatches("\\${VFXStoreAttributes:{(.*?)}}", stringBuilder.ToString()))
            {
                var str             = match.Groups[0].Value;
                var pattern         = match.Groups[1].Value;
                var storeAttributes = GenerateStoreAttribute(pattern, context, (uint)linkedEventOut.Count);
                ReplaceMultiline(stringBuilder, str, storeAttributes.builder);
            }

            foreach (var addionalReplacement in context.additionalReplacements)
            {
                ReplaceMultiline(stringBuilder, addionalReplacement.Key, addionalReplacement.Value.builder);
            }

            // Replace defines
            SubstituteMacros(stringBuilder);

            if (VFXViewPreference.advancedLogs)
            {
                Debug.LogFormat("GENERATED_OUTPUT_FILE_FOR : {0}\n{1}", context.ToString(), stringBuilder.ToString());
            }

            context.EndCompilation();
            return(stringBuilder);
        }