public override int GetHashCode() { unchecked { return(ShaderMixinObjectId.Compute(EffectName, UsedParameters).GetHashCode()); } }
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)); }
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)); } } }
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)); } } }
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); }
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); }