示例#1
0
 public override int GetHashCode()
 {
     unchecked
     {
         return(ShaderMixinObjectId.Compute(EffectName, UsedParameters).GetHashCode());
     }
 }
示例#2
0
 public bool Equals(EffectCompileRequest other)
 {
     if (ReferenceEquals(null, other))
     {
         return(false);
     }
     if (ReferenceEquals(this, other))
     {
         return(true);
     }
     return(string.Equals(EffectName, other.EffectName) && ShaderMixinObjectId.Compute(EffectName, UsedParameters) == ShaderMixinObjectId.Compute(other.EffectName, other.UsedParameters));
 }
示例#3
0
        public override TaskOrResult <EffectBytecodeCompilerResult> Compile(ShaderMixinSource mixin, EffectCompilerParameters effectParameters, CompilerParameters compilerParameters)
        {
            var database = FileProvider as DatabaseFileProvider ?? throw new NotSupportedException("Using the cache requires to ContentManager.FileProvider to be valid.");

            var usedParameters = compilerParameters;
            var mixinObjectId  = ShaderMixinObjectId.Compute(mixin, usedParameters.EffectParameters);

            // Final url of the compiled bytecode
            var compiledUrl = string.Format("{0}/{1}", CompiledShadersKey, mixinObjectId);

            var bytecode = new KeyValuePair <EffectBytecode, EffectBytecodeCacheLoadSource>(null, EffectBytecodeCacheLoadSource.JustCompiled);

            lock (bytecodes)
            {
                // ------------------------------------------------------------------------------------------------------------
                // 1) Try to load latest bytecode
                // ------------------------------------------------------------------------------------------------------------
                ObjectId bytecodeId;
                if (database.ContentIndexMap.TryGetValue(compiledUrl, out bytecodeId))
                {
                    bytecode = LoadEffectBytecode(database, bytecodeId);
                }

                // ------------------------------------------------------------------------------------------------------------
                // 2) Try to load from database cache
                // ------------------------------------------------------------------------------------------------------------
                if (bytecode.Key == null && database.ObjectDatabase.Exists(mixinObjectId))
                {
                    using (var stream = database.ObjectDatabase.OpenStream(mixinObjectId))
                    {
                        // We have an existing stream, make sure the shader is compiled
                        var objectIdBuffer = new byte[ObjectId.HashSize];
                        if (stream.Read(objectIdBuffer, 0, ObjectId.HashSize) == ObjectId.HashSize)
                        {
                            var newBytecodeId = new ObjectId(objectIdBuffer);
                            bytecode = LoadEffectBytecode(database, newBytecodeId);

                            if (bytecode.Key != null)
                            {
                                // If we successfully retrieved it from cache, add it to index map so that it won't be collected and available for faster lookup
                                database.ContentIndexMap[compiledUrl] = newBytecodeId;
                            }
                        }
                    }
                }
            }

            if (bytecode.Key != null)
            {
                return(new EffectBytecodeCompilerResult(bytecode.Key, bytecode.Value));
            }
            else if (Platform.IsWindowsDesktop == false || Compiler is NullEffectCompiler)
            {
                // On non Windows platform, we are expecting to have the bytecode stored directly or in database cache
                var stringBuilder = new StringBuilder();
                stringBuilder.AppendFormat("Unable to find compiled shaders [{0}] for mixin [{1}] with parameters [{2}]", compiledUrl, mixin, usedParameters.ToStringPermutationsDetailed());
                Log.Error(stringBuilder.ToString());
                throw new InvalidOperationException(stringBuilder.ToString());
            }

            // ------------------------------------------------------------------------------------------------------------
            // 3) Compile the shader
            // ------------------------------------------------------------------------------------------------------------
            lock (compilingShaders)
            {
                Task <EffectBytecodeCompilerResult> compilingShaderTask;
                if (compilingShaders.TryGetValue(mixinObjectId, out compilingShaderTask))
                {
                    // Note: Task might still be compiling
                    return(compilingShaderTask);
                }

                // Compile the mixin in a Task
                if (CompileEffectAsynchronously)
                {
                    var compilerParametersCopy = compilerParameters != null ? new CompilerParameters(compilerParameters) : null;
                    var resultTask             = Task.Factory.StartNew(() => CompileBytecode(mixin, effectParameters, compilerParametersCopy, mixinObjectId, database, compiledUrl), CancellationToken.None, TaskCreationOptions.None, taskSchedulerSelector != null ? taskSchedulerSelector(mixin, compilerParametersCopy.EffectParameters) : TaskScheduler.Default);

                    compilingShaders.Add(mixinObjectId, resultTask);

                    return(resultTask);
                }
                else
                {
                    return(CompileBytecode(mixin, effectParameters, compilerParameters, mixinObjectId, database, compiledUrl));
                }
            }
        }
示例#4
0
        public override TaskOrResult <EffectBytecodeCompilerResult> Compile(ShaderMixinSource mixin, CompilerParameters compilerParameters)
        {
            var database = (FileProvider ?? ContentManager.FileProvider) as DatabaseFileProvider;

            if (database == null)
            {
                throw new NotSupportedException("Using the cache requires to ContentManager.FileProvider to be valid.");
            }

            // Forward DatabaseFileProvider to actual compiler here
            // Since we might be in a Task, it has to be forwarded manually (otherwise MicroThreadLocal ones wouldn't work during build)
            // Note: this system might need an overhaul... (too many states?)
            base.FileProvider = database;

            var usedParameters = compilerParameters;
            var mixinObjectId  = ShaderMixinObjectId.Compute(mixin, usedParameters);

            // Final url of the compiled bytecode
            var compiledUrl = string.Format("{0}/{1}", CompiledShadersKey, mixinObjectId);

            EffectBytecode bytecode = null;

            lock (bytecodes)
            {
                // ------------------------------------------------------------------------------------------------------------
                // 1) Try to load latest bytecode
                // ------------------------------------------------------------------------------------------------------------
                ObjectId bytecodeId;
                if (database.AssetIndexMap.TryGetValue(compiledUrl, out bytecodeId))
                {
                    bytecode = LoadEffectBytecode(database, bytecodeId);
                }

                // On non Windows platform, we are expecting to have the bytecode stored directly
                if (Compiler is NullEffectCompiler && bytecode == null)
                {
                    var stringBuilder = new StringBuilder();
                    stringBuilder.AppendFormat("Unable to find compiled shaders [{0}] for mixin [{1}] with parameters [{2}]", compiledUrl, mixin, usedParameters.ToStringPermutationsDetailed());
                    Log.Error(stringBuilder.ToString());
                    throw new InvalidOperationException(stringBuilder.ToString());
                }

                // ------------------------------------------------------------------------------------------------------------
                // 2) Try to load from database cache
                // ------------------------------------------------------------------------------------------------------------
                if (bytecode == null && database.ObjectDatabase.Exists(mixinObjectId))
                {
                    using (var stream = database.ObjectDatabase.OpenStream(mixinObjectId))
                    {
                        // We have an existing stream, make sure the shader is compiled
                        var objectIdBuffer = new byte[ObjectId.HashSize];
                        if (stream.Read(objectIdBuffer, 0, ObjectId.HashSize) == ObjectId.HashSize)
                        {
                            var newBytecodeId = new ObjectId(objectIdBuffer);
                            bytecode = LoadEffectBytecode(database, newBytecodeId);

                            if (bytecode != null)
                            {
                                // If we successfully retrieved it from cache, add it to index map so that it won't be collected and available for faster lookup
                                database.AssetIndexMap[compiledUrl] = newBytecodeId;
                            }
                        }
                    }
                }
            }

            if (bytecode != null)
            {
                return(new EffectBytecodeCompilerResult(bytecode));
            }

            // ------------------------------------------------------------------------------------------------------------
            // 3) Compile the shader
            // ------------------------------------------------------------------------------------------------------------
            lock (compilingShaders)
            {
                Task <EffectBytecodeCompilerResult> compilingShaderTask;
                if (compilingShaders.TryGetValue(mixinObjectId, out compilingShaderTask))
                {
                    // Note: Task might still be compiling
                    return(compilingShaderTask);
                }

                // Compile the mixin in a Task
                if (CompileEffectAsynchronously)
                {
                    var compilerParametersCopy = new CompilerParameters(compilerParameters);
                    var resultTask             = Task.Factory.StartNew(() => CompileBytecode(mixin, compilerParametersCopy, mixinObjectId, database, compiledUrl), CancellationToken.None, TaskCreationOptions.None, taskSchedulerSelector != null ? taskSchedulerSelector(mixin, compilerParametersCopy.EffectParameters) : TaskScheduler.Default);

                    compilingShaders.Add(mixinObjectId, resultTask);

                    return(resultTask);
                }
                else
                {
                    return(CompileBytecode(mixin, compilerParameters, mixinObjectId, database, compiledUrl));
                }
            }
        }
示例#5
0
        public override EffectBytecode Compile(ShaderMixinSource mixin, string fullEffectName, ShaderMixinParameters compilerParameters, HashSet <string> modifiedShaders, HashSet <string> recentlyModifiedShaders, LoggerResult log)
        {
            var database = AssetManager.FileProvider;

            if (database == null)
            {
                throw new NotSupportedException("Using the cache requires to AssetManager.FileProvider to be valid.");
            }

            // remove the old shaders
            if (recentlyModifiedShaders != null && recentlyModifiedShaders.Count != 0)
            {
                RemoveObsoleteStoredResults(recentlyModifiedShaders);
            }

            var ids = ShaderMixinObjectId.Compute(mixin, compilerParameters);

            EffectBytecode bytecode = null;

            lock (storedResults)
            {
                if (storedResults.TryGetValue(ids.FullParametersId, out bytecode))
                {
                    return(bytecode);
                }

                // Final url of the compiled bytecode
                var compiledUrl = string.Format("{0}/{1}", CompiledShadersKey, ids.CompileParametersId);

                // ------------------------------------------------------------------------------------------------------------
                // 1) Try to load latest bytecode
                // ------------------------------------------------------------------------------------------------------------
                ObjectId bytecodeId;
                if (database.AssetIndexMap.TryGetValue(compiledUrl, out bytecodeId))
                {
                    using (var stream = database.ObjectDatabase.OpenStream(bytecodeId))
                    {
                        var localBytecode = BinarySerialization.Read <EffectBytecode>(stream);

                        // If latest bytecode is in sync
                        if (!Platform.IsWindowsDesktop || CheckBytecodeInSyncAgainstSources(localBytecode, database))
                        {
                            bytecode = localBytecode;

                            // if bytecode contains a modified shource, do not use it.
                            if (modifiedShaders != null && modifiedShaders.Count != 0 && IsBytecodeObsolete(bytecode, modifiedShaders))
                            {
                                bytecode = null;
                            }
                        }
                    }
                }

                // On non Windows platform, we are expecting to have the bytecode stored directly
                if (!Platform.IsWindowsDesktop && bytecode == null)
                {
                    Log.Error("Unable to find compiled shaders [{0}] for mixin [{1}] with parameters [{2}]", compiledUrl, mixin, compilerParameters.ToStringDetailed());
                    throw new InvalidOperationException("Unable to find compiled shaders [{0}]".ToFormat(compiledUrl));
                }

                // ------------------------------------------------------------------------------------------------------------
                // 2) Try to load from intermediate results
                // ------------------------------------------------------------------------------------------------------------
                if (bytecode == null)
                {
                    // Check if this id has already a ShaderBytecodeStore
                    var isObjectInDatabase = database.ObjectDatabase.Exists(ids.CompileParametersId);

                    // Try to load from an existing ShaderBytecode
                    if ((modifiedShaders == null || modifiedShaders.Count == 0) && isObjectInDatabase)
                    {
                        var stream = database.ObjectDatabase.OpenStream(ids.CompileParametersId, VirtualFileMode.Open, VirtualFileAccess.Read, VirtualFileShare.Read);
                        using (var resultsStore = new ShaderBytecodeStore(stream))
                        {
                            // Load new values
                            resultsStore.LoadNewValues();

                            var storedValues = resultsStore.GetValues();

                            foreach (KeyValuePair <HashSourceCollection, EffectBytecode> hashResults in storedValues)
                            {
                                if (CheckBytecodeInSyncAgainstSources(hashResults.Value, database))
                                {
                                    bytecode = hashResults.Value;
                                    break;
                                }
                            }
                        }
                    }

                    // --------------------------------------------------------------------------------------------------------
                    // 3) Bytecode was not found in the cache on disk, we need to compile it
                    // --------------------------------------------------------------------------------------------------------
                    if (bytecode == null)
                    {
                        // Open the database for writing
                        var stream = database.ObjectDatabase.OpenStream(ids.CompileParametersId, VirtualFileMode.OpenOrCreate, VirtualFileAccess.ReadWrite, VirtualFileShare.ReadWrite);
                        using (var resultsStore = new ShaderBytecodeStore(stream))
                        {
                            var localLogger = new LoggerResult();

                            // Compile the mixin
                            bytecode = base.Compile(mixin, fullEffectName, compilerParameters, modifiedShaders, recentlyModifiedShaders, localLogger);
                            log.Info("New effect compiled [{0}]\r\n{1}", ids.CompileParametersId, compilerParameters.ToStringDetailed());
                            localLogger.CopyTo(log);

                            // If there are any errors, return immediately
                            if (localLogger.HasErrors)
                            {
                                return(null);
                            }

                            // Else store the bytecode for this set of HashSources
                            resultsStore[bytecode.HashSources] = bytecode;
                        }
                    }

                    // Save latest bytecode into the storage
                    using (var stream = database.OpenStream(compiledUrl, VirtualFileMode.Create, VirtualFileAccess.Write, VirtualFileShare.Write))
                    {
                        BinarySerialization.Write(stream, bytecode);
                    }
                }
                else
                {
                    // clone the bytecode since it is not the first time we load it.
                    bytecode = bytecode.Clone();
                }

                // Store the bytecode in the memory cache
                storedResults[ids.FullParametersId] = bytecode;
            }

            return(bytecode);
        }
示例#6
0
        public override EffectBytecode Compile(ShaderMixinSourceTree mixinTree, CompilerParameters compilerParameters, LoggerResult log)
        {
            var database = AssetManager.FileProvider;

            if (database == null)
            {
                throw new NotSupportedException("Using the cache requires to AssetManager.FileProvider to be valid.");
            }

            var mixin          = mixinTree.Mixin;
            var usedParameters = mixinTree.UsedParameters;

            var mixinObjectId = ShaderMixinObjectId.Compute(mixin, usedParameters);

            // Final url of the compiled bytecode
            var compiledUrl = string.Format("{0}/{1}", CompiledShadersKey, mixinObjectId);

            EffectBytecode bytecode = null;

            lock (bytecodes)
            {
                // ------------------------------------------------------------------------------------------------------------
                // 1) Try to load latest bytecode
                // ------------------------------------------------------------------------------------------------------------
                ObjectId bytecodeId;
                if (database.AssetIndexMap.TryGetValue(compiledUrl, out bytecodeId))
                {
                    bytecode = LoadEffectBytecode(bytecodeId);
                }

                // On non Windows platform, we are expecting to have the bytecode stored directly
                if (!Platform.IsWindowsDesktop && bytecode == null)
                {
                    var stringBuilder = new StringBuilder();
                    stringBuilder.AppendFormat("Unable to find compiled shaders [{0}] for mixin [{1}] with parameters [{2}]", compiledUrl, mixin, usedParameters.ToStringDetailed());
                    Log.Error(stringBuilder.ToString());
                    throw new InvalidOperationException(stringBuilder.ToString());
                }

                // ------------------------------------------------------------------------------------------------------------
                // 2) Try to load from database cache
                // ------------------------------------------------------------------------------------------------------------
                if (bytecode == null && database.ObjectDatabase.Exists(mixinObjectId))
                {
                    using (var stream = database.ObjectDatabase.OpenStream(mixinObjectId))
                    {
                        // We have an existing stream, make sure the shader is compiled
                        var objectIdBuffer = new byte[ObjectId.HashSize];
                        if (stream.Read(objectIdBuffer, 0, ObjectId.HashSize) == ObjectId.HashSize)
                        {
                            var newBytecodeId = new ObjectId(objectIdBuffer);
                            bytecode = LoadEffectBytecode(newBytecodeId);

                            if (bytecode != null)
                            {
                                // If we successfully retrieved it from cache, add it to index map so that it won't be collected and available for faster lookup
                                database.AssetIndexMap[compiledUrl] = newBytecodeId;
                            }
                        }
                    }
                }
            }

            // ------------------------------------------------------------------------------------------------------------
            // 3) Compile the shader
            // ------------------------------------------------------------------------------------------------------------
            if (bytecode == null)
            {
                // Open the database for writing
                var localLogger = new LoggerResult();

                // Compile the mixin
                bytecode = base.Compile(mixinTree, compilerParameters, localLogger);
                localLogger.CopyTo(log);

                // If there are any errors, return immediately
                if (localLogger.HasErrors)
                {
                    return(null);
                }

                // Compute the bytecodeId
                var newBytecodeId = bytecode.ComputeId();

                // Check if we really need to store the bytecode
                lock (bytecodes)
                {
                    // Using custom serialization to the database to store an object with a custom id
                    // TODO: Check if we really need to write the bytecode everytime even if id is not changed
                    var memoryStream = new MemoryStream();
                    bytecode.WriteTo(memoryStream);
                    memoryStream.Position = 0;
                    database.ObjectDatabase.Write(memoryStream, newBytecodeId, true);
                    database.AssetIndexMap[compiledUrl] = newBytecodeId;

                    // Save bytecode Id to the database cache as well
                    memoryStream.SetLength(0);
                    memoryStream.Write((byte[])newBytecodeId, 0, ObjectId.HashSize);
                    memoryStream.Position = 0;
                    database.ObjectDatabase.Write(memoryStream, mixinObjectId);

                    if (!bytecodes.ContainsKey(newBytecodeId))
                    {
                        log.Info("New effect compiled #{0} [{1}] (db: {2})\r\n{3}", effectCompileCount, mixinObjectId, newBytecodeId, usedParameters.ToStringDetailed());
                        Interlocked.Increment(ref effectCompileCount);

                        // Replace or add new bytecode
                        bytecodes[newBytecodeId] = bytecode;
                    }
                }
            }

            return(bytecode);
        }