Пример #1
0
        private static bool BuildEffect(MaterialGroupBuilderContext builderContext, string version, out byte[] effectCode, out byte[] hash)
        {
            var shaderCode     = GetShaderCodeForProfile(builderContext, version);
            var compiledEffect = EffectCompiler.Compile(shaderCode);

            if (compiledEffect != null)
            {
                effectCode = compiledEffect;
                hash       = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(shaderCode));
                return(true);
            }
            effectCode = null;
            hash       = null;
            return(false);
        }
Пример #2
0
        internal static string GetShaderCodeForProfile(MaterialGroupBuilderContext builderContext, string profile)
        {
            var builder = new StringBuilder();

            builder.Append(GetShaderCodeBody(builderContext, "VS", "PS"));

            builder.AppendLine();
            builder.AppendLine
            (
                "technique Default " + Environment.NewLine +
                "{" + Environment.NewLine +
                "   pass" + Environment.NewLine +
                "   {" + Environment.NewLine +
                "       VertexShader = compile vs_" + profile + " VS();" + Environment.NewLine +
                "       PixelShader = compile ps_" + profile + " PS();" + Environment.NewLine +
                "   }" + Environment.NewLine +
                "}" + Environment.NewLine
            );

            return(builder.ToString());
        }
Пример #3
0
        internal static string GetShaderCodeBody(MaterialGroupBuilderContext builderContext, string vsName, string psName)
        {
            var builder = new StringBuilder();

            foreach (var materialPart in builderContext.MaterialPartDeclarations)
            {
                builder.AppendLine(materialPart.ToString());
                builder.AppendLine();
            }

            builder.Append(string.Concat("void ", vsName, "("));
            builder.Append(string.Join(", ", builderContext.VertexShaderInputs.Select(arg => arg.ToString())
                                       .Concat(builderContext.VertexShaderOutputs.Select(arg => arg.ToString()))
                                       .Concat(builderContext.VertexShaderOutputSemanticMapping.Select(p =>
                                                                                                       string.Concat("out ", p.Key.Type, " o_", p.Key.Name, ":", p.Value)))));
            builder.AppendLine(")");
            builder.AppendLine("{");
            foreach (var vsi in builderContext.VertexShaderInputs)
            {
                if (vsi.In && vsi.Out)
                {
                    builder.AppendLine(string.Concat("    ", vsi.Name, " = ", vsi.Name, ";"));
                }
            }
            builder.AppendLine();
            foreach (var arg in builderContext.ArgumentDictionary)
            {
                if (!builderContext.VertexShaderInputs.Any(vsi => vsi.Name == arg.Key) && !builderContext.VertexShaderOutputs.Any(vso => vso.Name == arg.Key))
                {
                    builder.AppendLine(string.Concat("    ", arg.Value.Type, " ", arg.Value.Name, " = ", arg.Value.DefaultValue, ";"));
                }
            }
            builder.AppendLine();
            foreach (var materialPart in builderContext.MaterialPartDeclarations)
            {
                if (materialPart.VertexShader != null)
                {
                    builder.AppendLine(materialPart.VertexShader.ToInvokeString(string.Concat("_", materialPart.Index)));
                }
            }
            builder.AppendLine();
            foreach (var p in builderContext.VertexShaderOutputSemanticMapping)
            {
                builder.AppendLine(string.Concat("    o_", p.Key.Name, " = ",
                                                 p.Key.Name.EndsWith("_pos0_") ? p.Key.Name.Replace("_pos0_", "") : p.Key.Name, ";"));
            }
            builder.AppendLine("}");

            builder.AppendLine();

            builder.Append(string.Concat("void ", psName, "("));
            builder.Append(string.Join(", ", builderContext.PixelShaderInputs.Select(arg => arg.ToString())
                                       .Concat(builderContext.PixelShaderOutputs.Select(arg =>
                                                                                        string.IsNullOrEmpty(arg.Semantic) ? string.Concat("out ", arg.Type, " ", arg.Name)
                                                                            : string.Concat("out ", arg.Type, " ", arg.Name, ":", arg.Semantic)))));
            builder.AppendLine(")");
            builder.AppendLine("{");
            foreach (var psi in builderContext.TemporaryPixelShaderVariables)
            {
                if (string.IsNullOrEmpty(psi.DefaultValue))
                {
                    builder.AppendLine(string.Concat("    ", psi.Type, " ", psi.Name, ";"));
                }
                else
                {
                    builder.AppendLine(string.Concat("    ", psi.Type, " ", psi.Name, " = ", psi.DefaultValue, ";"));
                }
            }
            builder.AppendLine();
            foreach (var psi in builderContext.PixelShaderInputs)
            {
                if (psi.In && psi.Out)
                {
                    builder.AppendLine(string.Concat("    ", psi.Name, " = ", psi.Name, ";"));
                }
            }
            builder.AppendLine();
            foreach (var arg in builderContext.ArgumentDictionary)
            {
                if (!builderContext.PixelShaderInputs.Any(psi => psi.Name == arg.Key) &&
                    !builderContext.PixelShaderOutputs.Any(t => t.Name == arg.Key) &&
                    !builderContext.TemporaryPixelShaderVariables.Any(t => t.Name == arg.Key))
                {
                    builder.AppendLine(string.Concat("    ", arg.Value.Type, " ", arg.Value.Name, " = ", arg.Value.DefaultValue, ";"));
                }
            }
            builder.AppendLine();
            foreach (var materialPart in builderContext.MaterialPartDeclarations)
            {
                if (materialPart.PixelShader != null)
                {
                    builder.AppendLine(materialPart.PixelShader.ToInvokeString(string.Concat("_", materialPart.Index)));
                }
            }
            builder.AppendLine("}");
            return(builder.ToString());
        }
Пример #4
0
        internal static MaterialGroupBuilderContext CreateMaterialGroupBuilderContext(IList <MaterialPart> materialParts, MaterialUsage usage, bool simplify)
        {
            var builderContext = new MaterialGroupBuilderContext();

            // Step 1: Parse material part declarations from input material group.
            builderContext.MaterialPartDeclarations = new List <MaterialPartDeclaration>();
            foreach (var part in materialParts)
            {
                var code = part.GetShaderCode(usage);
                if (!string.IsNullOrEmpty(code))
                {
                    var newPart = new Lexer(code).Read();
                    newPart.MaterialPart = part;
                    builderContext.MaterialPartDeclarations.Add(newPart);
                }
            }

            builderContext.ArgumentDictionary = new Dictionary <string, ArgumentDeclaration>();

            foreach (var arg in (from part in builderContext.MaterialPartDeclarations select part.VertexShader).Concat(
                         from part in builderContext.MaterialPartDeclarations select part.PixelShader).OfType <FunctionDeclaration>()
                     .SelectMany(f => f.Arguments))
            {
                ArgumentDeclaration argument;
                if (builderContext.ArgumentDictionary.TryGetValue(arg.Name, out argument))
                {
                    if (argument.Type != arg.Type)
                    {
                        throw new InvalidOperationException(string.Format("Paramter {0} has two different types {1}, {2}", arg.Name, argument, arg.Type));
                    }

                    if (!string.IsNullOrEmpty(argument.Semantic))
                    {
                        if (!string.IsNullOrEmpty(arg.Semantic) && arg.Semantic != argument.Semantic)
                        {
                            throw new InvalidOperationException(string.Format("Paramter {0} has two different sementics {1}, {2}", arg.Name, argument, arg.Semantic));
                        }
                        arg.Semantic = argument.Semantic;
                    }
                    else if (!string.IsNullOrEmpty(arg.Semantic))
                    {
                        argument.Semantic = arg.Semantic;
                    }

                    if (!string.IsNullOrEmpty(argument.DefaultValue))
                    {
                        if (!string.IsNullOrEmpty(arg.DefaultValue) && arg.DefaultValue != argument.DefaultValue)
                        {
                            throw new InvalidOperationException(string.Format("Paramter {0} has two different default value {1}, {2}", arg.Name, argument, arg.DefaultValue));
                        }
                        arg.DefaultValue = argument.DefaultValue;
                    }
                    else if (!string.IsNullOrEmpty(arg.DefaultValue))
                    {
                        argument.DefaultValue = arg.DefaultValue;
                    }
                }
                else
                {
                    builderContext.ArgumentDictionary.Add(arg.Name, arg);
                }
            }

            foreach (var arg in builderContext.ArgumentDictionary.Values)
            {
                arg.DefaultValue = arg.DefaultValue ?? "0";
            }


            // Step 2: Figure out the dependencies of material parts based on function input arguments
            Regex validPixelShaderOutputSemantic;
            Regex validVertexShaderOutputSemantic;

            validPixelShaderOutputSemantic  = new Regex("^COLOR[0-9]$");
            validVertexShaderOutputSemantic = new Regex("^(COLOR[0-9]+)|(POSITION[0-9]+)|(TEXCOORD[0-9]+)$");

            var psOutputSemantics = new List <string>();
            var vsOutputSemantics = new List <string>();

            for (int i = builderContext.MaterialPartDeclarations.Count - 1; i >= 0; i--)
            {
                var part = builderContext.MaterialPartDeclarations[i];
                part.Index = i;
                part.MaterialPart.ParameterSuffix = string.Concat("_", i);

                if (part.VertexShader != null)
                {
                    var vsOutputArgs = part.VertexShader.Arguments.Where(a => a != null && a.Semantic != null && a.Out && validVertexShaderOutputSemantic.IsMatch(a.Semantic)).Select(a => a.Semantic).ToArray();
                    if (vsOutputArgs.Length > 0 && !vsOutputSemantics.Intersect(vsOutputArgs).Any())
                    {
                        vsOutputSemantics.AddRange(vsOutputArgs);
                        part.IsVertexShaderOutput = true;
                    }
                }

                if (part.PixelShader != null)
                {
                    var psOutputArgs = part.PixelShader.Arguments.Where(a => a != null && a.Semantic != null && a.Out && validPixelShaderOutputSemantic.IsMatch(a.Semantic)).Select(a => a.Semantic).ToArray();
                    if (psOutputArgs.Length > 0 && !psOutputSemantics.Intersect(psOutputArgs).Any())
                    {
                        psOutputSemantics.AddRange(psOutputArgs);
                        part.IsPixelShaderOutput = true;
                    }
                }
            }

            foreach (var materialDeclaration in builderContext.MaterialPartDeclarations)
            {
                materialDeclaration.Dependencies =
                    (from part in builderContext.MaterialPartDeclarations
                     where part != materialDeclaration && part.VertexShader != null &&
                     part.VertexShader.Arguments.Any(arg => arg.Out && (!arg.In || part.Index < materialDeclaration.Index) &&
                                                     materialDeclaration.VertexShader != null && materialDeclaration.VertexShader.Arguments.Any(a => a.In && a.Name == arg.Name))
                     select part).Concat
                        (from part in builderContext.MaterialPartDeclarations
                        where part != materialDeclaration && part.PixelShader != null &&
                        part.PixelShader.Arguments.Any(arg => arg.Out && (!arg.In || part.Index < materialDeclaration.Index) &&
                                                       materialDeclaration.PixelShader != null && materialDeclaration.PixelShader.Arguments.Any(a => a.In && a.Name == arg.Name))
                        select part).Concat
                        (from part in builderContext.MaterialPartDeclarations
                        where part != materialDeclaration && part.VertexShader != null &&
                        part.VertexShader.Arguments.Any(arg => arg.Out &&
                                                        materialDeclaration.PixelShader != null && materialDeclaration.PixelShader.Arguments.Any(a => a.In && a.Name == arg.Name))
                        select part).ToArray();
            }

            // Step 3: Dependency sorting
            int[] order = new int[builderContext.MaterialPartDeclarations.Count];
            DependencyGraph.Sort(builderContext.MaterialPartDeclarations, order, new MaterialPartDeclarationDependencyProvider());
            builderContext.MaterialPartDeclarations = order.Select(i => builderContext.MaterialPartDeclarations[i]).ToList();

            // Remove pixel shader parts that don't have a path to the pixel shader output
            foreach (var part in Enumerable.Reverse(builderContext.MaterialPartDeclarations))
            {
                if (!simplify || part.IsPixelShaderOutput || part.Tagged || (part.PixelShader != null && part.PixelShader.ContainsClip))
                {
                    part.Tagged = true;
                    foreach (var d in part.Dependencies)
                    {
                        d.Tagged = true;
                    }
                }
            }

            builderContext.MaterialPartDeclarations = (from part in builderContext.MaterialPartDeclarations where part.Tagged || part.PixelShader == null select part).ToList();

            // Step 4: Get shader input/output argument semantics
            var argumentEqualtyComparer = new ArgumentDeclarationEqualyComparer();

            builderContext.VertexShaderInputs  = new List <ArgumentDeclaration>();
            builderContext.VertexShaderOutputs = new List <ArgumentDeclaration>();

            for (int i = 0; i < builderContext.MaterialPartDeclarations.Count; ++i)
            {
                if (builderContext.MaterialPartDeclarations[i].VertexShader != null)
                {
                    builderContext.VertexShaderInputs.AddRange(from arg in builderContext.MaterialPartDeclarations[i].VertexShader.Arguments
                                                               where arg.In && !builderContext.MaterialPartDeclarations.Take(i).Select(p => p.VertexShader)
                                                               .OfType <FunctionDeclaration>().SelectMany(f => f.Arguments)
                                                               .Any(a => a.Out && a.Name == arg.Name)
                                                               select arg);

                    builderContext.VertexShaderOutputs.AddRange(from arg in builderContext.MaterialPartDeclarations[i].VertexShader.Arguments
                                                                where arg.Out && !builderContext.MaterialPartDeclarations.Skip(i + 1).Select(p => p.VertexShader)
                                                                .OfType <FunctionDeclaration>().SelectMany(f => f.Arguments)
                                                                .Any(a => a.In && a.Name == arg.Name)
                                                                select arg);
                }
            }

            builderContext.PixelShaderInputs  = new List <ArgumentDeclaration>();
            builderContext.PixelShaderOutputs = new List <ArgumentDeclaration>();

            for (int i = 0; i < builderContext.MaterialPartDeclarations.Count; ++i)
            {
                if (builderContext.MaterialPartDeclarations[i].PixelShader != null)
                {
                    builderContext.PixelShaderInputs.AddRange(from arg in builderContext.MaterialPartDeclarations[i].PixelShader.Arguments
                                                              where arg.In && !builderContext.MaterialPartDeclarations.Take(i).Select(p => p.PixelShader)
                                                              .OfType <FunctionDeclaration>().SelectMany(f => f.Arguments)
                                                              .Any(a => a.Out && a.Name == arg.Name)
                                                              select arg);

                    builderContext.PixelShaderOutputs.AddRange(from arg in builderContext.MaterialPartDeclarations[i].PixelShader.Arguments
                                                               where arg.Out && !builderContext.MaterialPartDeclarations.Skip(i + 1).Select(p => p.PixelShader)
                                                               .OfType <FunctionDeclaration>().SelectMany(f => f.Arguments)
                                                               .Any(a => a.In && a.Name == arg.Name)
                                                               select arg);
                }
            }

            builderContext.PixelShaderInputs   = builderContext.PixelShaderInputs.Distinct(argumentEqualtyComparer).ToList();
            builderContext.PixelShaderOutputs  = builderContext.PixelShaderOutputs.Distinct(argumentEqualtyComparer).ToList();
            builderContext.VertexShaderInputs  = builderContext.VertexShaderInputs.Distinct(argumentEqualtyComparer).ToList();
            builderContext.VertexShaderOutputs = builderContext.VertexShaderOutputs.Distinct(argumentEqualtyComparer).ToList();

            builderContext.PixelShaderInputs.ForEach(a => a.DefaultValue   = builderContext.ArgumentDictionary[a.Name].DefaultValue);
            builderContext.PixelShaderOutputs.ForEach(a => a.DefaultValue  = builderContext.ArgumentDictionary[a.Name].DefaultValue);
            builderContext.VertexShaderInputs.ForEach(a => a.DefaultValue  = builderContext.ArgumentDictionary[a.Name].DefaultValue);
            builderContext.VertexShaderOutputs.ForEach(a => a.DefaultValue = builderContext.ArgumentDictionary[a.Name].DefaultValue);

            // Step 5: Argument simplification and validation
            builderContext.TemporaryPixelShaderVariables = (from arg in builderContext.PixelShaderOutputs
                                                            where simplify && (arg.Semantic == null || !validPixelShaderOutputSemantic.IsMatch(arg.Semantic)) &&
                                                            !builderContext.PixelShaderInputs.Any(psi => psi.Name == arg.Name)
                                                            select arg).ToList();

            // Remove duplicated pixel shader output semantics, keep only the last one.
            for (int i = 0; i < builderContext.PixelShaderOutputs.Count; ++i)
            {
                if (builderContext.PixelShaderOutputs.Skip(i + 1).Any(a => a.Semantic == builderContext.PixelShaderOutputs[i].Semantic))
                {
                    builderContext.TemporaryPixelShaderVariables.Add(builderContext.PixelShaderOutputs[i]);
                    foreach (var psi in builderContext.PixelShaderInputs.Where(p => p.Name == builderContext.PixelShaderOutputs[i].Name))
                    {
                        psi.Out = false;
                    }
                    builderContext.PixelShaderOutputs.RemoveAt(i);
                    i--;
                }
            }

            // Pixel shader inputs that does not have a matching vertex shader output and a valid semantic
            for (int i = 0; i < builderContext.PixelShaderInputs.Count; ++i)
            {
                var psi = builderContext.PixelShaderInputs[i];
                if (builderContext.VertexShaderOutputs.Any(vso => vso.Name == psi.Name))
                {
                    continue;
                }
                if (string.IsNullOrEmpty(psi.Semantic))
                {
                    builderContext.TemporaryPixelShaderVariables.Add(psi);
                    builderContext.PixelShaderInputs.RemoveAt(i--);
                }
                else
                {
                    var arg = new ArgumentDeclaration {
                        Name = psi.Name, Type = psi.Type, Semantic = psi.Semantic, In = true, Out = true
                    };
                    if (!builderContext.VertexShaderInputs.Any(vsi => vsi.Name == psi.Name))
                    {
                        builderContext.VertexShaderInputs.Add(arg);
                    }
                    else
                    {
                        builderContext.VertexShaderInputs.Where(vsi => vsi.Name == psi.Name).ForEach(vsi => vsi.Out = true);
                    }
                    builderContext.VertexShaderOutputs.Add(arg);
                }
            }

            // Remove temporary duplicates
            builderContext.TemporaryPixelShaderVariables = builderContext.TemporaryPixelShaderVariables.Distinct(argumentEqualtyComparer).ToList();

            // Valid vertex shader input semantic
            foreach (var input in builderContext.VertexShaderInputs)
            {
                if (string.IsNullOrEmpty(input.Semantic))
                {
                    throw new InvalidOperationException(string.Concat("Cannot find semantics for vertex shader input ", input.Name));
                }
            }

            // Remove vertex shader outputs that do not have a corresponding pixel shader input
            builderContext.VertexShaderOutputs.RemoveAll(vso => vso.Semantic != "POSITION0" && !builderContext.PixelShaderInputs.Any(psi => psi.Name == vso.Name));

            // Expand vertex shader outputs that do not have a valid semantic
            var nextValidSemanticIndex = 0;

            builderContext.VertexShaderOutputSemanticMapping = builderContext.VertexShaderOutputs.Where(vso => vso.Semantic == null || !validVertexShaderOutputSemantic.IsMatch(vso.Semantic))
                                                               .ToDictionary(arg => arg, arg => NextValidSemantic(builderContext.VertexShaderOutputs, ref nextValidSemanticIndex));

            // POSITION0 is not a valid pixel shader input semantics
            if (builderContext.PixelShaderInputs.Any(psi => psi.Semantic == "POSITION0"))
            {
                var vso = builderContext.VertexShaderOutputs.Single(a => a.Semantic == "POSITION0");
                var arg = new ArgumentDeclaration()
                {
                    Out  = true,
                    Name = vso.Name + "_pos0_",
                    Type = vso.Type,
                };
                var semantic = NextValidSemantic(builderContext.VertexShaderOutputs, ref nextValidSemanticIndex);
                builderContext.VertexShaderOutputSemanticMapping.Add(arg, semantic);
                builderContext.PixelShaderInputs.Single(psi => psi.Semantic == "POSITION0").Semantic = semantic;
            }

            builderContext.VertexShaderOutputs.RemoveAll(vso => builderContext.VertexShaderOutputSemanticMapping.Any(m => m.Key.Name == vso.Name));
            builderContext.VertexShaderOutputs.RemoveAll(vso => builderContext.VertexShaderInputs.Any(vsi => vsi.Name == vso.Name && vsi.Out));

            if (builderContext.VertexShaderOutputs.Any(vso => vso.Semantic == "POSITION0"))
            {
                builderContext.VertexShaderInputs.Where(vsi => vsi.Semantic == "POSITION0").ForEach(vsi => vsi.Out = false);
            }

            // Fix vertex shader input modifier based on the above mapping
            foreach (var vsi in builderContext.VertexShaderInputs)
            {
                if ((!builderContext.PixelShaderInputs.Any(psi => psi.Name == vsi.Name) && vsi.Semantic != "POSITION0") ||
                    builderContext.VertexShaderOutputSemanticMapping.Any(p => vsi.Name == p.Key.Name))
                {
                    vsi.Out = false;
                }
            }

            // Fix pixel shader input semantics based on the above mapping
            foreach (var psi in builderContext.PixelShaderInputs)
            {
                psi.Out = builderContext.PixelShaderOutputs.Any(pso => pso.Name == psi.Name);
                foreach (var p in builderContext.VertexShaderOutputSemanticMapping)
                {
                    if (psi.Name == p.Key.Name)
                    {
                        psi.Semantic = p.Value;
                        break;
                    }
                }
            }
            builderContext.PixelShaderOutputs.RemoveAll(pso => builderContext.PixelShaderInputs.Any(psi => psi.Name == pso.Name) || builderContext.TemporaryPixelShaderVariables.Any(t => t.Name == pso.Name));
            if (simplify)
            {
                foreach (var psi in builderContext.PixelShaderInputs)
                {
                    psi.Out = false;
                }
            }

            // Merge vertex shader inout parameters
            builderContext.VertexShaderOutputs.RemoveAll(vso => vso.In && builderContext.VertexShaderInputs.Any(vsi => vsi.Name == vso.Name));
            return(builderContext);
        }