forked from MonoGame/MonoGame
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathShaderResult.cs
160 lines (128 loc) · 5.8 KB
/
ShaderResult.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// MonoGame - Copyright (C) The MonoGame Team
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
using System;
using System.Collections.Generic;
using System.IO;
using TwoMGFX.TPGParser;
namespace TwoMGFX
{
public class ShaderResult
{
public ShaderInfo ShaderInfo { get; private set; }
public string FilePath { get; private set; }
public string FileContent { get; private set; }
public string OutputFilePath { get; private set; }
public List<string> Dependencies { get; private set; }
public List<string> AdditionalOutputFiles { get; private set; }
public ShaderProfile Profile { get; private set; }
public bool Debug { get; private set; }
static public ShaderResult FromFile(string path, Options options, IEffectCompilerOutput output)
{
var effectSource = File.ReadAllText(path);
return FromString(effectSource, path, options, output);
}
static public ShaderResult FromString(string effectSource, string filePath, Options options, IEffectCompilerOutput output)
{
var macros = new Dictionary<string, string>();
macros.Add("MGFX", "1");
options.Profile.AddMacros(macros);
// If we're building shaders for debug set that flag too.
if (options.Debug)
macros.Add("DEBUG", "1");
if (!string.IsNullOrEmpty(options.Defines))
{
var defines = options.Defines.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var define in defines)
{
var name = define;
var value = "1";
if (define.Contains("="))
{
var parts = define.Split('=');
if (parts.Length > 0)
name = parts[0].Trim();
if (parts.Length > 1)
value = parts[1].Trim();
}
macros.Add(name, value);
}
}
// Use the D3DCompiler to pre-process the file resolving
// all #includes and macros.... this even works for GLSL.
string newFile;
var fullPath = Path.GetFullPath(filePath);
var dependencies = new List<string>();
newFile = Preprocessor.Preprocess(effectSource, fullPath, macros, dependencies, output);
// Parse the resulting file for techniques and passes.
var tree = new Parser(new Scanner()).Parse(newFile, fullPath);
if (tree.Errors.Count > 0)
{
var errors = String.Empty;
foreach (var error in tree.Errors)
errors += string.Format("{0}({1},{2}) : {3}\r\n", error.File, error.Line, error.Column, error.Message);
throw new Exception(errors);
}
// Evaluate the results of the parse tree.
var shaderInfo = tree.Eval() as ShaderInfo;
// Remove the samplers and techniques so that the shader compiler
// gets a clean file without any FX file syntax in it.
var cleanFile = newFile;
WhitespaceNodes(TokenType.Technique_Declaration, tree.Nodes, ref cleanFile);
WhitespaceNodes(TokenType.Sampler_Declaration_States, tree.Nodes, ref cleanFile);
// Setup the rest of the shader info.
ShaderResult result = new ShaderResult();
result.ShaderInfo = shaderInfo;
result.Dependencies = dependencies;
result.FilePath = fullPath;
result.FileContent = cleanFile;
if (!string.IsNullOrEmpty(options.OutputFile))
result.OutputFilePath = Path.GetFullPath(options.OutputFile);
result.AdditionalOutputFiles = new List<string>();
// Remove empty techniques.
for (var i = 0; i < shaderInfo.Techniques.Count; i++)
{
var tech = shaderInfo.Techniques[i];
if (tech.Passes.Count <= 0)
{
shaderInfo.Techniques.RemoveAt(i);
i--;
}
}
// We must have at least one technique.
if (shaderInfo.Techniques.Count <= 0)
throw new Exception("The effect must contain at least one technique and pass!");
result.Profile = options.Profile;
result.Debug = options.Debug;
return result;
}
public static void WhitespaceNodes(TokenType type, List<ParseNode> nodes, ref string sourceFile)
{
for (var i = 0; i < nodes.Count; i++)
{
var n = nodes[i];
if (n.Token.Type != type)
{
WhitespaceNodes(type, n.Nodes, ref sourceFile);
continue;
}
// Get the full content of this node.
var start = n.Token.StartPos;
var end = n.Token.EndPos;
var length = end - n.Token.StartPos;
var content = sourceFile.Substring(start, length);
// Replace the content of this node with whitespace.
for (var c = 0; c < length; c++)
{
if (!char.IsWhiteSpace(content[c]))
content = content.Replace(content[c], ' ');
}
// Add the whitespace back to the source file.
var newfile = sourceFile.Substring(0, start);
newfile += content;
newfile += sourceFile.Substring(end);
sourceFile = newfile;
}
}
}
}