public static void Write(string name, CompilerParameters parameters, EffectBytecode effectData, TextWriter writer) { const string codeTemplate = @"//------------------------------------------------------------------------------ // <auto-generated> // Paradox Effect Compiler File Generated: {0}// // Command Line: {7} // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ namespace {1} {{ {2} class {3} {{ {4} static readonly byte[] {5} = new byte[] {{ {6} }}; }} }} "; var effectToGenerateText = new StringBuilder(); effectToGenerateText.AppendFormat("// Effect [{0}]\r\n", name); var buffer = new MemoryStream(); effectData.WriteTo(buffer); var bufferAsText = new StringBuilder(); var bufferArray = buffer.ToArray(); for (int i = 0; i < bufferArray.Length; i++) { bufferAsText.Append(bufferArray[i]).Append(", "); if (i > 0 && (i % 64) == 0) { bufferAsText.AppendLine(); } } var classDeclaration = parameters.Get(EffectSourceCodeKeys.ClassDeclaration); var fieldDeclaration = parameters.Get(EffectSourceCodeKeys.FieldDeclaration); var nameSpace = parameters.Get(EffectSourceCodeKeys.Namespace); var className = parameters.Get(EffectSourceCodeKeys.ClassName) ?? name; var fieldName = parameters.Get(EffectSourceCodeKeys.FieldName); var commandLine = string.Join(" ", Environment.GetCommandLineArgs()); var graphicsPlatform = parameters.Get(CompilerParameters.GraphicsPlatformKey); string paradoxDefine = "undefined"; switch (graphicsPlatform) { case GraphicsPlatform.Direct3D11: paradoxDefine = "SILICONSTUDIO_PARADOX_GRAPHICS_API_DIRECT3D11"; break; case GraphicsPlatform.OpenGL: paradoxDefine = "SILICONSTUDIO_PARADOX_GRAPHICS_API_OPENGLCORE"; break; case GraphicsPlatform.OpenGLES: paradoxDefine = "SILICONSTUDIO_PARADOX_GRAPHICS_API_OPENGLES"; break; } writer.WriteLine("#if {0}", paradoxDefine); writer.Write(codeTemplate, effectToGenerateText, // {0} nameSpace, // {1} classDeclaration, // {2} className, // {3} fieldDeclaration, // {4} fieldName, // {5} bufferAsText, // {6} commandLine); // {7} writer.WriteLine("#endif"); writer.Flush(); }
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); }