Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@
using SharpDX.D3DCompiler;
using Stride.Core.Diagnostics;
using Stride.Core.Storage;
using Stride.Rendering;
using Stride.Graphics;
using ConstantBufferType = Stride.Shaders.ConstantBufferType;
using ShaderBytecode = Stride.Shaders.ShaderBytecode;
using ShaderVariableType = SharpDX.D3DCompiler.ShaderVariableType;

namespace Stride.Shaders.Compiler.Direct3D
Expand All @@ -23,13 +20,21 @@ public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, Shad
var isDebug = effectParameters.Debug;
var optimLevel = effectParameters.OptimizationLevel;
var profile = effectParameters.Profile;

var shaderModel = ShaderStageToString(stage) + "_" + ShaderProfileFromGraphicsProfile(profile);

var shaderFlags = ShaderFlags.None;
if (isDebug)
{
shaderFlags = ShaderFlags.Debug;

// Disable optimizations when debugging, except for Graphics Profiles 9.3 and below
// where optimizations are required to have valid shaders (otherwise compilation fails
// indicating too many instruction slots used)
if (profile >= GraphicsProfile.Level_10_0)
{
shaderFlags |= ShaderFlags.SkipOptimization;
}
}
switch (optimLevel)
{
Expand Down
28 changes: 19 additions & 9 deletions sources/engine/Stride.Shaders.Compiler/OpenGL/ShaderCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Stride.Core;
using Stride.Core.Extensions;
Expand All @@ -14,9 +12,7 @@
using Stride.Graphics;
using Stride.Core.Shaders.Ast;
using Stride.Core.Shaders.Ast.Glsl;
using Stride.Core.Shaders.Ast.Hlsl;
using Stride.Core.Shaders.Convertor;
using Stride.Core.Shaders.Writer.Hlsl;
using ConstantBuffer = Stride.Core.Shaders.Ast.Hlsl.ConstantBuffer;
using StorageQualifier = Stride.Core.Shaders.Ast.StorageQualifier;

Expand Down Expand Up @@ -80,7 +76,7 @@ public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, Shad
return shaderBytecodeResult;

if (effectParameters.Platform == GraphicsPlatform.OpenGLES) // TODO: Add check to run on android only. The current version breaks OpenGL ES on windows.
{
{
//TODO: Remove this ugly hack!
if (shaderSource.Contains($"Texture2D StrideInternal_TextureExt0") && shader.Contains("uniform sampler2D"))
{
Expand Down Expand Up @@ -111,6 +107,20 @@ public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, Shad
var inputFileName = Path.ChangeExtension(Path.GetTempFileName(), inputFileExtension);
var outputFileName = Path.ChangeExtension(inputFileName, ".spv");

var args = $"-V"; // Generate SPIR-V binary

// Add debug info if needed
if (effectParameters.Debug)
{
args += " -g"; // Generate debug information
Copy link
Member

@Kryptos-FR Kryptos-FR Nov 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String concatenation creates garbage that needs to be cleaned up by the GC. Let's try to avoid that.

It can be achieved in multiple ways: either with a StringBuilder or with string interpolation (which the compiler can optimize).

For instance (pseudo code):

bool isDebug = ...;

var str = $"{isDebug ? "-D" : ""} some literal {filename}";

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did write it with string concatenation because the function already allocates a lot (to start, two Dictionarys at the top, the ShaderBytecodeResult, the ShaderBytecode, BinarySerializationWriter, MemoryStream, and even calls Encoding.ASCII.GetBytes() several times) 😢.

Also, I tried interpolation, but I think it obfuscates the logic a bit:

string args = $"-V {                  // Generate SPIR-V binary
    (effectParameters.Debug
        ? effectParameters.Profile < GraphicsProfile.Level_10_0
            ? "-g"                    // Generate debug information
            : "-g -Od"                // Generate debug and disable optimizations (only for level 10.0+)
        : "")
    } -o {outputFileName} {inputFileName}";

This is the version with StringBuilder:

var argsBuilder = new StringBuilder();

argsBuilder.Append("-V");  // Generate SPIR-V binary

if (effectParameters.Debug)
{
    argsBuilder.Append(" -g");  // Generate debug information

    if (effectParameters.Profile >= GraphicsProfile.Level_10_0)
    {
        argsBuilder.Append(" -Od"); // Disable optimizations
    }
}
argsBuilder.Append($" -o {outputFileName} {inputFileName}");
string args = argsBuilder.ToString();

What do you think? Obviously, interpolation just allocates once (the final string), but reads worse, while StringBuilder allocates twice (hopefully, not counting internal buffers), and reads better.


if (effectParameters.Profile >= GraphicsProfile.Level_10_0)
{
args += " -Od"; // Disable optimizations
}
}
args += $" -o {outputFileName} {inputFileName}";

// Write shader source to disk
File.WriteAllBytes(inputFileName, Encoding.ASCII.GetBytes(shader));

Expand All @@ -131,7 +141,7 @@ public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, Shad
default:
throw new PlatformNotSupportedException();
}
ShellHelper.RunProcessAndRedirectToLogger(filename, $"-V -o {outputFileName} {inputFileName}", null, shaderBytecodeResult);
ShellHelper.RunProcessAndRedirectToLogger(filename, args, workingDirectory: null, shaderBytecodeResult);

if (!File.Exists(outputFileName))
{
Expand Down Expand Up @@ -163,13 +173,13 @@ public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, Shad
// store string on OpenGL platforms
rawData = Encoding.UTF8.GetBytes(shader);
}

var bytecodeId = ObjectId.FromBytes(rawData);
var bytecode = new ShaderBytecode(bytecodeId, rawData);
bytecode.Stage = stage;

shaderBytecodeResult.Bytecode = bytecode;

return shaderBytecodeResult;
}

Expand Down Expand Up @@ -262,7 +272,7 @@ private string Compile(string shaderSource, string entryPoint, ShaderStage stage
MarkResourceBindingAsUsed(reflection, resourceBindingIndex, stage);
}
}

foreach (var variable in glslShader.Declarations.OfType<Variable>().Where(x => (x.Qualifiers.Contains(StorageQualifier.Uniform))))
{
// Check if we have a variable that starts or ends with this name (in case of samplers)
Expand Down
Loading