private EffectBytecodeCompilerResult CompileBytecode(ShaderMixinSource mixinTree, CompilerParameters compilerParameters, ObjectId mixinObjectId, DatabaseFileProvider database, string compiledUrl, ShaderMixinParameters usedParameters) { // Open the database for writing var log = new LoggerResult(); // Note: this compiler is expected to not be async and directly write stuff in localLogger var compiledShader = base.Compile(mixinTree, compilerParameters).WaitForResult(); compiledShader.CompilationLog.CopyTo(log); // If there are any errors, return immediately if (log.HasErrors) { lock (compilingShaders) { compilingShaders.Remove(mixinObjectId); } return(new EffectBytecodeCompilerResult(null, log)); } // Compute the bytecodeId var newBytecodeId = compiledShader.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(); compiledShader.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, true); 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] = compiledShader.Bytecode; } } lock (compilingShaders) { compilingShaders.Remove(mixinObjectId); } return(compiledShader); }
private EffectBytecodeCompilerResult CompileBytecode(ShaderMixinSource mixinTree, CompilerParameters compilerParameters, ObjectId mixinObjectId, DatabaseFileProvider database, string compiledUrl, ShaderMixinParameters usedParameters) { // Open the database for writing var log = new LoggerResult(); // Note: this compiler is expected to not be async and directly write stuff in localLogger var compiledShader = base.Compile(mixinTree, compilerParameters).WaitForResult(); compiledShader.CompilationLog.CopyTo(log); // If there are any errors, return immediately if (log.HasErrors) { lock (compilingShaders) { compilingShaders.Remove(mixinObjectId); } return new EffectBytecodeCompilerResult(null, log); } // Compute the bytecodeId var newBytecodeId = compiledShader.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(); compiledShader.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, true); 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] = compiledShader.Bytecode; } } lock (compilingShaders) { compilingShaders.Remove(mixinObjectId); } return compiledShader; }
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); }