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
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)}\"");
|
|
}
|
|
}
|
|
}
|