Example #1
0
        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);
        }