You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

154 lines
6.3 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Rendering;
namespace UnityEditor.Rendering
{
internal class CSharpToHLSL
{
/// <summary>
/// Generate all shader code from <see cref="GenerateHLSL" /> attribute.
/// </summary>
/// <returns>An awaitable task.</returns>
public static async Task GenerateAll()
{
Dictionary<string, List<ShaderTypeGenerator>> sourceGenerators = null;
try
{
// Store per source file path the generator definitions
sourceGenerators = DictionaryPool<string, List<ShaderTypeGenerator>>.Get();
// Extract all types with the GenerateHLSL tag
foreach (var type in TypeCache.GetTypesWithAttribute<GenerateHLSL>())
{
var attr = type.GetCustomAttributes(typeof(GenerateHLSL), false).First() as GenerateHLSL;
if (!sourceGenerators.TryGetValue(attr.sourcePath, out var generators))
{
generators = ListPool<ShaderTypeGenerator>.Get();
sourceGenerators.Add(attr.sourcePath, generators);
}
generators.Add(new ShaderTypeGenerator(type, attr));
}
// Generate all files
await Task.WhenAll(sourceGenerators.Select(async it =>
await GenerateAsync($"{it.Key}.hlsl", $"{Path.ChangeExtension(it.Key, "custom")}.hlsl", it.Value)));
}
finally
{
// Make sure we always release pooled resources
if (sourceGenerators != null)
{
foreach (var pair in sourceGenerators)
ListPool<ShaderTypeGenerator>.Release(pair.Value);
DictionaryPool<string, List<ShaderTypeGenerator>>.Release(sourceGenerators);
}
}
}
/// <summary>
/// Generate all shader code from <paramref name="generators" /> into <paramref name="targetFilename" />.
/// </summary>
/// <param name="targetFilename">Path of the file to generate.</param>
/// <param name="targetCustomFilename">Path of the custom file to include. (If it exists)</param>
/// <param name="generators">Generators to execute.</param>
/// <returns>Awaitable task.</returns>
private static async Task GenerateAsync(string targetFilename, string targetCustomFilename,
List<ShaderTypeGenerator> generators)
{
var skipFile = false;
// Sort elements to have consistent result
generators.Sort();
// Emit atomic element for all generators
foreach (var gen in generators.Where(gen => !gen.Generate()))
{
// Error reporting will be done by the generator. Skip this file.
gen.PrintErrors();
skipFile = true;
break;
}
// If an error occured during generation, we abort this file
if (skipFile)
return;
// Check access to the file
if (File.Exists(targetFilename))
{
FileInfo info = null;
try
{
info = new FileInfo(targetFilename);
}
catch (UnauthorizedAccessException)
{
Debug.Log("Access to " + targetFilename + " is denied. Skipping it.");
return;
}
catch (SecurityException)
{
Debug.Log("You do not have permission to access " + targetFilename + ". Skipping it.");
return;
}
if (info?.IsReadOnly ?? false)
{
Debug.Log(targetFilename + " is ReadOnly. Skipping it.");
return;
}
}
// Generate content
using var writer = File.CreateText(targetFilename);
writer.NewLine = "\n";
// Include guard name
var guard = Path.GetFileName(targetFilename).Replace(".", "_").ToUpper();
if (!char.IsLetter(guard[0]))
guard = "_" + guard;
await writer.WriteLineAsync("//");
await writer.WriteLineAsync("// This file was automatically generated. Please don't edit by hand. Execute Editor command [ Edit > Rendering > Generate Shader Includes ] instead");
await writer.WriteLineAsync("//");
await writer.WriteLineAsync();
await writer.WriteLineAsync("#ifndef " + guard);
await writer.WriteLineAsync("#define " + guard);
foreach (var gen in generators.Where(gen => gen.hasStatics))
await writer.WriteLineAsync(gen.EmitDefines().Replace("\n", writer.NewLine));
foreach (var gen in generators.Where(gen => gen.hasFields))
await writer.WriteLineAsync(gen.EmitTypeDecl().Replace("\n", writer.NewLine));
foreach (var gen in generators.Where(gen => gen.hasFields && gen.needAccessors && !gen.hasPackedInfo))
{
await writer.WriteAsync(gen.EmitAccessors().Replace("\n", writer.NewLine));
await writer.WriteAsync(gen.EmitSetters().Replace("\n", writer.NewLine));
const bool emitInitters = true;
await writer.WriteAsync(gen.EmitSetters(emitInitters).Replace("\n", writer.NewLine));
}
foreach (var gen in generators.Where(gen =>
gen.hasStatics && gen.hasFields && gen.needParamDebug && !gen.hasPackedInfo))
await writer.WriteLineAsync(gen.EmitFunctions().Replace("\n", writer.NewLine));
foreach (var gen in generators.Where(gen => gen.hasPackedInfo))
await writer.WriteLineAsync(gen.EmitPackedInfo().Replace("\n", writer.NewLine));
await writer.WriteLineAsync();
await writer.WriteLineAsync("#endif");
if (File.Exists(targetCustomFilename))
await writer.WriteAsync($"#include \"{Path.GetFileName(targetCustomFilename)}\"");
}
}
}