//FORMAT OF SHADER CONTENT ARCHIVE:
        //int32 holding shader flags
        //Count of path-shaders stored in int32
        //{Contiguous list of path-shaders}
        //
        //Where each element is represented as:
        //Path name
        //Shader bytecode size in bytes (int64)
        //Shader bytecode
        //
        //Count of path-timestamps stored in int32
        //{Contiguous list of path-timestamps}
        //
        //Where each element is represented as:
        //Path name
        //timestamp
        //
        //Count of path-dependencies stored in int32
        //{Contiguous list of path-dependencies}
        //
        //Where each element is represented as:
        //Path name
        //number of dependencies
        //[list of dependency paths]
        //
        //Where each Unique element name is represented by a UTF-16 string, stored as:
        //Character count (int32)
        //{Characters}


        public static void Save(ShaderCompilationCache cache, string path)
        {
            using (var archiveFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                Save(cache, archiveFileStream);
            }
        }
 public void CopyFrom(string source, ShaderCompilationCache loadedCache)
 {
     lock (CompiledShaders)
     {
         //Just try every stage. Could be smarter about this, but nah.
         foreach (var stage in MetadataParsing.Stages)
         {
             //The old cached data is still valid. Copy it over into the new cache and skip.
             //Note that there may be multiple compiled shaders due to shader permutations.
             string sourceWithExtension = source + stage.Extension;
             var    existingShaders     = from pair in loadedCache.CompiledShaders
                                          where pair.Key.Name == sourceWithExtension
                                          select pair;
             foreach (var entry in existingShaders)
             {
                 CompiledShaders.Add(entry.Key, entry.Value);
             }
         }
         TimeStamps[source] = loadedCache.TimeStamps[source];
         var dependencies = new HashSet <string>(loadedCache.Dependencies[source]);
         foreach (var dependency in dependencies)
         {
             TimeStamps[dependency] = loadedCache.TimeStamps[dependency];
         }
         Dependencies[source] = dependencies;
     }
 }
 public static bool TryLoad(string path, out ShaderCompilationCache cache)
 {
     if (!File.Exists(path))
     {
         cache = null;
         return(false);
     }
     using (var stream = File.OpenRead(path))
     {
         return(TryLoad(stream, out cache));
     }
 }
        public static void Save(ShaderCompilationCache cache, Stream outputStream)
        {
            //Save the number of shaders.
            using (var writer = new BinaryWriter(outputStream))
            {
                writer.Write((int)cache.ShaderFlags);
                writer.Write(cache.CompiledShaders.Count);

                //Save every path-shader in sequence.
                foreach (var element in cache.CompiledShaders)
                {
                    //Write the element's name.
                    writer.Write(element.Key.Name);
                    writer.Write(element.Key.Defines.Length);
                    for (int i = 0; i < element.Key.Defines.Length; ++i)
                    {
                        writer.Write(element.Key.Defines[i]);
                    }

                    //Write the size of the shader bytecode.
                    writer.Write(element.Value.Data.Length);
                    //Write the bytecode itself.
                    writer.Write(element.Value.Data, 0, element.Value.Data.Length);
                }

                //Save the number of timestamped references.
                //Note that timestamps and dependencies do not have define permutations. Timestamps and dependencies do not change with respect to permutations.
                writer.Write(cache.TimeStamps.Count);

                foreach (var element in cache.TimeStamps)
                {
                    writer.Write(element.Key);

                    //Write the current time stamp onto the element.
                    writer.Write(element.Value);
                }

                writer.Write(cache.Dependencies.Count);
                foreach (var pathDependenciesPair in cache.Dependencies)
                {
                    writer.Write(pathDependenciesPair.Key);
                    writer.Write(pathDependenciesPair.Value.Count);
                    foreach (var dependency in pathDependenciesPair.Value)
                    {
                        writer.Write(dependency);
                    }
                }
            }
        }
        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));
                    }
                }
        public static bool TryLoad(Stream stream, out ShaderCompilationCache cache)
        {
            using (var reader = new BinaryReader(stream))
            {
                try
                {
                    cache = new ShaderCompilationCache((ShaderFlags)reader.ReadInt32());

                    byte[] data = new byte[16384];

                    var shaderDataCount = reader.ReadInt32();
                    for (int i = 0; i < shaderDataCount; ++i)
                    {
                        var shaderSource = SourceShader.Read(reader);

                        //Read the size in bytes of the content element data itself.
                        int sizeInBytes = reader.ReadInt32();

                        if (data.Length < sizeInBytes)
                        {
                            data = new byte[sizeInBytes];
                        }

                        reader.Read(data, 0, sizeInBytes);

                        ShaderBytecode bytecode;
                        unsafe
                        {
                            fixed(byte *buffer = data)
                            {
                                bytecode = new ShaderBytecode(new IntPtr(buffer), sizeInBytes);
                            }
                        }
                        cache.CompiledShaders.Add(shaderSource, bytecode);
                    }

                    var timeStampCount = reader.ReadInt32();
                    for (int i = 0; i < timeStampCount; ++i)
                    {
                        var shaderSource = reader.ReadString();

                        //Read the time stamp.
                        long timeStamp = reader.ReadInt64();
                        cache.TimeStamps.Add(shaderSource, timeStamp);
                    }

                    var dependenciesCount = reader.ReadInt32();
                    for (int i = 0; i < dependenciesCount; ++i)
                    {
                        var shaderSource          = reader.ReadString();
                        var pathDependenciesCount = reader.ReadInt32();
                        var dependencies          = new HashSet <string>();
                        for (int j = 0; j < pathDependenciesCount; ++j)
                        {
                            dependencies.Add(reader.ReadString());
                        }
                        cache.Dependencies.Add(shaderSource, dependencies);
                    }
                }
                catch
                {
                    cache = null;
                    return(false);
                }
                return(true);
            }
        }