private EffectPool(GraphicsDevice device, string name = null) : base(name) { dataGroup = new EffectData(); mapNameToEffect = new Dictionary<string, EffectData.Effect>(); mapNameToConstantBuffer = new Dictionary<GraphicsDevice, Dictionary<string, Dictionary<EffectConstantBufferKey, EffectConstantBuffer>>>(); compiledShaders = new List<DeviceChild>(); registered = new Dictionary<EffectData, bool>(new IdentityEqualityComparer<EffectData>()); effects = new List<Effect>(); this.graphicsDevice = device.MainDevice; constantBufferAllocator = DefaultConstantBufferAllocator; }
public void Write(EffectData effectData, TextWriter writer) { const string codeTemplate = @"//------------------------------------------------------------------------------ // <auto-generated> // SharpDX Toolkit Compiler File Generated: {0}// // 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 SharpDX.Toolkit.Graphics.EffectData {5} = SharpDX.Toolkit.Graphics.EffectData.Load(new byte[] {{ {6} }}); }} }} "; var effectToGenerateText = new StringBuilder(); foreach(var effect in effectData.Effects) { effectToGenerateText.AppendFormat("// Effect [{0}]\r\n", effect.Name); } var buffer = new MemoryStream(); effectData.Save(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(); } } writer.Write(codeTemplate, effectToGenerateText, // {0} Namespace, // {1} ClassDeclaration, // {2} ClassName, // {3} FieldDeclaration, // {4} FieldName, // {5} bufferAsText); // {6} writer.Flush(); }
internal void CreateInstanceFrom(GraphicsDevice device, EffectData effectData, EffectPool effectPool) { GraphicsDevice = device; ConstantBuffers = new EffectConstantBufferCollection(); Parameters = new EffectParameterCollection(); Techniques = new EffectTechniqueCollection(); Pool = effectPool ?? device.DefaultEffectPool; // Sets the effect name Name = effectData.Description.Name; // Register the bytecode to the pool var effect = Pool.RegisterBytecode(effectData); // Initialize from effect InitializeFrom(effect, null); // If everything was fine, then we can register it into the pool Pool.AddEffect(this); }
/// <summary> /// Registers a EffectData to this pool. /// </summary> /// <param name="data">The effect data to register.</param> /// <param name="allowOverride">if set to <c>true</c> [allow override].</param> public void RegisterBytecode(EffectData data, bool allowOverride = false) { // Lock the whole EffectPool in case multiple threads would add EffectData at the same time. lock (sync) { bool hasNewBytecode = false; if (!registered.ContainsKey(data)) { // Pre-cache all input signatures CacheInputSignature(data); dataGroup.MergeFrom(data, allowOverride); registered.Add(data, true); hasNewBytecode = true; } if (hasNewBytecode) { // Create all mapping mapNameToEffect.Clear(); foreach (var effect in dataGroup.Effects) mapNameToEffect.Add(effect.Name, effect); // Just alocate the compiled shaders array according to the currennt size of shader datas for (int i = compiledShaders.Count; i < dataGroup.Shaders.Count; i++) { compiledShaders.Add(null); } } } }
private void DumpBytecode(EffectCompiler compiler, EffectData effectData) { Console.WriteLine(); for (int i = 0; i < effectData.Shaders.Count; i++) { var shader = effectData.Shaders[i]; Color(ConsoleColor.White); Console.WriteLine("--------------------------------------------------------------------------------"); Console.WriteLine("Shader[{0}] {1}Type: {2} Level: {3} Visibility: {4} ByteCode Size: {5}", i, shader.Name == null ? string.Empty : string.Format("{0} ", shader.Name), shader.Type, shader.Level, shader.Name != null ? "public" : "private", shader.Bytecode.Length); Console.WriteLine("--------------------------------------------------------------------------------"); Console.WriteLine(); ResetColor(); DumpHtmlToConsole(compiler.DisassembleShader(shader)); Console.WriteLine(); } Color(ConsoleColor.White); Console.WriteLine("--------------------------------------------------------------------------------"); Console.WriteLine("effect {0}", effectData.Description.Name); Console.WriteLine("--------------------------------------------------------------------------------"); Console.WriteLine(); ResetColor(); const string tab = " "; var effect = effectData.Description; { Console.WriteLine("{"); foreach (var technique in effect.Techniques) { Console.Write(tab + "technique"); Color(ConsoleColor.LightGreen); Console.WriteLine(" {0}", technique.Name); ResetColor(); Console.WriteLine(tab + "{"); for (int passIndex = 0; passIndex < technique.Passes.Count; passIndex++) { var pass = technique.Passes[passIndex]; var passtab = pass.IsSubPass ? tab + tab + tab : tab + tab; Console.Write("{0}{1} #{2}", passtab, ((pass.IsSubPass) ? "subpass" : "pass"), passIndex); Color(ConsoleColor.LightGreen); Console.WriteLine(" {0}", pass.Name); ResetColor(); Console.WriteLine(passtab + "{"); for (int i = 0; i < pass.Pipeline.Links.Length; i++) { var shaderType = (EffectShaderType)i; var link = pass.Pipeline.Links[i]; if (link == null) { continue; } Color(ConsoleColor.LightYellow); Console.Write(passtab + tab + "{0}", shaderType); ResetColor(); Console.Write(" = "); Color(ConsoleColor.White); Console.WriteLine("{0}", link.IsImport ? link.ImportName : link.IsNullShader ? "null" : string.Format("Shader[{0}] => {1}", link.Index, effectData.Shaders[link.Index].Name == null ? "private" : "public " + effectData.Shaders[link.Index].Name)); ResetColor(); } if (pass.Properties.Count > 0) { Console.WriteLine(); foreach (var attribute in pass.Properties) { var typeName = attribute.Value != null?attribute.Value.GetType().FullName.StartsWith("SharpDX") ? attribute.Value.GetType().FullName : null : null; Console.WriteLine(passtab + tab + "{0} = {1}", attribute.Key, typeName == null ? attribute.Value : string.Format("{0}({1})", typeName, attribute.Value)); } } Console.WriteLine(passtab + "}"); } Console.WriteLine(tab + "}"); } Console.WriteLine("}"); } }
/// <summary> /// Disassembles a shader HLSL bytecode to asm code. /// </summary> /// <param name="shader">The shader.</param> /// <returns>A string containing asm code decoded from HLSL bytecode.</returns> public string DisassembleShader(EffectData.Shader shader) { var compiler = new EffectCompilerInternal(); return compiler.DisassembleShader(shader); }
/// <summary> /// Initializes a new instance of the <see cref="Effect"/> class with the specified bytecode effect. See remarks. /// </summary> /// <param name="device">The device.</param> /// <param name="bytecode">The bytecode to add to <see cref="GraphicsDevice.DefaultEffectPool"/>. This bytecode must contain only one effect.</param> /// <exception cref="ArgumentException">If the bytecode doesn't contain a single effect.</exception> /// <remarks> /// The effect bytecode must contain only a single effect and will be registered into the <see cref="GraphicsDevice.DefaultEffectPool"/>. /// </remarks> public Effect(GraphicsDevice device, byte[] bytecode) : this(device, EffectData.Load(bytecode)) { }
/// <summary> /// Initializes a new instance of the <see cref="Effect" /> class with the specified bytecode effect. See remarks. /// </summary> /// <param name="device">The device.</param> /// <param name="effectData">The bytecode to add to the Effect Pool. This bytecode must contain only one effect.</param> /// <param name="effectPool">The effect pool used to register the bytecode. Default is <see cref="GraphicsDevice.DefaultEffectPool"/>.</param> /// <exception cref="ArgumentException">If the bytecode doesn't contain a single effect.</exception> /// <remarks>The effect bytecode must contain only a single effect and will be registered into the <see cref="GraphicsDevice.DefaultEffectPool"/>.</remarks> public Effect(GraphicsDevice device, EffectData effectData, EffectPool effectPool = null) : base(device) { CreateInstanceFrom(device, effectData, effectPool); }
/// <summary> /// Merges an existing <see cref="EffectData" /> into this instance. /// </summary> /// <param name="source">The EffectData to merge.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> /// <remarks>This method is useful to build an archive of several effects.</remarks> private EffectData.Effect RegisterInternal(EffectData source) { var effect = source.Description; var effectRuntime = new EffectData.Effect() { Name = effect.Name, Arguments = effect.Arguments, ShareConstantBuffers = effect.ShareConstantBuffers, Techniques = new List<EffectData.Technique>(effect.Techniques.Count) }; Logger logger = null; foreach (var techniqueOriginal in effect.Techniques) { var technique = techniqueOriginal.Clone(); effectRuntime.Techniques.Add(technique); foreach (var pass in technique.Passes) { foreach (var shaderLink in pass.Pipeline) { // No shader set for this stage if (shaderLink == null) continue; // If the shader is an import, we try first to resolve it directly if (shaderLink.IsImport) { var index = FindShaderByName(shaderLink.ImportName); if (index >= 0) { shaderLink.ImportName = null; shaderLink.Index = index; } else { if (logger == null) { logger = new Logger(); } logger.Error("Cannot find shader import by name [{0}]", shaderLink.ImportName); } } else if (!shaderLink.IsNullShader) { var shader = source.Shaders[shaderLink.Index]; // Find a similar shader var shaderIndex = FindSimilarShader(shader); if (shaderIndex >= 0) { var previousShader = RegisteredShaders[shaderIndex]; // If the previous shader is if (shader.Name != null) { // if shader from this instance is local and shader from source is global => transform current shader to global if (previousShader.Name == null) { previousShader.Name = shader.Name; } else if (shader.Name != previousShader.Name) { if (logger == null) { logger = new Logger(); } // If shader from this instance is global and shader from source is global => check names. If exported names are different, this is an error logger.Error("Cannot merge shader [{0}] into this instance, as there is already a global shader with a different name [{1}]", shader.Name, previousShader.Name); } } shaderLink.Index = shaderIndex; } else { shaderLink.Index = RegisteredShaders.Count; RegisteredShaders.Add(shader); } } } } } if (logger != null && logger.HasErrors) throw new InvalidOperationException(Utilities.Join("\r\n", logger.Messages)); return effectRuntime; }
/// <summary> /// Initializes a new instance of the <see cref="EffectCompilerResult" /> class. /// </summary> /// <param name="dependencyFilePath">The path to dependency file (may be null).</param> /// <param name="effectData">The EffectData.</param> /// <param name="logger">The logger.</param> public EffectCompilerResult(string dependencyFilePath, EffectData effectData, Logger logger) { DependencyFilePath = dependencyFilePath; EffectData = effectData; Logger = logger; }
/// <summary> /// Initializes the specified effect bytecode. /// </summary> /// <param name="effectDataArg">The effect bytecode.</param> /// <exception cref="System.InvalidOperationException"></exception> private void Initialize(EffectData.Effect effectDataArg) { if (effectDataArg == null) throw new ArgumentException(string.Format("Unable to find effect [{0}] from the EffectPool", Name), "effectName"); rawEffect = effectDataArg; ShareConstantBuffers = effectDataArg.ShareConstantBuffers; // Create the local effect constant buffers cache if (!ShareConstantBuffers) effectConstantBuffersCache = new Dictionary<EffectConstantBufferKey, EffectConstantBuffer>(); if (effectDataArg.Techniques.Count == 0) throw new InvalidOperationException("No techniques found in this effect"); var logger = new Logger(); int techniqueIndex = 0; int totalPassCount = 0; EffectPass parentPass = null; foreach (var techniqueRaw in effectDataArg.Techniques) { var name = techniqueRaw.Name; if (string.IsNullOrEmpty(name)) name = string.Format("${0}", techniqueIndex++); var technique = new EffectTechnique(this, name); Techniques.Add(technique); int passIndex = 0; foreach (var passRaw in techniqueRaw.Passes) { name = passRaw.Name; if (string.IsNullOrEmpty(name)) name = string.Format("${0}", passIndex++); var pass = new EffectPass(logger, this, technique, passRaw, name); pass.Initialize(logger); // If this is a subpass, add it to the parent pass if (passRaw.IsSubPass) { if (parentPass == null) { logger.Error("Pass [{0}] is declared as a subpass but has no parent"); } else { parentPass.SubPasses.Add(pass); } } else { technique.Passes.Add(pass); parentPass = pass; } } // Count the number of passes totalPassCount += technique.Passes.Count; } if (totalPassCount == 0) throw new InvalidOperationException("No passes found in this effect"); // Log all the exception in a single throw if (logger.HasErrors) throw new InvalidOperationException(Utilities.Join("\n", logger.Messages)); // Initialize the resource linker when we are done with all pass/parameters ResourceLinker.Initialize(); //// Sort all parameters by their resource types //// in order to achieve better local cache coherency in resource linker Parameters.Items.Sort((left, right) => { // First, order first all value types, then resource type var comparison = left.IsValueType != right.IsValueType ? left.IsValueType ? -1 : 1 : 0; // If same type if (comparison == 0) { // Order by resource type comparison = ((int)left.ResourceType).CompareTo((int)right.ResourceType); // If same, order by resource index if (comparison == 0) { comparison = left.Offset.CompareTo(right.Offset); } } return comparison; }); // Prelink constant buffers int resourceIndex = 0; foreach (var parameter in Parameters) { // Recalculate parameter resource index if (!parameter.IsValueType) { parameter.Offset = resourceIndex++; } // Set the default values parameter.SetDefaultValue(); if (parameter.ResourceType == EffectResourceType.ConstantBuffer) parameter.SetResource(0, ConstantBuffers[parameter.Name]); } // Compute slot links foreach (var technique in Techniques) { foreach (var pass in technique.Passes) { foreach (var subPass in pass.SubPasses) { subPass.ComputeSlotLinks(); } pass.ComputeSlotLinks(); } } // Setup the first Current Technique. CurrentTechnique = this.Techniques[0]; }
/// <summary> /// Initializes a new instance of the <see cref="EffectCompilerResult" /> class. /// </summary> /// <param name="effectData">The EffectData.</param> /// <param name="logger">The logger.</param> internal EffectCompilerResult(EffectData effectData, Logger logger) { EffectData = effectData; Logger = logger; }
public TerrainRendererEffect(GraphicsDevice device, EffectData effectData) : base(device, effectData) { }
protected TerrainEffectBase(GraphicsDevice device, EffectData effectData) : base(device, effectData, null) { }
/// <summary> /// Initializes a new instance of the <see cref="Effect" /> class with the specified bytecode effect. See remarks. /// </summary> /// <param name="device">The device.</param> /// <param name="effectData">The bytecode to add to the Effect Pool. This bytecode must contain only one effect.</param> /// <param name="effectPool">The effect pool used to register the bytecode. Default is <see cref="GraphicsDevice.DefaultEffectPool"/>.</param> /// <exception cref="ArgumentException">If the bytecode doesn't contain a single effect.</exception> /// <remarks>The effect bytecode must contain only a single effect and will be registered into the <see cref="GraphicsDevice.DefaultEffectPool"/>.</remarks> public Effect(GraphicsDevice device, EffectData effectData, EffectPool effectPool = null) : base(device) { if (effectData.Effects.Count != 1) throw new ArgumentException(string.Format("Expecting only one effect in the effect bytecode instead of [{0}].", Utilities.Join(",", effectData.Effects)), "effectData"); ConstantBuffers = new EffectConstantBufferCollection(); Parameters = new EffectParameterCollection(); Techniques = new EffectTechniqueCollection(); Pool = effectPool ?? device.DefaultEffectPool; // Sets the effect name Name = effectData.Effects[0].Name; // Register the bytecode to the pool Pool.RegisterBytecode(effectData); // Initialize from effect InitializeFrom(Pool.Find(Name)); // If everything was fine, then we can register it into the pool Pool.AddEffect(this); }
/// <summary> /// Initializes a new instance of the <see cref="EffectCompilerResult" /> class. /// </summary> /// <param name="effectData">The EffectData.</param> /// <param name="logger">The logger.</param> internal EffectCompilerResult(bool isUpToDate, EffectData effectData, Logger logger) { IsUpToDate = isUpToDate; EffectData = effectData; Logger = logger; }
private void DumpBytecode(EffectData effectData) { Console.WriteLine(); for (int i = 0; i < effectData.Shaders.Count; i++) { var shader = effectData.Shaders[i]; Color(ConsoleColor.White); Console.WriteLine("--------------------------------------------------------------------------------"); Console.WriteLine("Shader[{0}] {1}Type: {2} Level: {3} Visibility: {4} ByteCode Size: {5}", i, shader.Name == null ? string.Empty : string.Format("{0} ", shader.Name), shader.Type, shader.Level, shader.Name != null ? "public" : "private", shader.Bytecode.Length); Console.WriteLine("--------------------------------------------------------------------------------"); Console.WriteLine(); ResetColor(); DumpHtmlToConsole(EffectCompiler.DisassembleShader(shader)); Console.WriteLine(); } Color(ConsoleColor.White); Console.WriteLine("--------------------------------------------------------------------------------"); Console.WriteLine("Effects"); Console.WriteLine("--------------------------------------------------------------------------------"); Console.WriteLine(); ResetColor(); const string tab = " "; foreach (var effect in effectData.Effects) { Console.Write("effect"); Color(ConsoleColor.LightGreen); Console.WriteLine(" {0}", effect.Name); ResetColor(); Console.WriteLine("{"); foreach (var technique in effect.Techniques) { Console.Write(tab + "technique"); Color(ConsoleColor.LightGreen); Console.WriteLine(" {0}", technique.Name); ResetColor(); Console.WriteLine(tab + "{"); for (int passIndex = 0; passIndex < technique.Passes.Count; passIndex++) { var pass = technique.Passes[passIndex]; var passtab = pass.IsSubPass ? tab + tab + tab : tab + tab; Console.Write("{0}{1} #{2}", passtab, ((pass.IsSubPass) ? "subpass" : "pass"), passIndex); Color(ConsoleColor.LightGreen); Console.WriteLine(" {0}", pass.Name); ResetColor(); Console.WriteLine(passtab + "{"); for (int i = 0; i < pass.Pipeline.Links.Length; i++) { var shaderType = (EffectShaderType) i; var link = pass.Pipeline.Links[i]; if (link == null) continue; Color(ConsoleColor.LightYellow); Console.Write(passtab + tab + "{0}", shaderType); ResetColor(); Console.Write(" = "); Color(ConsoleColor.White); Console.WriteLine("{0}", link.IsImport ? link.ImportName : link.IsNullShader ? "null" : string.Format("Shader[{0}] => {1}", link.Index, effectData.Shaders[link.Index].Name == null ? "private" : "public " + effectData.Shaders[link.Index].Name)); ResetColor(); } if (pass.Attributes.Count > 0) { Console.WriteLine(); foreach (var attribute in pass.Attributes) { var typeName = attribute.Value != null ? attribute.Value.GetType().FullName.StartsWith("SharpDX") ? attribute.Value.GetType().FullName : null : null; Console.WriteLine(passtab + tab + "{0} = {1}", attribute.Name, typeName == null ? attribute.Value : string.Format("{0}({1})", typeName, attribute.Value)); } } Console.WriteLine(passtab + "}"); } Console.WriteLine(tab + "}"); } Console.WriteLine("}"); } }
/// <summary> /// Initializes a new instance of the <see cref="Effect" /> class with the specified bytecode effect. See remarks. /// </summary> /// <param name="device">The device.</param> /// <param name="effectData">The bytecode to add to <see cref="GraphicsDevice.DefaultEffectPool"/>. This bytecode must contain only one effect.</param> /// <exception cref="ArgumentException">If the bytecode doesn't contain a single effect.</exception> /// <remarks> /// The effect bytecode must contain only a single effect and will be registered into the <see cref="GraphicsDevice.DefaultEffectPool"/>. /// </remarks> public Effect(GraphicsDevice device, EffectData effectData) : base(device) { if (effectData.Effects.Count != 1) throw new ArgumentException(string.Format("Expecting only one effect in the effect bytecode instead of [{0}]. Use GraphicsDevice.DefaultEffectPool.RegisterBytecode instead", Utilities.Join(",", effectData.Effects)), "bytecode"); ConstantBuffers = new EffectConstantBufferCollection(); Parameters = new EffectParameterCollection(); Techniques = new EffectTechniqueCollection(); ResourceLinker = ToDispose(new EffectResourceLinker()); Pool = device.DefaultEffectPool; // Sets the effect name Name = effectData.Effects[0].Name; // Register the bytecode to the pool Pool.RegisterBytecode(effectData); Initialize(); }
internal EffectConstantBuffer GetOrCreateConstantBuffer(GraphicsDevice context, EffectData.ConstantBuffer bufferRaw) { // Only lock the constant buffer object lock (mapNameToConstantBuffer) { Dictionary<string, Dictionary<EffectConstantBufferKey, EffectConstantBuffer>> nameToConstantBufferList; // ---------------------------------------------------------------------------- // 1) Get the cache of constant buffers for a particular GraphicsDevice // ---------------------------------------------------------------------------- // TODO cache is not clear if a GraphicsDevice context is disposed // To simplify, we assume that a GraphicsDevice is alive during the whole life of the application. if (!mapNameToConstantBuffer.TryGetValue(context, out nameToConstantBufferList)) { nameToConstantBufferList = new Dictionary<string, Dictionary<EffectConstantBufferKey, EffectConstantBuffer>>(); mapNameToConstantBuffer[context] = nameToConstantBufferList; } // ---------------------------------------------------------------------------- // 2) Get a set of constant buffers for a particular constant buffer name // ---------------------------------------------------------------------------- Dictionary<EffectConstantBufferKey, EffectConstantBuffer> bufferSet; if (!nameToConstantBufferList.TryGetValue(bufferRaw.Name, out bufferSet)) { bufferSet = new Dictionary<EffectConstantBufferKey, EffectConstantBuffer>(); nameToConstantBufferList[bufferRaw.Name] = bufferSet; } // ---------------------------------------------------------------------------- // 3) Get an existing constant buffer having the same name/size/layout/parameters // ---------------------------------------------------------------------------- var bufferKey = new EffectConstantBufferKey(bufferRaw); EffectConstantBuffer buffer; if (!bufferSet.TryGetValue(bufferKey, out buffer)) { // 4) If this buffer doesn't exist, create a new one and register it. buffer = new EffectConstantBuffer(graphicsDevice, bufferRaw); bufferSet[bufferKey] = ToDispose(buffer); } return buffer; } }
private int FindSimilarShader(EffectData.Shader shader) { for (int i = 0; i < RegisteredShaders.Count; i++) { if (RegisteredShaders[i].IsSimilar(shader)) return i; } return -1; }
/// <summary> /// Binds the specified effect data to this instance. /// </summary> /// <param name="effectDataArg">The effect data arg.</param> /// <param name="cloneFromEffect">The clone from effect.</param> /// <exception cref="System.InvalidOperationException">If no techniques found in this effect.</exception> /// <exception cref="System.ArgumentException">If unable to find effect [effectName] from the EffectPool.</exception> internal void InitializeFrom(EffectData.Effect effectDataArg, Effect cloneFromEffect) { RawEffectData = effectDataArg; // Clean any previously allocated resources if (DisposeCollector != null) { DisposeCollector.DisposeAndClear(); } ConstantBuffers.Clear(); Parameters.Clear(); Techniques.Clear(); ResourceLinker = ToDispose(new EffectResourceLinker()); if (effectConstantBuffersCache != null) { effectConstantBuffersCache.Clear(); } // Copy data IsSupportingDynamicCompilation = RawEffectData.Arguments != null; ShareConstantBuffers = RawEffectData.ShareConstantBuffers; // Create the local effect constant buffers cache if (!ShareConstantBuffers) effectConstantBuffersCache = new Dictionary<EffectConstantBufferKey, EffectConstantBuffer>(); var logger = new Logger(); int techniqueIndex = 0; int totalPassCount = 0; EffectPass parentPass = null; foreach (var techniqueRaw in RawEffectData.Techniques) { var name = techniqueRaw.Name; if (string.IsNullOrEmpty(name)) name = string.Format("${0}", techniqueIndex++); var technique = new EffectTechnique(this, name); Techniques.Add(technique); int passIndex = 0; foreach (var passRaw in techniqueRaw.Passes) { name = passRaw.Name; if (string.IsNullOrEmpty(name)) name = string.Format("${0}", passIndex++); var pass = new EffectPass(logger, this, technique, passRaw, name); pass.Initialize(logger); // If this is a subpass, add it to the parent pass if (passRaw.IsSubPass) { if (parentPass == null) { logger.Error("Pass [{0}] is declared as a subpass but has no parent."); } else { parentPass.SubPasses.Add(pass); } } else { technique.Passes.Add(pass); parentPass = pass; } } // Count the number of passes totalPassCount += technique.Passes.Count; } if (totalPassCount == 0) throw new InvalidOperationException("No passes found in this effect."); // Log all the exception in a single throw if (logger.HasErrors) throw new InvalidOperationException(Utilities.Join("\n", logger.Messages)); // Initialize the resource linker when we are done with all pass/parameters ResourceLinker.Initialize(); //// Sort all parameters by their resource types //// in order to achieve better local cache coherency in resource linker Parameters.Items.Sort((left, right) => { // First, order first all value types, then resource type var comparison = left.IsValueType != right.IsValueType ? left.IsValueType ? -1 : 1 : 0; // If same type if (comparison == 0) { // Order by resource type comparison = ((int)left.ResourceType).CompareTo((int)right.ResourceType); // If same, order by resource index if (comparison == 0) { comparison = left.Offset.CompareTo(right.Offset); } } return comparison; }); // Prelink constant buffers int resourceIndex = 0; foreach (var parameter in Parameters) { // Recalculate parameter resource index if (!parameter.IsValueType) { parameter.Offset = resourceIndex++; } // Set the default values parameter.SetDefaultValue(); if (parameter.ResourceType == EffectResourceType.ConstantBuffer) parameter.SetResource(ConstantBuffers[parameter.Name]); } // Compute slot links foreach (var technique in Techniques) { foreach (var pass in technique.Passes) { foreach (var subPass in pass.SubPasses) { subPass.ComputeSlotLinks(); } pass.ComputeSlotLinks(); } } // Setup the first Current Technique. CurrentTechnique = this.Techniques[0]; // Initialize predefined parameters used by Model.Draw (to speedup things internally) DefaultParameters = new EffectDefaultParameters(this); // If this is a clone, we need to if (cloneFromEffect != null) { // Copy the content of the constant buffers to the new instance. for (int i = 0; i < ConstantBuffers.Count; i++) { cloneFromEffect.ConstantBuffers[i].CopyTo(ConstantBuffers[i]); } // Copy back all bound resources except constant buffers // that are already initialized with InitializeFrom method. for (int i = 0; i < cloneFromEffect.ResourceLinker.Count; i++) { if (cloneFromEffect.ResourceLinker.BoundResources[i] is EffectConstantBuffer) continue; ResourceLinker.BoundResources[i] = cloneFromEffect.ResourceLinker.BoundResources[i]; unsafe { ResourceLinker.Pointers[i] = cloneFromEffect.ResourceLinker.Pointers[i]; } } // If everything was fine, then we can register it into the pool Pool.AddEffect(this); } // Allow subclasses to complete initialization. Initialize(); }
internal EffectConstantBuffer GetOrCreateConstantBuffer(GraphicsDevice context, EffectData.ConstantBuffer bufferRaw) { EffectConstantBuffer constantBuffer; // Is the effect is using shared constant buffers via the EffectPool? if (ShareConstantBuffers) { // Use the pool to share constant buffers constantBuffer = Pool.GetOrCreateConstantBuffer(context, bufferRaw); } else { // ---------------------------------------------------------------------------- // Get an existing constant buffer having the same name/sizel/ayout/parameters // ---------------------------------------------------------------------------- var bufferKey = new EffectConstantBufferKey(bufferRaw); if (!effectConstantBuffersCache.TryGetValue(bufferKey, out constantBuffer)) { // 4) If this buffer doesn't exist, create a new one and register it. constantBuffer = new EffectConstantBuffer(context, bufferRaw); effectConstantBuffersCache.Add(bufferKey, constantBuffer); DisposeCollector.Collect(constantBuffer); } } return constantBuffer; }
/// <summary> /// Merges an existing <see cref="EffectData" /> into this instance. /// </summary> /// <param name="source">The EffectData to merge.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> /// <remarks>This method is useful to build an archive of several effects.</remarks> private EffectData.Effect RegisterInternal(EffectData source) { var effect = source.Description; var effectRuntime = new EffectData.Effect() { Name = effect.Name, Arguments = effect.Arguments, ShareConstantBuffers = effect.ShareConstantBuffers, Techniques = new List <EffectData.Technique>(effect.Techniques.Count) }; Logger logger = null; foreach (var techniqueOriginal in effect.Techniques) { var technique = techniqueOriginal.Clone(); effectRuntime.Techniques.Add(technique); foreach (var pass in technique.Passes) { foreach (var shaderLink in pass.Pipeline) { // No shader set for this stage if (shaderLink == null) { continue; } // If the shader is an import, we try first to resolve it directly if (shaderLink.IsImport) { var index = FindShaderByName(shaderLink.ImportName); if (index >= 0) { shaderLink.ImportName = null; shaderLink.Index = index; } else { if (logger == null) { logger = new Logger(); } logger.Error("Cannot find shader import by name [{0}]", shaderLink.ImportName); } } else if (!shaderLink.IsNullShader) { var shader = source.Shaders[shaderLink.Index]; // Find a similar shader var shaderIndex = FindSimilarShader(shader); if (shaderIndex >= 0) { var previousShader = RegisteredShaders[shaderIndex]; // If the previous shader is if (shader.Name != null) { // if shader from this instance is local and shader from source is global => transform current shader to global if (previousShader.Name == null) { previousShader.Name = shader.Name; } else if (shader.Name != previousShader.Name) { if (logger == null) { logger = new Logger(); } // If shader from this instance is global and shader from source is global => check names. If exported names are different, this is an error logger.Error("Cannot merge shader [{0}] into this instance, as there is already a global shader with a different name [{1}]", shader.Name, previousShader.Name); } } shaderLink.Index = shaderIndex; } else { shaderLink.Index = RegisteredShaders.Count; RegisteredShaders.Add(shader); } } } } } if (logger != null && logger.HasErrors) { throw new InvalidOperationException(Utilities.Join("\r\n", logger.Messages)); } return(effectRuntime); }
/// <summary> /// Merges an existing <see cref="EffectData" /> into this instance. /// </summary> /// <param name="source">The EffectData to merge.</param> /// <param name="allowOverride">if set to <c>true</c> [allow override].</param> /// <exception cref="System.InvalidOperationException"></exception> /// <exception cref="InvalidOperationException">If the merge failed.</exception> /// <remarks>This method is useful to build an archive of several effects.</remarks> public void MergeFrom(EffectData source, bool allowOverride = false) { var logger = new Logger(); if (!MergeFrom(source, logger, allowOverride)) throw new InvalidOperationException(Utilities.Join("\r\n",logger.Messages)); }
void Run(string[] args) { var assemblyUri = new Uri(Assembly.GetEntryAssembly().CodeBase); var assemblyPath = Path.GetDirectoryName(assemblyUri.LocalPath); var newPath = Path.GetFullPath(Path.Combine(assemblyPath, @"..\Redist\D3D\" + (IntPtr.Size == 4 ? "x86" : "x64"))) + ";" + Environment.GetEnvironmentVariable("PATH"); Environment.SetEnvironmentVariable("PATH", newPath); // Print the exe header PrintHeader(); // Parse the command line if (!ParseCommandLine(args)) { Environment.Exit(-1); } var options = this; // ---------------------------------------------------------------- // Process macros // ---------------------------------------------------------------- var macros = new List <EffectData.ShaderMacro>(); foreach (var define in options.Defines) { var nameValue = define.Split('='); string name = nameValue[0]; string value = null; if (nameValue.Length > 1) { value = nameValue[1]; } macros.Add(new EffectData.ShaderMacro(name, value)); } // ---------------------------------------------------------------- // Setup compiler flags // ---------------------------------------------------------------- var flags = EffectCompilerFlags.None; if (options.Debug) { flags |= EffectCompilerFlags.Debug; } switch (options.OptimizationLevel) { case 0: flags |= EffectCompilerFlags.OptimizationLevel0; break; case 1: flags |= EffectCompilerFlags.OptimizationLevel1; break; case 2: flags |= EffectCompilerFlags.OptimizationLevel2; break; case 3: flags |= EffectCompilerFlags.OptimizationLevel3; break; } if (options.PackRowMajor) { flags |= EffectCompilerFlags.PackMatrixRowMajor; } if (options.PackColumnMajor) { flags |= EffectCompilerFlags.PackMatrixColumnMajor; } if (options.AvoidFlowControl) { flags |= EffectCompilerFlags.AvoidFlowControl; } if (options.PreferFlowControl) { flags |= EffectCompilerFlags.PreferFlowControl; } if (options.EnableStrictness) { flags |= EffectCompilerFlags.EnableStrictness; } if (options.EnableBackwardsCompatibility) { flags |= EffectCompilerFlags.EnableBackwardsCompatibility; } if (options.IeeeStrictness) { flags |= EffectCompilerFlags.IeeeStrictness; } hasErrors = false; // ---------------------------------------------------------------- // Process each fx files / tkfxo files // ---------------------------------------------------------------- var fxFile = options.FxFile; var filePath = Path.Combine(Environment.CurrentDirectory, fxFile); // Check that input file exists if (!File.Exists(filePath)) { ErrorColor(); Console.Error.WriteLine("File [{0}] does not exist", fxFile); ResetColor(); Abort(); } // ---------------------------------------------------------------- // Pre check files // ---------------------------------------------------------------- if (options.OutputClassFile == null && options.OutputFile == null) { options.OutputFile = Path.GetFileNameWithoutExtension(options.FxFile) + ".tkb"; } // Check for output files bool outputFileExist = options.OutputClassFile != null && File.Exists(options.OutputClassFile); if (options.OutputFile != null && !File.Exists(options.OutputFile)) { outputFileExist = false; } // New Compiler var compiler = new EffectCompiler(); string outputDependencyDirPath = Path.Combine(Environment.CurrentDirectory, OutputDependencyDirectory); string outputDependencyFilePath = Path.Combine(outputDependencyDirPath, compiler.GetDependencyFileNameFromSourcePath(options.FxFile)); if (AllowDynamicCompiling) { CompileOnlyIfNewer = true; } if (CompileOnlyIfNewer) { if (!compiler.CheckForChanges(outputDependencyFilePath) && outputFileExist) { Console.Error.WriteLine("Nothing to compile. Output file [{0}] is up-to-date", options.OutputFile); Environment.Exit(0); } } var viewOnly = false; // Try to load this file as a precompiled file var effectData = EffectData.Load(fxFile); EffectCompilerResult compilerResult = null; if (effectData != null) { Console.WriteLine("Load Compiled File [{0}]", fxFile); viewOnly = true; } else { // Compile the fx file Console.WriteLine("Compile Effect File [{0}]", filePath); compilerResult = compiler.Compile(File.ReadAllText(filePath), filePath, flags, macros, options.IncludeDirs, AllowDynamicCompiling, CompileOnlyIfNewer ? outputDependencyFilePath : null); // If there is any warning, errors, turn Error color on if (compilerResult.Logger.Messages.Count > 0) { ErrorColor(); } // Show a message error for the current file if (compilerResult.HasErrors) { Console.Error.WriteLine("Error when compiling file [{0}]:", fxFile); hasErrors = true; } // Print all messages (warning and errors) foreach (var logMessage in compilerResult.Logger.Messages) { Console.WriteLine(logMessage); } // If we have some messages, reset the color back if (compilerResult.Logger.Messages.Count > 0) { ResetColor(); } effectData = compilerResult.EffectData; } if (!NoDisassembly && effectData != null) { DumpBytecode(compiler, effectData); } if (hasErrors) { Abort(); } if (!viewOnly) { Console.WriteLine(); if (CompileOnlyIfNewer && compilerResult.DependencyFilePath != null) { // Dependency file save to Console.WriteLine("Save dependency list to [{0}]", outputDependencyFilePath); } if (OutputClassFile != null) { var codeWriter = new EffectDataCodeWriter { Namespace = OutputNamespace, ClassName = OutputClassname ?? Path.GetFileNameWithoutExtension(OutputClassFile), FieldName = OutputFieldName, }; Console.WriteLine("Save C# code output to [{0}]", OutputClassFile); using (var stream = new NativeFileStream(OutputClassFile, NativeFileMode.Create, NativeFileAccess.Write, NativeFileShare.Write)) codeWriter.Write(effectData, new StreamWriter(stream, Encoding.UTF8)); } if (options.OutputFile != null) { Console.WriteLine("Save binary output to [{0}]", options.OutputFile); // Save the result effectData.Save(options.OutputFile); } } }
private bool CompareResourceParameter(EffectData.ResourceParameter left, EffectData.ResourceParameter right) { return (left.Class != right.Class || left.Type != right.Type || left.Count != right.Count); }
public EffectToRebind(Effect effect, EffectData effectData) { Effect = effect; EffectData = effectData; }
protected ViewerEffectBase(GraphicsDevice device, EffectData effectData) : base(device, effectData, null) { }
/// <summary> /// Merges an existing <see cref="EffectData" /> into this instance. /// </summary> /// <param name="source">The EffectData to merge.</param> /// <param name="logger">Logger used to report merging errors.</param> /// <param name="allowOverride">if set to <c>true</c> [allow override].</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> /// <remarks>This method is useful to build an archive of several effects.</remarks> public bool MergeFrom(EffectData source, Logger logger, bool allowOverride = false) { bool isMergeOk = true; foreach (var effect in source.Effects) { bool effectAlreadyRegistered = false; // Add effect that is not already in the archive with this same name. int previousEffectIndex = 0; for (; previousEffectIndex < Effects.Count; previousEffectIndex++) { var effect2 = Effects[previousEffectIndex]; if (effect2.Name == effect.Name) { effectAlreadyRegistered = true; break; } } if (effectAlreadyRegistered) { if (allowOverride) { Effects[previousEffectIndex] = effect; } else { // Skip the effect it it is already registered continue; } } else { Effects.Add(effect); } foreach (var technique in effect.Techniques) { foreach (var pass in technique.Passes) { foreach (var shaderLink in pass.Pipeline) { if (shaderLink == null) continue; if (shaderLink.IsImport) { // If this is an import, we try first to resolve it directly // Else we keep the name as-is var index = FindShaderByName(shaderLink.ImportName); if (index >= 0) { shaderLink.ImportName = null; shaderLink.Index = index; } } else if (!shaderLink.IsNullShader) { var shader = source.Shaders[shaderLink.Index]; var index = FindShader(shader); if (index >= 0) { var previousShader = Shaders[index]; if (shader.Name != null) { // if shader from this instance is local and shader from source is global => transform current shader to global if (previousShader.Name == null) { previousShader.Name = shader.Name; } else if (shader.Name != previousShader.Name) { // If shader from this instance is global and shader from source is global => check names. If exported names are different, this is an error logger.Error("Cannot merge shader [{0}] into this instance, as there is already a global shader with a different name [{1}]", shader.Name, previousShader.Name); isMergeOk = false; } } shaderLink.Index = index; } else { shaderLink.Index = Shaders.Count; Shaders.Add(shader); } } } } } } return isMergeOk; }
public object ReadContent(IContentManager readerManager, ref ContentReaderParameters parameters) { parameters.KeepStreamOpen = false; return(EffectData.Load(parameters.Stream)); }
/// <summary> /// Initializes a new instance of the <see cref="EffectPass" /> class. /// </summary> /// <param name="logger">The logger used to log errors.</param> /// <param name="effect"> The effect. </param> /// <param name="pass"> The pass. </param> /// <param name="name"> The name. </param> internal EffectPass(Logger logger, Effect effect, EffectTechnique technique, EffectData.Pass pass, string name) : base(name) { this.Technique = technique; this.pass = pass; this.Effect = effect; this.graphicsDevice = effect.GraphicsDevice; pipeline = new PipelineBlock() { Stages = new StageBlock[EffectPass.StageCount], }; Attributes = PrepareAttributes(logger, pass.Attributes); IsSubPass = pass.IsSubPass; // Don't create SubPasses collection for subpass. if (!IsSubPass) SubPasses = new EffectPassCollection(); }
/// <summary> /// Registers a EffectData to this pool. /// </summary> /// <param name="data">The effect data to register.</param> /// <returns>The effect description.</returns> public EffectData.Effect RegisterBytecode(EffectData data) { // Lock the whole EffectPool in case multiple threads would add EffectData at the same time. lock (sync) { EffectData.Effect effect; if (!registered.TryGetValue(data, out effect)) { // Pre-cache all input signatures CacheInputSignature(data); effect = RegisterInternal(data); registered.Add(data, effect); // Just allocate the compiled shaders array according to the current size of shader data foreach (var compiledShaders in compiledShadersGroup) { for (int i = compiledShaders.Count; i < RegisteredShaders.Count; i++) { compiledShaders.Add(null); } } } return effect; } }
private bool ProcessBytecode(EffectData input, EffectData merged) { var hasErrors = false; if (!NoDisassembly) DumpBytecode(input); var logger = new Logger(); merged.MergeFrom(input, logger); // If there is any errors from if (logger.HasErrors) { hasErrors = true; ErrorColor(); foreach (var message in logger.Messages) Console.Error.WriteLine(message); ResetColor(); } return hasErrors; }
private void CacheInputSignature(EffectData effectData) { // Iterate on all vertex shaders and make unique the bytecode // for faster comparison when creating input layout. foreach (var shader in effectData.Shaders) { if (shader.Type == EffectShaderType.Vertex && shader.InputSignature.Bytecode != null) { var inputSignature = graphicsDevice.GetOrCreateInputSignatureManager(shader.InputSignature.Bytecode, shader.InputSignature.Hashcode); shader.InputSignature.Bytecode = inputSignature.Bytecode; } } }
void Run(string[] args) { var assemblyUri = new Uri(Assembly.GetEntryAssembly().CodeBase); var assemblyPath = Path.GetDirectoryName(assemblyUri.LocalPath); var newPath = Path.GetFullPath(Path.Combine(assemblyPath, @"..\Redist\D3D\" + (IntPtr.Size == 4 ? "x86" : "x64"))) + ";" + Environment.GetEnvironmentVariable("PATH"); Environment.SetEnvironmentVariable("PATH", newPath); // Print the exe header PrintHeader(); // Parse the command line if (!ParseCommandLine(args)) Environment.Exit(-1); var options = this; // ---------------------------------------------------------------- // Process macros // ---------------------------------------------------------------- var macros = new List<ShaderMacro>(); foreach (var define in options.Defines) { var nameValue = define.Split('='); string name = nameValue[0]; string value = null; if (nameValue.Length > 1) { value = nameValue[1]; } macros.Add(new ShaderMacro(name, value)); } // ---------------------------------------------------------------- // Setup compiler flags // ---------------------------------------------------------------- var flags = EffectCompilerFlags.None; if (options.Debug) flags |= EffectCompilerFlags.Debug; switch (options.OptimizationLevel) { case 0: flags |= EffectCompilerFlags.OptimizationLevel0; break; case 1: flags |= EffectCompilerFlags.OptimizationLevel1; break; case 2: flags |= EffectCompilerFlags.OptimizationLevel2; break; case 3: flags |= EffectCompilerFlags.OptimizationLevel3; break; } if (options.PackRowMajor) flags |= EffectCompilerFlags.PackMatrixRowMajor; if (options.PackColumnMajor) flags |= EffectCompilerFlags.PackMatrixColumnMajor; var archiveBytecode = new EffectData(); bool hasErrors = false; bool isFileUpToDate = CompileOnlyIfNewer; // ---------------------------------------------------------------- // Pre check files // ---------------------------------------------------------------- var outputTime = new DateTime(); var outputFileName = OutputClassFile ?? OutputFile; if (!ViewOnly && CompileOnlyIfNewer) { if (OutputClassFile != null) { if (File.Exists(OutputClassFile)) outputTime = File.GetLastWriteTime(OutputClassFile); } else if (OutputFile != null) { if (File.Exists(OutputFile)) outputTime = File.GetLastWriteTime(OutputFile); } else { ErrorColor(); Console.WriteLine("Missing /Fo OutputFile or /Fc OutputClassFile"); ResetColor(); Environment.Exit(-1); } // If the assembly is more recent than the ouput file, then regenerate it var assemblyTime = File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location); if (assemblyTime > outputTime) { isFileUpToDate = false; } else { // Else check that all files are not more recent. foreach (var fxFile in options.FxFiles) { var filePath = Path.Combine(Environment.CurrentDirectory, fxFile); if (!File.Exists(filePath)) { isFileUpToDate = false; break; } if (File.GetLastWriteTime(filePath) > outputTime) { isFileUpToDate = false; break; } } } } if (!ViewOnly && isFileUpToDate) { Console.WriteLine("Nothing to compile, output file is up to date [{0}]", outputFileName); Environment.Exit(0); } // ---------------------------------------------------------------- // Process each fx files / tkfxo files // ---------------------------------------------------------------- foreach (var fxFile in options.FxFiles) { var filePath = Path.Combine(Environment.CurrentDirectory, fxFile); if (!File.Exists(filePath)) { ErrorColor(); Console.Error.WriteLine("File [{0}] does not exist", fxFile); ResetColor(); hasErrors = true; continue; } EffectData effectData = null; // Try to load this file as a precompiled file effectData = EffectData.Load(fxFile); if (effectData != null) { Console.WriteLine("Load Compiled File [{0}]", fxFile); } else { // Compile the fx file Console.WriteLine("Compile Effect File [{0}]", filePath); var effectBytecode = EffectCompiler.Compile(File.ReadAllText(filePath), filePath, flags, macros, options.IncludeDirs); // If there is any warning, errors, turn Error color on if (effectBytecode.Logger.Messages.Count > 0) { ErrorColor(); } // Show a message error for the current file if (effectBytecode.HasErrors) { Console.Error.WriteLine("Error when compiling file [{0}]:", fxFile); hasErrors = true; } // Print all messages (warning and errors) foreach (var logMessage in effectBytecode.Logger.Messages) { Console.WriteLine(logMessage); } // If we have some messages, reset the color back if (effectBytecode.Logger.Messages.Count > 0) { ResetColor(); } effectData = effectBytecode.EffectData; } // If there is no errors, merge the result to the final archive if (!hasErrors) { if (ProcessBytecode(effectData, archiveBytecode)) hasErrors = true; } } if (hasErrors) { ErrorColor(); Console.Error.WriteLine("Compilation has errors. Process aborted."); ResetColor(); Environment.Exit(-1); } else if (!ViewOnly) { Console.WriteLine(); if (OutputClassFile != null) { var codeWriter = new EffectDataCodeWriter { Namespace = OutputNamespace, ClassName = OutputClassname ?? Path.GetFileNameWithoutExtension(OutputClassFile), FieldName = OutputFieldName, }; Console.WriteLine("Save C# code output to [{0}]", OutputClassFile); using (var stream = new NativeFileStream(OutputClassFile, NativeFileMode.Create, NativeFileAccess.Write, NativeFileShare.Write)) codeWriter.Write(archiveBytecode, new StreamWriter(stream, Encoding.UTF8)); } else { Console.WriteLine("Save output to [{0}]", options.OutputFile); // Save the result archiveBytecode.Save(options.OutputFile); } } }
public object ReadContent(IContentManager readerManager, string assetName, Stream stream, out bool keepStreamOpen, object options) { keepStreamOpen = false; return(EffectData.Load(stream)); }