4 using System.Collections.Generic;
7 using System.Runtime.InteropServices;
9 using System.Text.RegularExpressions;
10 using SiliconStudio.Shaders.Parser;
12 namespace SiliconStudio.Shaders
19 #if !FRAMEWORK_SHADER_USE_SHARPDX
20 [Guid(
"8BA5FB08-5195-40e2-AC58-0D989C3A0102"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
21 internal interface IBlob
24 System.IntPtr GetBufferPointer();
27 System.IntPtr GetBufferSize();
30 [DllImport(
"d3dcompiler_43.dll", EntryPoint =
"D3DPreprocess", PreserveSig =
true, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi)]
31 private static extern int D3DPreprocess(IntPtr ptrData, IntPtr
size, [MarshalAs(UnmanagedType.LPStr)]
string pSourceName, [MarshalAs(UnmanagedType.LPArray)]
ShaderMacro[] pDefines, IntPtr pInclude, [Out] out IBlob ppCodeText, [Out] out IBlob ppErrorMsgs);
44 public static string Run(
string shaderSource,
string sourceFileName,
ShaderMacro[] defines = null, params
string[] includeDirectories)
49 if (includeDirectories != null)
50 defaultHandler.AddDirectories(includeDirectories);
52 defaultHandler.AddDirectory(Environment.CurrentDirectory);
54 var directoryName = Path.GetDirectoryName(sourceFileName);
55 if (!
string.IsNullOrEmpty(directoryName))
56 defaultHandler.AddDirectory(directoryName);
59 return Run(shaderSource, sourceFileName, defines, defaultHandler);
75 shaderSource = ConcatenateTokens(shaderSource, defines);
79 #if FRAMEWORK_SHADER_USE_SHARPDX
80 string compilationErrors;
81 shaderSource = SharpDX.D3DCompiler.ShaderBytecode.Preprocess(
83 defines != null ? defines.Select(x =>
new SharpDX.Direct3D.ShaderMacro(x.Name, x.Definition)).ToArray() : null,
84 new IncludeShadow(include),
85 out compilationErrors, sourceFileName);
87 IBlob blobForText = null;
88 IBlob blobForErrors = null;
90 var shadow = include == null ? null :
new IncludeShadow(include);
92 var data = Encoding.ASCII.GetBytes(shaderSource);
96 fixed (
void* pData = data)
97 result = D3DPreprocess((IntPtr)pData,
new IntPtr(data.Length), sourceFileName, PrepareMacros(defines), shadow != null ? shadow.NativePointer : IntPtr.Zero, out blobForText, out blobForErrors);
104 throw new InvalidOperationException(
string.Format(
"Include errors: {0}", blobForErrors == null ?
"" : Marshal.PtrToStringAnsi(blobForErrors.GetBufferPointer())));
106 shaderSource = Marshal.PtrToStringAnsi(blobForText.GetBufferPointer());
110 Console.WriteLine(
"Warning, error while preprocessing file [{0}] : {1}", sourceFileName, ex.Message);
120 if (macros.Length == 0)
123 if (macros[macros.Length - 1].
Name == null && macros[macros.Length - 1].
Definition == null)
126 var macroArray =
new ShaderMacro[macros.Length + 1];
128 Array.Copy(macros, macroArray, macros.Length);
134 #if FRAMEWORK_SHADER_USE_SHARPDX
138 internal class IncludeShadow : SharpDX.CallbackBase, SharpDX.D3DCompiler.Include
144 this.callback = callback;
147 public Stream Open(SharpDX.D3DCompiler.IncludeType type,
string fileName,
Stream parentStream)
149 return callback.Open((
IncludeType)type, fileName, parentStream);
152 public void Close(
Stream stream)
154 callback.Close(stream);
161 internal class IncludeShadow : IDisposable
163 private static readonly IncludeVtbl Vtbl =
new IncludeVtbl();
164 private readonly GCHandle handle;
165 private Dictionary<IntPtr, Frame> _frames;
167 public IntPtr NativePointer;
173 this.Callback = callback;
175 NativePointer = Marshal.AllocHGlobal(IntPtr.Size * 2);
177 handle = GCHandle.Alloc(
this);
178 Marshal.WriteIntPtr(NativePointer, Vtbl.Pointer);
179 Marshal.WriteIntPtr(NativePointer, IntPtr.Size, GCHandle.ToIntPtr(handle));
181 _frames =
new Dictionary<IntPtr, Frame>();
187 public void Dispose()
192 protected void Dispose(
bool disposing)
194 if (NativePointer != IntPtr.Zero)
198 foreach (var frame
in _frames)
203 Marshal.FreeHGlobal(NativePointer);
204 NativePointer = IntPtr.Zero;
215 private static byte[] ReadStream(
Stream stream)
218 return ReadStream(stream, ref readLength);
227 private static byte[] ReadStream(
Stream stream, ref
int readLength)
229 int num = readLength;
231 readLength = (int)(stream.Length - stream.Position);
234 System.Diagnostics.Debug.Assert(num >= 0);
238 byte[] buffer =
new byte[num];
244 bytesRead += stream.Read(buffer, bytesRead, readLength - bytesRead);
245 }
while (bytesRead < readLength);
252 public Frame(
Stream stream, GCHandle handle)
258 public bool IsClosed;
260 public GCHandle Handle;
275 private class IncludeVtbl
278 private List<Delegate> methods;
284 private void AddMethod(Delegate method)
286 int index = methods.Count;
288 Marshal.WriteIntPtr(
Pointer, index * IntPtr.Size, Marshal.GetFunctionPointerForDelegate(method));
294 Pointer = Marshal.AllocHGlobal(IntPtr.Size * 2);
295 methods =
new List<Delegate>();
297 AddMethod(
new OpenDelegate(OpenImpl));
298 AddMethod(
new CloseDelegate(CloseImpl));
302 private static IncludeShadow ToShadow(IntPtr thisPtr)
304 var handle = GCHandle.FromIntPtr(Marshal.ReadIntPtr(thisPtr, IntPtr.Size));
305 return (IncludeShadow)handle.Target;
318 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
319 private delegate
int OpenDelegate(IntPtr thisPtr,
IncludeType includeType, IntPtr fileNameRef, IntPtr pParentData, out IntPtr dataRef, out
int bytesRef);
320 private static int OpenImpl(IntPtr thisPtr,
IncludeType includeType, IntPtr fileNameRef, IntPtr pParentData, out IntPtr dataRef, out
int bytesRef)
322 dataRef = IntPtr.Zero;
327 var shadow = ToShadow(thisPtr);
328 var callback = shadow.Callback;
331 Stream parentStream = null;
333 if (shadow._frames.ContainsKey(pParentData))
334 parentStream = shadow._frames[pParentData].Stream;
336 stream = callback.Open(includeType, Marshal.PtrToStringAnsi(fileNameRef), parentStream);
341 byte[] data = ReadStream(stream);
342 var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
343 dataRef = handle.AddrOfPinnedObject();
344 bytesRef = data.Length;
346 shadow._frames.Add(dataRef,
new Frame(stream, handle));
365 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
366 private delegate
int CloseDelegate(IntPtr thisPtr, IntPtr pData);
367 private static int CloseImpl(IntPtr thisPtr, IntPtr pData)
371 var shadow = ToShadow(thisPtr);
372 var callback = shadow.Callback;
375 if (shadow._frames.TryGetValue(pData, out frame))
377 callback.Close(frame.Stream);
390 private readonly
static Regex ConcatenateTokensRegex =
new Regex(
@"(\w*)\s*#(#)?\s*(\w+)", RegexOptions.Compiled);
391 private readonly
static HashSet<string> PreprocessorKeywords =
new HashSet<string>(
new[] {
"if",
"else",
"elif",
"endif",
"define",
"undef",
"ifdef",
"ifndef",
"line",
"error",
"pragma",
"include" });
397 if (!source.Contains(
'#'))
402 var newSource =
new StringBuilder();
403 var reader =
new StringReader(source);
405 while ((sourceLine= reader.ReadLine()) != null)
408 string comment = null;
411 if (sourceLine.TrimStart().StartsWith(
"#"))
413 newSource.AppendLine(sourceLine);
418 int indexComment = sourceLine.IndexOf(
"//", StringComparison.InvariantCultureIgnoreCase);
419 if (indexComment >= 0)
421 comment = sourceLine.Substring(indexComment, sourceLine.Length - indexComment);
422 line = sourceLine.Substring(0, indexComment);
432 var match = ConcatenateTokensRegex.Match(line, position);
437 newSource.AppendLine(sourceLine);
442 Dictionary<string, string> macroMap = null;
446 macroMap =
new Dictionary<string, string>();
447 foreach (var shaderMacro
in macros)
449 macroMap[shaderMacro.Name] = shaderMacro.Definition;
451 if (shaderMacro.Definition != null)
453 addLength += shaderMacro.Definition.Length;
458 var stringBuilder =
new StringBuilder(line.Length + addLength);
460 while (match.Success)
463 stringBuilder.Append(line, position, match.Index - position);
466 bool stringify = !match.Groups[2].Success;
468 var group = match.Groups[3];
469 var token = group.Value;
470 if (stringify && PreprocessorKeywords.Contains(token))
473 stringBuilder.Append(match.Groups[0].Value);
478 stringBuilder.Append(TransformToken(match.Groups[1].Value, macroMap));
482 stringBuilder.Append(
'"');
484 stringBuilder.Append(EscapeString(TransformToken(token, macroMap,
true)));
485 stringBuilder.Append(
'"');
489 stringBuilder.Append(TransformToken(token, macroMap));
494 position = group.Index + group.Length;
495 match = ConcatenateTokensRegex.Match(line, position);
499 stringBuilder.Append(line, position, line.Length - position);
503 stringBuilder.Append(comment);
506 newSource.AppendLine(stringBuilder.ToString());
509 return newSource.ToString();
512 private static string TransformToken(
string token, Dictionary<string, string> macros,
bool emptyIfNotFound =
false)
518 return macros.TryGetValue(token, out result) ? result : emptyIfNotFound ?
string.Empty : token;
521 private static string EscapeString(
string s)
523 return s.Replace(
"\\",
"\\\\").
Replace(
"\"",
"\\\"");
SiliconStudio.Paradox.Shaders.ShaderMacro ShaderMacro
Default IncludeHandler implementation loading files from a set of predefined directories.
Macro to be used with PreProcessor.
IncludeType
Type of include file.
C++ preprocessor using D3DPreprocess method from d3dcompiler API.
static string Run(string shaderSource, string sourceFileName=null, ShaderMacro[] defines=null, IncludeHandler include=null)
Preprocesses the provided shader or effect source.
Callback interface to handle included file requested by the PreProcessor.
_In_ size_t _In_ size_t size
readonly string Definition
Value of the macro to set.
static string Run(string shaderSource, string sourceFileName, ShaderMacro[] defines=null, params string[] includeDirectories)
Preprocesses the provided shader or effect source.
readonly string Name
Name of the macro to set.