MemoryStream ExtractMetadataAndStream(string path)
 {
     if (ShaderFileCache.TryLoad(path, out string text))
     {
         DependencyPaths.Add(path);
         if (MetadataParsing.Parse(path, text, Stages, MacroGroups, IncludeParsingErrors))
         {
             //We write ascii into the bytes regardless of the source text so that fxc is kept happy.
             var bytes = Encoding.ASCII.GetBytes(text);
             return(new MemoryStream(bytes));
         }
     }
     return(null);
 }
        private static void CollectCompilationTargets(string source, string workingPath,
                                                      ShaderFileCache shaderFileCache, ShaderCompilationCache loadedCache, ShaderCompilationCache cache,
                                                      List <ShaderCompilationResult> errors, List <ShaderCompilationTarget> compilationTargets)
        {
            if (!shaderFileCache.TryLoad(source, out string shaderCode))
            {
                lock (errors)
                {
                    errors.Add(new ShaderCompilationResult("Shader", "", "", source, 0, 0, 0, 0, "Could not find file " + source));
                }
                return;
            }


            //Only compile this shader if the shader has been modified since the previous compile.
            var currentTimeStamp = File.GetLastWriteTime(source).Ticks;

            if (loadedCache.ShaderFlags == cache.ShaderFlags && loadedCache.TimeStamps.TryGetValue(source, out long previousTimeStamp)) //If this file was contained before, we MIGHT be able to skip its compilation.
            {
                if (currentTimeStamp <= previousTimeStamp)                                                                              //If the file hasn't been updated, we MIGHT be able to skip.
                {
                    //Check the timestamps associated with the dependencies of this file.
                    //If any are newer than the previous snapshot, skip.
                    bool allowSkip = true;
                    if (loadedCache.Dependencies.TryGetValue(source, out HashSet <string> loadedDependencyPaths))
                    {
                        foreach (var dependency in loadedDependencyPaths)
                        {
                            //The loadedCache is guaranteed to contain a time stamp for any dependency referenced,
                            //because the only time any dependency is added to the list the dependency is ALSO put into the timestamps list.
                            var previousDependencyTimeStamp = loadedCache.TimeStamps[dependency];
                            var currentDependencyTimeStamp  = File.GetLastWriteTime(dependency).Ticks;
                            if (currentDependencyTimeStamp > previousDependencyTimeStamp)
                            {
                                //One of the dependencies has been updated. This shader must be compiled.
                                allowSkip = false;
                                break;
                            }
                        }
                    }
                    else
                    {
                        //One of the dependencies could not be found in the old cache. Implies a new dependency was added; must compile.
                        allowSkip = false;
                    }

                    if (allowSkip)
                    {
                        Console.WriteLine($"Shader up to date: {Path.GetFileName(source)}");
                        cache.CopyFrom(source, loadedCache);
                        return;
                    }
                }
            }


            var localWorkingPath = Path.GetDirectoryName(source);
            var include          = new IncludeHandler(shaderFileCache, workingPath, localWorkingPath);


            var stages                = new List <ShaderStage>();
            var macroGroups           = new List <MacroGroup>();
            var metadataParsingErrors = new List <MetadataParsingError>();

            MetadataParsing.Parse(source, shaderCode, stages, macroGroups, metadataParsingErrors);
            //Prepass to collect include metadata. Seems hacky, oh well.
            try
            {
                ShaderBytecode.Preprocess(shaderCode, null, include);
            }
            catch (CompilationException e)
            {
                if (ParseCompilerResult(e.Message, out string filePath, out int lineBegin, out int columnBegin, out int lineEnd, out int columnEnd, out string description))
                {
                    var errorChecker = new Regex("error X(?<errorCode>[0-9]{4}): ");
                    var errorCode    = errorChecker.Match(e.Message).Groups["errorCode"].Value;
                    lock (errors)
                    {
                        errors.Add(new ShaderCompilationResult("Shader", errorCode, "", source, lineBegin, columnBegin, lineEnd, columnEnd, description));
                    }
                }