Files
sbox-public/engine/Sandbox.Engine/Systems/Render/ShaderCompile/ProgramSource.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

192 lines
4.8 KiB
C#

using Humanizer;
using System.Threading;
namespace Sandbox.Engine.Shaders;
class ProgramSource
{
internal ShaderProgramType ProgramType;
public bool IsOutOfDate { get; set; }
~ProgramSource()
{
if ( builder.IsValid )
{
builder.Delete();
builder = default;
}
}
CVfxByteCodeManager builder;
/// <summary>
/// Compile a single program on this shader
/// </summary>
internal async Task<bool> Compile( ShaderCompileOptions options, Shader vfx, string source, ShaderCompile.Results result, CancellationToken token, string absolutePath, string relativePath )
{
//Console.WriteLine( $" {ProgramType}: {(IsOutOfDate ? "out of date" : "okay")}" );
bool nonInteractiveConsole = Console.IsOutputRedirected || Console.IsInputRedirected || Console.IsErrorRedirected;
using var context = ShaderCompile.GetSharedContext( ProgramType );
context.MaskedSource = ShaderTools.MaskShaderSource( source, ProgramType, false );
ShaderPreprocessor preprocessor = new( new ShaderPreprocessorOptions() { ExpandIncludes = true, IgnoreCoreIncludes = true } );
context.MaskedSource = preprocessor.Preprocess( context.MaskedSource, absolutePath, relativePath );
FastTimer fastTimer = FastTimer.StartNew();
var stepResult = new ShaderCompile.Results.Program();
result.Programs.Add( stepResult );
List<CompiledCombo> allCompiles = new();
var p = vfx.GetProgram( ProgramType );
var combos = p.EnumerateCombos( ProgramType )
.ToArray()
.OrderBy( x => Guid.NewGuid() )
.ToArray();
var totalCombos = combos.Length;
stepResult.Name = $"{ProgramType}";
stepResult.ComboCount = totalCombos;
stepResult.Source = context.MaskedSource;
if ( options.ConsoleOutput )
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine( $" {ProgramType} - {totalCombos:n0} {"combo".Pluralize()}" );
Console.ForegroundColor = ConsoleColor.White;
}
var originalPos = nonInteractiveConsole ? default : Console.GetCursorPosition();
var updateUI = () =>
{
if ( nonInteractiveConsole )
{
Console.Write( $" {Math.Floor( ((double)allCompiles.Count / (double)totalCombos) * 100.0 )}%" );
}
else
{
Console.SetCursorPosition( Console.WindowLeft, originalPos.Top );
Console.Write( new string( ' ', 8 ) );
Console.SetCursorPosition( Console.WindowLeft, originalPos.Top );
Console.Write( $" {Math.Floor( ((double)allCompiles.Count / (double)totalCombos) * 100.0 )}%" );
Console.SetCursorPosition( Console.WindowLeft, originalPos.Top );
}
};
var timeBetweenUpdates = nonInteractiveConsole ? 2000 : 32;
int errors = 0;
var compileCombo = ( ShaderProgram.Combo d ) =>
{
try
{
if ( errors > 0 )
return;
if ( token.IsCancellationRequested )
return;
var result = ShaderCompile.CompileSingleCombo( vfx, this, d.Static, d.Dynamic, context, !options.ForceRecompile );
lock ( this )
{
if ( !result.IsSuccess )
{
Interlocked.Add( ref errors, 1 );
}
allCompiles.Add( result );
if ( options.ConsoleOutput && fastTimer.ElapsedMilliSeconds > timeBetweenUpdates )
{
fastTimer = FastTimer.StartNew();
updateUI();
}
}
}
catch ( System.Exception e )
{
Log.Warning( e );
}
};
if ( options.SingleThreaded )
{
foreach ( var d in combos )
{
await Task.Run( () => compileCombo( d ), token );
}
}
else
{
await Task.Run( () =>
{
Parallel.ForEach( combos, compileCombo );
}, token );
}
token.ThrowIfCancellationRequested();
//
// deduplicate the compiler outputs, add them to our log
//
{
var outputs = string.Join( '\n', allCompiles.Select( x => x.CompilerOutput ).Distinct() ).Trim();
foreach ( var line in outputs.Split( '\n', StringSplitOptions.RemoveEmptyEntries ) )
{
if ( options.ConsoleOutput )
{
Console.WriteLine( line );
}
stepResult.Log( line );
}
}
// One of our compiles failed, so just bail
if ( allCompiles.Any( x => !x.IsSuccess ) )
{
return false;
}
if ( builder.IsValid )
{
builder.Delete();
builder = default;
}
builder = CVfxByteCodeManager.Create();
foreach ( var staticGroup in allCompiles.GroupBy( x => x.StaticCombo ).OrderBy( x => x.Key ) )
{
builder.OnStaticCombo( staticGroup.Key );
foreach ( var entry in staticGroup.OrderBy( x => x.DynamicCombo ) )
{
builder.OnDynamicCombo( entry.GetResult() );
vfx.native.WriteCombo( ProgramType, entry.StaticCombo, entry.DynamicCombo, entry.GetResult() );
}
}
stepResult.Success = true;
return true;
}
public byte[] BuildCompiledShader( Shader vfx )
{
// Step 1. Copy all our compiled shaders into a CVfxByteCodeManager
using var buffer = CUtlBuffer.Create();
vfx.native.WriteProgramToBuffer( ProgramType, builder, buffer );
return buffer.ToArray();
}
}