mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-02 11:28:19 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
244 lines
8.5 KiB
C#
244 lines
8.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Diagnostics;
|
|
|
|
namespace ShaderGraph;
|
|
|
|
/// <summary>
|
|
/// Tests for ShaderGraph compilation
|
|
/// These tests verify that shader graphs from the tools addon can compile correctly
|
|
/// </summary>
|
|
[TestClass]
|
|
public class ShaderGraphTests
|
|
{
|
|
private Sandbox.PackageLoader packageLoader;
|
|
private Sandbox.PackageLoader.Enroller enroller;
|
|
|
|
/// <summary>
|
|
/// Initialize the package loader and load the tools addon
|
|
/// </summary>
|
|
private async Task InitializePackageLoader()
|
|
{
|
|
packageLoader = new Sandbox.PackageLoader( "shadergraph_test", GetType().Assembly );
|
|
packageLoader.ToolsMode = true;
|
|
enroller = packageLoader.CreateEnroller( "shadergraph_test" );
|
|
|
|
var project = Project.AddFromFile( "addons/tools/.sbproj" );
|
|
await Project.SyncWithPackageManager();
|
|
await Project.CompileAsync();
|
|
|
|
enroller.LoadPackage( project.Package.FullIdent, true );
|
|
packageLoader.Tick();
|
|
|
|
var loadedAssemblies = enroller.GetLoadedAssemblies();
|
|
Assert.IsTrue( loadedAssemblies.Length > 0, "No assemblies were loaded from the tools addon" );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find and return the required ShaderGraph types from loaded assemblies
|
|
/// </summary>
|
|
private (Type shaderGraphType, Type resultNodeType, Type graphCompilerType) FindShaderGraphTypes()
|
|
{
|
|
var loadedAssemblies = enroller.GetLoadedAssemblies();
|
|
Type shaderGraphType = null;
|
|
Type resultNodeType = null;
|
|
Type graphCompilerType = null;
|
|
|
|
foreach ( var loadedAssembly in loadedAssemblies )
|
|
{
|
|
var types = loadedAssembly.Assembly.GetTypes();
|
|
foreach ( var type in types )
|
|
{
|
|
if ( type.Name == "ShaderGraph" && type.Namespace == "Editor.ShaderGraph" )
|
|
shaderGraphType = type;
|
|
else if ( type.Name == "Result" && type.Namespace == "Editor.ShaderGraph" )
|
|
resultNodeType = type;
|
|
else if ( type.Name == "GraphCompiler" && type.Namespace == "Editor.ShaderGraph" )
|
|
graphCompilerType = type;
|
|
}
|
|
}
|
|
|
|
Assert.IsNotNull( shaderGraphType, "ShaderGraph type not found" );
|
|
Assert.IsNotNull( resultNodeType, "Result node type not found" );
|
|
Assert.IsNotNull( graphCompilerType, "GraphCompiler type not found" );
|
|
|
|
return (shaderGraphType, resultNodeType, graphCompilerType);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a minimal shader graph with a result node
|
|
/// </summary>
|
|
private object CreateMinimalShaderGraph( Type shaderGraphType, Type resultNodeType, string description )
|
|
{
|
|
var shaderGraph = Activator.CreateInstance( shaderGraphType );
|
|
Assert.IsNotNull( shaderGraph, "Failed to create ShaderGraph instance" );
|
|
|
|
// Set basic properties
|
|
var descriptionProperty = shaderGraphType.GetProperty( "Description" );
|
|
descriptionProperty?.SetValue( shaderGraph, description );
|
|
|
|
// Create a Result node
|
|
var resultNode = Activator.CreateInstance( resultNodeType );
|
|
Assert.IsNotNull( resultNode, "Failed to create Result node instance" );
|
|
|
|
// Add the result node to the graph
|
|
var addNodeMethod = shaderGraphType.GetMethod( "AddNode" );
|
|
Assert.IsNotNull( addNodeMethod, "AddNode method not found" );
|
|
addNodeMethod.Invoke( shaderGraph, new[] { resultNode } );
|
|
|
|
return shaderGraph;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate shader code from a shader graph using the GraphCompiler
|
|
/// </summary>
|
|
private string GenerateShaderCode( object shaderGraph, Type shaderGraphType, Type graphCompilerType )
|
|
{
|
|
// Create a compiler and generate shader code
|
|
var compilerConstructor = graphCompilerType.GetConstructor( new[] { shaderGraphType, typeof( bool ) } );
|
|
Assert.IsNotNull( compilerConstructor, "GraphCompiler constructor not found" );
|
|
|
|
var compiler = compilerConstructor.Invoke( new[] { shaderGraph, false } );
|
|
Assert.IsNotNull( compiler, "Failed to create GraphCompiler instance" );
|
|
|
|
// Generate the shader source code
|
|
var generateMethod = graphCompilerType.GetMethod( "Generate" );
|
|
Assert.IsNotNull( generateMethod, "Generate method not found" );
|
|
|
|
var shaderCode = generateMethod.Invoke( compiler, null ) as string;
|
|
Assert.IsNotNull( shaderCode, "Shader compilation failed - returned null" );
|
|
Assert.IsTrue( shaderCode.Length > 0, "Shader compilation returned empty string" );
|
|
|
|
return shaderCode;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test that we can create a minimal shader graph and compile it to
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task GraphToShader()
|
|
{
|
|
await InitializePackageLoader();
|
|
var (shaderGraphType, resultNodeType, graphCompilerType) = FindShaderGraphTypes();
|
|
|
|
var shaderGraph = CreateMinimalShaderGraph( shaderGraphType, resultNodeType, "Test shader graph" );
|
|
var shaderCode = GenerateShaderCode( shaderGraph, shaderGraphType, graphCompilerType );
|
|
|
|
// Basic validation that it looks like shader code
|
|
Assert.IsTrue( shaderCode.Contains( "HEADER" ), "Generated shader doesn't contain expected HEADER section" );
|
|
Assert.IsTrue( shaderCode.Contains( "MainPs" ), "Generated shader doesn't contain expected MainPs function" );
|
|
|
|
Console.WriteLine( $"Generated shader code length: {shaderCode.Length} characters" );
|
|
Console.WriteLine( "First 500 characters of generated shader:" );
|
|
Console.WriteLine( shaderCode.Substring( 0, Math.Min( 500, shaderCode.Length ) ) );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test that we can create a shader graph, generate shader code, and compile it using shadercompiler.exe
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task ShaderCompilation()
|
|
{
|
|
await InitializePackageLoader();
|
|
var (shaderGraphType, resultNodeType, graphCompilerType) = FindShaderGraphTypes();
|
|
|
|
var shaderGraph = CreateMinimalShaderGraph( shaderGraphType, resultNodeType, "Test shader graph for unit testing" );
|
|
var shaderCode = GenerateShaderCode( shaderGraph, shaderGraphType, graphCompilerType );
|
|
|
|
// Write the shader code to a file in the game directory
|
|
var gameDir = Directory.GetCurrentDirectory();
|
|
var shadersDir = Path.Combine( gameDir, "core", "shaders" );
|
|
Directory.CreateDirectory( shadersDir );
|
|
|
|
var shaderFileName = "test_shadergraph_unit_test.shader";
|
|
var shaderPath = Path.Combine( shadersDir, shaderFileName );
|
|
|
|
File.WriteAllText( shaderPath, shaderCode );
|
|
Console.WriteLine( $"Wrote shader to: {shaderPath}" );
|
|
|
|
// Find shadercompiler.exe
|
|
var shaderCompilerPath = Path.Combine( gameDir, "bin", "managed", "shadercompiler.exe" );
|
|
Assert.IsTrue( File.Exists( shaderCompilerPath ), $"Shader compiler not found at: {shaderCompilerPath}" );
|
|
|
|
// Run shadercompiler.exe on this specific shadershader
|
|
var success = await RunShaderCompiler( shaderCompilerPath, gameDir, shaderPath );
|
|
Assert.IsTrue( success, "Shader compilation with shadercompiler.exe failed" );
|
|
|
|
// Check if shader_c file was created
|
|
var shaderCPath = shaderPath + "_c";
|
|
Assert.IsTrue( File.Exists( shaderCPath ), $"Compiled shader file not found at: {shaderCPath}" );
|
|
|
|
var shaderCSize = new FileInfo( shaderCPath ).Length;
|
|
Assert.IsTrue( shaderCSize > 0, "Compiled shader file is empty" );
|
|
|
|
Console.WriteLine( $"Successfully compiled shader to {shaderCPath} ({shaderCSize} bytes)" );
|
|
|
|
// Clean up
|
|
try
|
|
{
|
|
File.Delete( shaderPath );
|
|
File.Delete( shaderCPath );
|
|
}
|
|
catch ( Exception ex )
|
|
{
|
|
Console.WriteLine( $"Failed to clean up temp files: {ex.Message}" );
|
|
}
|
|
}
|
|
|
|
private async Task<bool> RunShaderCompiler( string shaderCompilerPath, string workingDirectory, string shaderFile )
|
|
{
|
|
using ( var process = new Process() )
|
|
{
|
|
process.StartInfo.FileName = shaderCompilerPath;
|
|
process.StartInfo.Arguments = shaderFile; // Just compile this specific shader
|
|
process.StartInfo.UseShellExecute = false;
|
|
process.StartInfo.RedirectStandardOutput = true;
|
|
process.StartInfo.RedirectStandardError = true;
|
|
process.StartInfo.CreateNoWindow = true;
|
|
process.StartInfo.WorkingDirectory = workingDirectory;
|
|
|
|
var outputMessages = new List<string>();
|
|
var errorMessages = new List<string>();
|
|
|
|
process.OutputDataReceived += ( sender, e ) =>
|
|
{
|
|
if ( e.Data != null )
|
|
{
|
|
outputMessages.Add( e.Data );
|
|
Console.WriteLine( $"ShaderCompiler: {e.Data}" );
|
|
}
|
|
};
|
|
|
|
process.ErrorDataReceived += ( sender, e ) =>
|
|
{
|
|
if ( e.Data != null )
|
|
{
|
|
errorMessages.Add( e.Data );
|
|
Console.WriteLine( $"ShaderCompiler ERROR: {e.Data}" );
|
|
}
|
|
};
|
|
|
|
Console.WriteLine( $"Running: {shaderCompilerPath} {process.StartInfo.Arguments}" );
|
|
process.Start();
|
|
|
|
process.BeginOutputReadLine();
|
|
process.BeginErrorReadLine();
|
|
|
|
await process.WaitForExitAsync();
|
|
|
|
if ( process.ExitCode != 0 )
|
|
{
|
|
Console.WriteLine( $"Shader compiler failed with exit code: {process.ExitCode}" );
|
|
foreach ( var error in errorMessages )
|
|
{
|
|
Console.WriteLine( $"Error: {error}" );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|