diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 0a269bd..982f35d 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -1,30 +1,44 @@ -name: Publish Preview NuGet Package +name: Publish Development Preview NuGet Package permissions: - contents: write # needed to push tags and commits - packages: write # optional – good practice when publishing packages + contents: read + packages: write on: push: branches: - - development # run on merge to develop (if you have a develop branch) + - development jobs: - build_and_publish: + build_and_publish_preview: runs-on: ubuntu-latest steps: # Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 # fetch full history for tags - # Setup Git for committing and tagging - - name: Set up Git + # Extract base version from CHANGELOG.md + - name: Extract version from CHANGELOG.md + id: extract_version run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git + if [ ! -f CHANGELOG.md ]; then + echo "❌ CHANGELOG.md not found!" + exit 1 + fi + + BASE_VERSION=$(grep -m1 -oP '(?<=## \[)[^]]+' CHANGELOG.md) + + if [ -z "$BASE_VERSION" ]; then + echo "❌ Could not extract version from CHANGELOG.md" + exit 1 + fi + + SHORT_SHA=$(git rev-parse --short HEAD) + + PREVIEW_VERSION="${BASE_VERSION}-preview.${SHORT_SHA}" + + echo "Preview version: $PREVIEW_VERSION" + echo "VERSION=$PREVIEW_VERSION" >> $GITHUB_ENV # Setup .NET - name: Setup .NET @@ -32,82 +46,43 @@ jobs: with: dotnet-version: '8.0.x' - # Bump preview version - - name: Bump preview version - run: | - CSPROJS=( - "./RisShaderToolkit/RisShaderToolkit/RisShaderToolkit.csproj" - ) - for csproj in "${CSPROJS[@]}"; do - echo "Processing $csproj" - CURRENT_VERSION=$(grep -oP '(?<=).*?(?=)' "$csproj") - if [[ -z "$CURRENT_VERSION" ]]; then - echo "Warning: No found in $csproj" - continue - fi - PREVIEW_NUMBER=$(echo "$CURRENT_VERSION" | grep -oP '(?<=-preview\.)\d+' || echo "0") - NEW_PREVIEW_NUMBER=$((PREVIEW_NUMBER + 1)) - NEW_VERSION=$(echo "$CURRENT_VERSION" | sed -E "s/(.*-preview\.)[0-9]+/\1$NEW_PREVIEW_NUMBER/") - if [[ "$CURRENT_VERSION" != *"-preview."* ]]; then - NEW_VERSION="${CURRENT_VERSION}-preview.1" - fi - echo "Bumping $CURRENT_VERSION → $NEW_VERSION in $csproj" - sed -i "s#.*#$NEW_VERSION#g" "$csproj" - git add "$csproj" - done - git commit -m "Bump preview version to $NEW_VERSION" || echo "No changes to commit" - git push origin development || echo "Push failed (maybe no changes)" - echo "VERSION=$NEW_VERSION" >> "$GITHUB_ENV" - # Build - name: Build run: dotnet build ./RisShaderToolkit/RisShaderToolkit.sln --configuration Release - # Pack + # Pack with preview version - name: Pack - run: dotnet pack ./RisShaderToolkit/RisShaderToolkit.sln --configuration Release --no-build + run: | + dotnet pack ./RisShaderToolkit/RisShaderToolkit.sln \ + --configuration Release \ + --no-build \ + /p:PackageVersion=${{ env.VERSION }} # Find NuGet package - name: Find NuGet packages id: find_pkgs run: | mapfile -t packages < <(find ./RisShaderToolkit -name "*.nupkg" ! -name "*symbols*.nupkg") + if [ ${#packages[@]} -eq 0 ]; then - echo "Error: No .nupkg files found!" + echo "❌ No .nupkg files found!" exit 1 fi + { echo "packages<> $GITHUB_OUTPUT - # Publish to NuGet - - name: Publish all NuGet packages + # Publish preview packages + - name: Publish preview NuGet packages run: | while IFS= read -r pkg; do [[ -z "$pkg" ]] && continue - echo "Publishing $pkg" + echo "Publishing preview $pkg" dotnet nuget push "$pkg" \ --api-key ${{ secrets.NUGET_API_KEY }} \ --source https://api.nuget.org/v3/index.json \ --skip-duplicate done <<< "${{ steps.find_pkgs.outputs.packages }}" - - - name: Create and push git tag - run: | - git tag "v${{ env.VERSION }}" - git push origin "v${{ env.VERSION }}" - - # Create GitHub Release - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - tag_name: v${{ env.VERSION }} - name: Preview ${{ env.VERSION }} - draft: false - prerelease: true - generate_release_notes: true - files: | - ${{ steps.find_pkgs.outputs.packages }} - overwrite: true # equivalent to overwrite_files: true in newer versions diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f98958..a24ac7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.2.0] - 2026-03-21 + +### Added +- `Slang` to `glsl` shader compilation is now supported. +- `CompileSlangToGlsl` method was added to the `ShaderCompiler`. + ## [0.1.0] - 2026-03-04 ### Added diff --git a/README.md b/README.md index a8bd7a9..1b1aedd 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,9 @@ The toolkit consists of two main parts: - Provides easy-to-use methods for compiling Slang shaders into multiple target formats: - WGSL - SPIR-V + - GLSL - possibly others in the future + - Published as **NuGet package(s)** - Used by **RisGameFramework** but can be used by any other .NET project diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client.Desktop/Program.cs b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client.Desktop/Program.cs deleted file mode 100644 index 8a189d3..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client.Desktop/Program.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; - -using Avalonia; - -namespace RisGameFramework.ShaderToolkit.Client.Desktop; - -class Program -{ - // Initialization code. Don't use any Avalonia, third-party APIs or any - // SynchronizationContext-reliant code before AppMain is called: things aren't initialized - // yet and stuff might break. - [STAThread] - public static void Main(string[] args) => BuildAvaloniaApp() - .StartWithClassicDesktopLifetime(args); - - // Avalonia configuration, don't remove; also used by visual designer. - public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() - .UsePlatformDetect() - .WithInterFont() - .LogToTrace(); - -} diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client.Desktop/RisGameFramework.ShaderToolkit.Client.Desktop.csproj b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client.Desktop/RisGameFramework.ShaderToolkit.Client.Desktop.csproj deleted file mode 100644 index b4a898e..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client.Desktop/RisGameFramework.ShaderToolkit.Client.Desktop.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - WinExe - - net8.0 - enable - true - app.manifest - - - - - - - - - - diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client.Desktop/app.manifest b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client.Desktop/app.manifest deleted file mode 100644 index e0ce8d0..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client.Desktop/app.manifest +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/App.axaml b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/App.axaml deleted file mode 100644 index cb427e8..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/App.axaml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/App.axaml.cs b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/App.axaml.cs deleted file mode 100644 index b1ff05c..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/App.axaml.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Data.Core.Plugins; -using Avalonia.Markup.Xaml; - -using RisGameFramework.ShaderToolkit.Client.ViewModels; -using RisGameFramework.ShaderToolkit.Client.Views; - -namespace RisGameFramework.ShaderToolkit.Client; - -public partial class App : Application -{ - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); - } - - public override void OnFrameworkInitializationCompleted() - { - // Line below is needed to remove Avalonia data validation. - // Without this line you will get duplicate validations from both Avalonia and CT - BindingPlugins.DataValidators.RemoveAt(0); - - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - desktop.MainWindow = new MainWindow - { - DataContext = new MainViewModel() - }; - } - else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform) - { - singleViewPlatform.MainView = new MainView - { - DataContext = new MainViewModel() - }; - } - - base.OnFrameworkInitializationCompleted(); - } -} diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Assets/avalonia-logo.ico b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Assets/avalonia-logo.ico deleted file mode 100644 index da8d49f..0000000 Binary files a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Assets/avalonia-logo.ico and /dev/null differ diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Controls/Editor.axaml b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Controls/Editor.axaml deleted file mode 100644 index 36cc1ce..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Controls/Editor.axaml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Controls/Editor.axaml.cs b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Controls/Editor.axaml.cs deleted file mode 100644 index f761a0e..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Controls/Editor.axaml.cs +++ /dev/null @@ -1,65 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using AvaloniaEdit.TextMate; -using System; -using TextMateSharp.Grammars; -using static AvaloniaEdit.TextMate.TextMate; - -namespace RisGameFramework.ShaderToolkit.Client.Controls; - -/// -/// The shader editor control. -/// -public partial class Editor : UserControl -{ - private const string HLSL_GRAMMAR_EXTENSION = ".hlsl"; - - private RegistryOptions _registryOptions; - private Installation _textMateInstallation; - - /// - /// The constructor. - /// - public Editor() - { - InitializeComponent(); - - // Setup TextMate with a VS Code-like dark theme - _registryOptions = new RegistryOptions(ThemeName.DarkPlus); - _textMateInstallation = ShaderEditorInstance.InstallTextMate(_registryOptions); - Language language = _registryOptions.GetLanguageByExtension(HLSL_GRAMMAR_EXTENSION); - _textMateInstallation.SetGrammar(_registryOptions.GetScopeByLanguageId(_registryOptions.GetLanguageByExtension(HLSL_GRAMMAR_EXTENSION).Id)); - - ShaderEditorInstance.TextChanged += (s, e) => - { - SetValue(SourceProperty, ShaderEditorInstance.Text); - SourceChanged?.Invoke(this, e); - }; - } - - /// - /// Invoked whenever source text is changed. - /// - public event EventHandler SourceChanged; - - /// - /// The source property. - /// - public static readonly StyledProperty SourceProperty = AvaloniaProperty.Register(nameof(Source), ""); - - /// - /// The source shader text. - /// - public string Source - { - get - { - return GetValue(SourceProperty); - } - set - { - SetValue(SourceProperty, value); - ShaderEditorInstance?.Text = value; - } - } -} \ No newline at end of file diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Controls/TextViewer.axaml b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Controls/TextViewer.axaml deleted file mode 100644 index 5b822d8..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Controls/TextViewer.axaml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Controls/TextViewer.axaml.cs b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Controls/TextViewer.axaml.cs deleted file mode 100644 index 8670c78..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Controls/TextViewer.axaml.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Reactive; -using AvaloniaEdit.TextMate; -using System; -using TextMateSharp.Grammars; -using static AvaloniaEdit.TextMate.TextMate; - -namespace RisGameFramework.ShaderToolkit.Client.Controls; - -/// -/// The component which is created to view compiled shader content. -/// -public partial class TextViewer : UserControl -{ - private const string HLSL_GRAMMAR_EXTENSION = ".hlsl"; - - private RegistryOptions _registryOptions; - private Installation _textMateInstallation; - - /// - /// The constructor. - /// - public TextViewer() - { - InitializeComponent(); - - // Setup TextMate with a VS Code-like dark theme - _registryOptions = new RegistryOptions(ThemeName.DarkPlus); - _textMateInstallation = ShaderViewerInstance.InstallTextMate(_registryOptions); - Language language = _registryOptions.GetLanguageByExtension(HLSL_GRAMMAR_EXTENSION); - _textMateInstallation.SetGrammar(_registryOptions.GetScopeByLanguageId(_registryOptions.GetLanguageByExtension(HLSL_GRAMMAR_EXTENSION).Id)); - - - IObserver> observer = new AnonymousObserver>(args => - { - ShaderViewerInstance.Text = args.NewValue.Value; - }); - TextProperty.Changed.Subscribe(observer); - } - - /// - /// Invoked whenever source text is changed. - /// - public event EventHandler SourceChanged; - - /// - /// The compiled shader text property. - /// - public static readonly StyledProperty TextProperty = AvaloniaProperty.Register(nameof(Text), ""); - - /// - /// The compiled shader text. - /// - public string Text - { - get - { - return GetValue(TextProperty); - } - set - { - SetValue(TextProperty, value); - ShaderViewerInstance?.Text = value; - } - } -} \ No newline at end of file diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Resources/Colors.axaml b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Resources/Colors.axaml deleted file mode 100644 index c92ed81..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Resources/Colors.axaml +++ /dev/null @@ -1,11 +0,0 @@ - - - #2B2B2B - #D9DDDC - #888A89 - #A3A5A4 - - #D9DDDC - #2B2B2B - diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/RisGameFramework.ShaderToolkit.Client.csproj b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/RisGameFramework.ShaderToolkit.Client.csproj deleted file mode 100644 index d465818..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/RisGameFramework.ShaderToolkit.Client.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - net8.0 - enable - latest - true - RisGameFramework.ShaderToolkit - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/UserControls/TargetWidget.axaml b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/UserControls/TargetWidget.axaml deleted file mode 100644 index e5ed837..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/UserControls/TargetWidget.axaml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/UserControls/TargetWidget.axaml.cs b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/UserControls/TargetWidget.axaml.cs deleted file mode 100644 index 83790f2..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/UserControls/TargetWidget.axaml.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Controls.Platform; -using Avalonia.Markup.Xaml; - -namespace RisGameFramework.ShaderToolkit.UserControls; - -public partial class TargetWidget : UserControl -{ - public TargetWidget() - { - InitializeComponent(); - } -} \ No newline at end of file diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/ViewModels/EditorViewModel.cs b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/ViewModels/EditorViewModel.cs deleted file mode 100644 index 8a18337..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/ViewModels/EditorViewModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; - -namespace RisGameFramework.ShaderToolkit.Client.ViewModels; - -/// -/// The editor view model. -/// -public partial class EditorViewModel : ObservableObject -{ - -} diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/ViewModels/MainViewModel.cs b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/ViewModels/MainViewModel.cs deleted file mode 100644 index b23371a..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/ViewModels/MainViewModel.cs +++ /dev/null @@ -1,81 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using RisShaderToolkit; -using System.Collections.Generic; - -namespace RisGameFramework.ShaderToolkit.Client.ViewModels; - -public partial class MainViewModel : ViewModelBase -{ - private readonly ShaderCompiler _compiler = new ShaderCompiler(); - - [ObservableProperty] - private string _sourceShaderCode = @" - void main() - { - gl_Position = vec4(0.0, 0.0, 0.0, 1.0); - }"; - - [ObservableProperty] - private string _targetShaderCode = ""; - - internal List ProfileTypes { get; } = [ - ProfileType.GLSL, - ProfileType.SLANG, - ProfileType.HLSL - ]; - - internal List GlslProfiles { get; } = [ - GlslProfile.GLSL_330, - GlslProfile.GLSL_400, - GlslProfile.GLSL_410, - GlslProfile.GLSL_420, - GlslProfile.GLSL_430, - GlslProfile.GLSL_450, - GlslProfile.GLSL_460, - GlslProfile.GLES_300, - GlslProfile.GLES_310, - GlslProfile.GLES_320, - ]; - - internal List ShaderStages { get; } = [ - ShaderStage.VERTEX, - ShaderStage.FRAGMENT, - ShaderStage.COMPUTE, - ]; - - [ObservableProperty] - private ProfileType _sourceProfileType = ProfileType.SLANG; - - [ObservableProperty] - private ProfileType _targetProfileType = ProfileType.GLSL; - - [ObservableProperty] - private GlslProfile _targetGlslProfile = GlslProfile.GLES_300; - - [ObservableProperty] - private ShaderStage _targetShaderStage = ShaderStage.VERTEX; - - public MainViewModel() - { - - } - - public void Build() - { - CompileResult? compileResult = null; - if(SourceProfileType == ProfileType.SLANG && TargetProfileType == ProfileType.GLSL) - { - compileResult = _compiler.CompileSlangSourceCodeToGlsl(SourceShaderCode, TargetGlslProfile, ShaderStage.VERTEX, "main_vs"); - } - - if (compileResult?.Success == true) - { - TargetShaderCode = compileResult.SourceCode!; - } - } - - partial void OnSourceShaderCodeChanged(string value) - { - Build(); - } -} diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/ViewModels/ViewModelBase.cs b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/ViewModels/ViewModelBase.cs deleted file mode 100644 index 5705bcc..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/ViewModels/ViewModelBase.cs +++ /dev/null @@ -1,7 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; - -namespace RisGameFramework.ShaderToolkit.Client.ViewModels; - -public class ViewModelBase : ObservableObject -{ -} diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Views/MainView.axaml b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Views/MainView.axaml deleted file mode 100644 index 5db0c67..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Views/MainView.axaml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Views/MainView.axaml.cs b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Views/MainView.axaml.cs deleted file mode 100644 index 72b4af3..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Views/MainView.axaml.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Avalonia.Controls; -using AvaloniaEdit.TextMate; -using TextMateSharp.Grammars; - -namespace RisGameFramework.ShaderToolkit.Client.Views; - -public partial class MainView : UserControl -{ - private TextMate.Installation _textMateInstallation; - - public MainView() - { - InitializeComponent(); - - - } -} diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Views/MainWindow.axaml b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Views/MainWindow.axaml deleted file mode 100644 index 9a86df1..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Views/MainWindow.axaml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Views/MainWindow.axaml.cs b/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Views/MainWindow.axaml.cs deleted file mode 100644 index 06bbe0b..0000000 --- a/RisShaderToolkit/RisGameFramework.ShaderToolkit.Client/Views/MainWindow.axaml.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Avalonia.Controls; - -namespace RisGameFramework.ShaderToolkit.Client.Views; - -public partial class MainWindow : Window -{ - public MainWindow() - { - InitializeComponent(); - } -} diff --git a/RisShaderToolkit/RisShaderToolkit.Tests/Data/compile.json b/RisShaderToolkit/RisShaderToolkit.Tests/Data/compile.json index a4e3c7d..02eaffd 100644 --- a/RisShaderToolkit/RisShaderToolkit.Tests/Data/compile.json +++ b/RisShaderToolkit/RisShaderToolkit.Tests/Data/compile.json @@ -1,5 +1,4 @@ { - "compiler_verson": "0.0.1", "shaders": [ { "name": "sprite.slang", @@ -7,15 +6,6 @@ "source_profile": "slang", "stage": "vertex", "profile": "gles_300", - "rules": { - "input_name_rule": { - "prefix": "in_" - }, - "output_name_rule": { - "prefix": "out_", - "trim_entry_point_name": true - } - } }, { "name": "sprite.slang", @@ -23,15 +13,6 @@ "source_profile": "slang", "stage": "fragment", "profile": "gles_300", - "rules": { - "input_name_rule": { - "prefix": "in_" - }, - "output_name_rule": { - "prefix": "out_", - "trim_entry_point_name": true - } - } } ] } \ No newline at end of file diff --git a/RisShaderToolkit/RisShaderToolkit.Tests/Data/compile_slang_to_glsl.json b/RisShaderToolkit/RisShaderToolkit.Tests/Data/compile_slang_to_glsl.json new file mode 100644 index 0000000..5b66010 --- /dev/null +++ b/RisShaderToolkit/RisShaderToolkit.Tests/Data/compile_slang_to_glsl.json @@ -0,0 +1,16 @@ +{ + "shaders": [ + { + "name": "sprite.slang", + "profile": "glsl_450", + "stages": [ "vertex", "fragment" ], + "output_file": "sprite.glsl" + }, + { + "name": "sprite.slang", + "profile": "gles_300", + "stages": [ "vertex", "fragment" ], + "output_file": "sprite_300es.glsl" + } + ] +} \ No newline at end of file diff --git a/RisShaderToolkit/RisShaderToolkit.Tests/Data/compile_slang_to_spirv.json b/RisShaderToolkit/RisShaderToolkit.Tests/Data/compile_slang_to_spirv.json index 189bbbb..e5b7156 100644 --- a/RisShaderToolkit/RisShaderToolkit.Tests/Data/compile_slang_to_spirv.json +++ b/RisShaderToolkit/RisShaderToolkit.Tests/Data/compile_slang_to_spirv.json @@ -1,9 +1,9 @@ { - "compiler_verson": "0.0.1", "shaders": [ { "name": "sprite.slang", "profile": "spirv_1_2", + "stages": [ "vertex", "fragment" ], "output_file": "sprite.spv" } ] diff --git a/RisShaderToolkit/RisShaderToolkit.Tests/Data/compile_slang_to_wgsl.json b/RisShaderToolkit/RisShaderToolkit.Tests/Data/compile_slang_to_wgsl.json index 1ec2afd..dc654c5 100644 --- a/RisShaderToolkit/RisShaderToolkit.Tests/Data/compile_slang_to_wgsl.json +++ b/RisShaderToolkit/RisShaderToolkit.Tests/Data/compile_slang_to_wgsl.json @@ -4,6 +4,7 @@ { "name": "sprite.slang", "profile": "wgsl", + "stages": [ "vertex", "fragment" ], "output_file": "sprite.wgsl" } ] diff --git a/RisShaderToolkit/RisShaderToolkit.Tests/RisShaderToolkit.Tests.csproj b/RisShaderToolkit/RisShaderToolkit.Tests/RisShaderToolkit.Tests.csproj index 927499b..e4dbc9a 100644 --- a/RisShaderToolkit/RisShaderToolkit.Tests/RisShaderToolkit.Tests.csproj +++ b/RisShaderToolkit/RisShaderToolkit.Tests/RisShaderToolkit.Tests.csproj @@ -1,7 +1,7 @@ - net10.0 + net8.0 enable enable @@ -30,6 +30,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/RisShaderToolkit/RisShaderToolkit.Tests/SlangToGlslTests.cs b/RisShaderToolkit/RisShaderToolkit.Tests/SlangToGlslTests.cs new file mode 100644 index 0000000..bc7288e --- /dev/null +++ b/RisShaderToolkit/RisShaderToolkit.Tests/SlangToGlslTests.cs @@ -0,0 +1,150 @@ +namespace RisShaderToolkit.Tests; + +/// +/// Tests for compiling Slang shaders to GLSL. +/// +public class SlangToGlslTests +{ + /// + /// Test compiling a Slang shader source code to GLSL. + /// + [Fact] + public void TestCompileSlangToGlsl() + { + // Arrange + ShaderCompiler compiler = new ShaderCompiler(); + string slangSourceCode = File.ReadAllText("Data/sprite.slang"); + // Act + CompileResult vertexResult = compiler.CompileSlangToGlsl(slangSourceCode, + ShaderStage.VERTEX, + GlslProfile.GLSL_450, + "main_vs"); + + Assert.True(vertexResult.Success); + Assert.NotNull(vertexResult.SourceCode); + Assert.True(vertexResult.SourceCode.Length > 0); + Assert.Contains("main()", vertexResult.SourceCode); + + // Act + CompileResult fragmentResult = compiler.CompileSlangToGlsl(slangSourceCode, + ShaderStage.FRAGMENT, + GlslProfile.GLSL_450, + "main_fs"); + + Assert.True(fragmentResult.Success); + Assert.NotNull(fragmentResult.SourceCode); + Assert.True(fragmentResult.SourceCode.Length > 0); + Assert.Contains("main()", fragmentResult.SourceCode); + } + + /// + /// Test compiling a Slang shader source code to SPIR-V. + /// + [Fact] + public void TestCompileSlangToGlsl2() + { + // Arrange + ShaderCompiler compiler = new ShaderCompiler(); + string slangSourceCode = File.ReadAllText("Data/sprite.slang"); + // Act + CompileResult vertexResult = compiler.CompileSlangToGlsl(slangSourceCode, + ShaderStage.VERTEX); + + Assert.True(vertexResult.Success); + Assert.NotNull(vertexResult.SourceCode); + Assert.True(vertexResult.SourceCode.Length > 0); + Assert.Contains("main()", vertexResult.SourceCode); + + // Act + CompileResult fragmentResult = compiler.CompileSlangToGlsl(slangSourceCode, + ShaderStage.FRAGMENT); + + Assert.True(fragmentResult.Success); + Assert.NotNull(fragmentResult.SourceCode); + Assert.True(fragmentResult.SourceCode.Length > 0); + Assert.Contains("main()", fragmentResult.SourceCode); + } + + /// + /// Test compiling a Slang shader source code to GLSL. + /// + [Fact] + public void TestCompileSlangToGlsl300es() + { + // Arrange + ShaderCompiler compiler = new ShaderCompiler(); + string slangSourceCode = File.ReadAllText("Data/sprite.slang"); + // Act + CompileResult vertexResult = compiler.CompileSlangToGlsl(slangSourceCode, + ShaderStage.VERTEX, + GlslProfile.GLES_300, + "main_vs"); + + Assert.True(vertexResult.Success); + Assert.NotNull(vertexResult.SourceCode); + Assert.True(vertexResult.SourceCode.Length > 0); + Assert.Contains("main()", vertexResult.SourceCode); + + // Act + CompileResult fragmentResult = compiler.CompileSlangToGlsl(slangSourceCode, + ShaderStage.FRAGMENT, + GlslProfile.GLES_300, + "main_fs"); + + Assert.True(fragmentResult.Success); + Assert.NotNull(fragmentResult.SourceCode); + Assert.True(fragmentResult.SourceCode.Length > 0); + Assert.Contains("main()", fragmentResult.SourceCode); + } + + + /// + /// Test compiling a Slang shader source code to GLSL. + /// + [Fact] + public void TestCompileSlangToGlsl300Es2() + { + // Arrange + ShaderCompiler compiler = new ShaderCompiler(); + string slangSourceCode = File.ReadAllText("Data/sprite.slang"); + // Act + CompileResult vertexResult = compiler.CompileSlangToGlsl(slangSourceCode, + ShaderStage.VERTEX, + GlslProfile.GLES_300); + + Assert.True(vertexResult.Success); + Assert.NotNull(vertexResult.SourceCode); + Assert.True(vertexResult.SourceCode.Length > 0); + Assert.Contains("main()", vertexResult.SourceCode); + + // Act + CompileResult fragmentResult = compiler.CompileSlangToGlsl(slangSourceCode, + ShaderStage.FRAGMENT, + GlslProfile.GLES_300); + + Assert.True(fragmentResult.Success); + Assert.NotNull(fragmentResult.SourceCode); + Assert.True(fragmentResult.SourceCode.Length > 0); + Assert.Contains("main()", fragmentResult.SourceCode); + } + + /// + /// Compiles shaders from a JSON file. + /// Tests Slang to SpirV JSON compilation. + /// + [Fact] + public void TestCompileGlslJson() + { + // Arrange + ShaderCompiler compiler = new ShaderCompiler(); + string jsonFilePath = "Data/compile_slang_to_glsl.json"; + // Act + compiler.CompileFromJson(jsonFilePath); + // If we reach this point, the compilation was successful. + Assert.True(true); + foreach (var result in compiler.CompileFromJson(jsonFilePath).Results) + { + Assert.True(result.Success); + } + } +} diff --git a/RisShaderToolkit/RisShaderToolkit.Tests/SlangToSpirVTests.cs b/RisShaderToolkit/RisShaderToolkit.Tests/SlangToSpirVTests.cs index c9a8c5e..23d936e 100644 --- a/RisShaderToolkit/RisShaderToolkit.Tests/SlangToSpirVTests.cs +++ b/RisShaderToolkit/RisShaderToolkit.Tests/SlangToSpirVTests.cs @@ -15,14 +15,24 @@ public void TestCompileSlangToSpirV() ShaderCompiler compiler = new ShaderCompiler(); string slangSourceCode = File.ReadAllText("Data/sprite.slang"); // Act - CompileResult result = compiler.CompileSlangToSpirV(slangSourceCode, - [ShaderStage.VERTEX, ShaderStage.FRAGMENT], + CompileResult vertexResult = compiler.CompileSlangToSpirV(slangSourceCode, + [ShaderStage.VERTEX], SpirVProfile.SPIRV_1_2, - ["main_vs", "main_fs"]); + "main_vs"); - Assert.True(result.Success); - Assert.NotNull(result.BinarySourceCode); - Assert.True(result.BinarySourceCode.Length > 0); + Assert.True(vertexResult.Success); + Assert.NotNull(vertexResult.BinarySourceCode); + Assert.True(vertexResult.BinarySourceCode.Length > 0); + + // Act + CompileResult fragmentResult = compiler.CompileSlangToSpirV(slangSourceCode, + [ShaderStage.FRAGMENT], + SpirVProfile.SPIRV_1_2, + "main_fs"); + + Assert.True(fragmentResult.Success); + Assert.NotNull(fragmentResult.BinarySourceCode); + Assert.True(fragmentResult.BinarySourceCode.Length > 0); } /// @@ -35,10 +45,15 @@ public void TestCompileSlangToSpirV2() ShaderCompiler compiler = new ShaderCompiler(); string slangSourceCode = File.ReadAllText("Data/sprite.slang"); // Act - CompileResult result = compiler.CompileSlangToSpirV(slangSourceCode, [ShaderStage.VERTEX, ShaderStage.FRAGMENT], entryPoints: []); - Assert.True(result.Success); - Assert.NotNull(result.BinarySourceCode); - Assert.True(result.BinarySourceCode.Length > 0); + CompileResult vertexResult = compiler.CompileSlangToSpirV(slangSourceCode, [ShaderStage.VERTEX]); + Assert.True(vertexResult.Success); + Assert.NotNull(vertexResult.BinarySourceCode); + Assert.True(vertexResult.BinarySourceCode.Length > 0); + + CompileResult fragmentResult = compiler.CompileSlangToSpirV(slangSourceCode, [ShaderStage.FRAGMENT]); + Assert.True(fragmentResult.Success); + Assert.NotNull(fragmentResult.BinarySourceCode); + Assert.True(fragmentResult.BinarySourceCode.Length > 0); } /// @@ -46,8 +61,10 @@ public void TestCompileSlangToSpirV2() /// Tests Slang to SpirV JSON compilation. /// [Fact] - public void TestCompilSpirVJson() + public void TestCompileSpirVJson() { + // TODO: fix when removing automapper. + // Arrange ShaderCompiler compiler = new ShaderCompiler(); string jsonFilePath = "Data/compile_slang_to_spirv.json"; diff --git a/RisShaderToolkit/RisShaderToolkit/AutoMapperProfile.cs b/RisShaderToolkit/RisShaderToolkit/AutoMapperProfile.cs deleted file mode 100644 index ff7bd25..0000000 --- a/RisShaderToolkit/RisShaderToolkit/AutoMapperProfile.cs +++ /dev/null @@ -1,41 +0,0 @@ -using AutoMapper; -using RisShaderToolkit.Dto; -using RisShaderToolkit.Json; -using RisShaderToolkit.Rules; -using System; - -namespace RisShaderToolkit -{ - internal class AutoMapperProfile : Profile - { - private static AnyProfile? ResolveProfile(string? profileStr) - { - if (String.IsNullOrEmpty(profileStr)) - { - return null; - } - - if (Enum.TryParse(profileStr, ignoreCase: true, out AnyProfile value)) - { - return value; - } - - throw new InvalidOperationException($"Unsupported profile type: {profileStr}."); - } - - public AutoMapperProfile() - { - CreateMap(); - CreateMap(); - CreateMap() - .ForMember(dest => dest.OutputFilePath, opt => opt.MapFrom(src => src.OutputFile)) - .ForMember(dest => dest.InputNameRule, opt => opt.MapFrom(src => src.Rules != null ? src.Rules.InputNameRule : null)) - .ForMember(dest => dest.OutputNameRule, opt => opt.MapFrom(src => src.Rules != null ? src.Rules.OutputNameRule : null)) - .ForMember(dest => dest.Profile, opt => opt.MapFrom(src => ResolveProfile(src.Profile))) - .ForMember(dest => dest.SourceProfile, opt => opt.MapFrom(src => ResolveProfile(src.SourceProfile))); - - - - } - } -} diff --git a/RisShaderToolkit/RisShaderToolkit/Dto/ShaderCompileTaskDto.cs b/RisShaderToolkit/RisShaderToolkit/Dto/ShaderCompileTaskDto.cs index 140977a..adbda50 100644 --- a/RisShaderToolkit/RisShaderToolkit/Dto/ShaderCompileTaskDto.cs +++ b/RisShaderToolkit/RisShaderToolkit/Dto/ShaderCompileTaskDto.cs @@ -1,6 +1,4 @@ -using RisShaderToolkit.Rules; - -namespace RisShaderToolkit.Dto; +namespace RisShaderToolkit.Dto; /// /// The shader to compile. @@ -25,7 +23,7 @@ internal record ShaderCompileTaskDto /// /// The stage of the shader to compile. /// - public ShaderStage[]? Stages { get; set; } + public List Stages { get; set; } = new(); /// /// The target profile for the shader. @@ -36,14 +34,4 @@ internal record ShaderCompileTaskDto /// The source profile of the shader. /// public AnyProfile? SourceProfile { get; set; } - - /// - /// The input name rule to apply to the shader. - /// - public ReplaceStageInputNameRule? InputNameRule { get; set; } - - /// - /// The output name rule to apply to the shader. - /// - public ReplaceStageOutputNameRule? OutputNameRule { get; set; } } diff --git a/RisShaderToolkit/RisShaderToolkit/Json/ReplaceStageInputNameRuleJson.cs b/RisShaderToolkit/RisShaderToolkit/Json/ReplaceStageInputNameRuleJson.cs deleted file mode 100644 index 28b2468..0000000 --- a/RisShaderToolkit/RisShaderToolkit/Json/ReplaceStageInputNameRuleJson.cs +++ /dev/null @@ -1,14 +0,0 @@ - - -namespace RisShaderToolkit.Json; - -/// -/// The input name rule JSON representation. -/// -public class ReplaceStageInputNameRuleJson -{ - /// - /// The prefix to add to input names. - /// - public string Prefix { get; set; } = string.Empty; -} diff --git a/RisShaderToolkit/RisShaderToolkit/Json/ReplaceStageOutputNameRuleJson.cs b/RisShaderToolkit/RisShaderToolkit/Json/ReplaceStageOutputNameRuleJson.cs deleted file mode 100644 index 5e0ae88..0000000 --- a/RisShaderToolkit/RisShaderToolkit/Json/ReplaceStageOutputNameRuleJson.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace RisShaderToolkit.Json; - -/// -/// The output name rule JSON representation. -/// -public class ReplaceStageOutputNameRuleJson -{ - /// - /// The prefix to add to the stage output names. - /// - public string? Prefix { get; set; } - - /// - /// Sets whether to trim the entry point name from output variable names. - /// - public bool TrimEntryPointName { get; set; } -} diff --git a/RisShaderToolkit/RisShaderToolkit/Json/RulesJson.cs b/RisShaderToolkit/RisShaderToolkit/Json/RulesJson.cs deleted file mode 100644 index 3c21c6e..0000000 --- a/RisShaderToolkit/RisShaderToolkit/Json/RulesJson.cs +++ /dev/null @@ -1,19 +0,0 @@ - - -namespace RisShaderToolkit.Json; - -/// -/// The rules JSON representation. -/// -public class RulesJson -{ - /// - /// The input name rule. - /// - public ReplaceStageInputNameRuleJson? InputNameRule { get; set; } - - /// - /// The output name rule. - /// - public ReplaceStageOutputNameRuleJson? OutputNameRule { get; set; } -} diff --git a/RisShaderToolkit/RisShaderToolkit/Json/ShaderJson.cs b/RisShaderToolkit/RisShaderToolkit/Json/ShaderJson.cs index 92895e4..f33fb45 100644 --- a/RisShaderToolkit/RisShaderToolkit/Json/ShaderJson.cs +++ b/RisShaderToolkit/RisShaderToolkit/Json/ShaderJson.cs @@ -20,6 +20,12 @@ public class ShaderJson /// public string[]? EntryPoints { get; set; } + /// + /// The stage of the shader to compile. + /// This is used if the shader only has one stage, and is mutually exclusive with the `stages` property, which is used if the shader has multiple stages. + /// + public ShaderStage? Stage { get; set; } + /// /// The stage of the shader to compile. /// @@ -34,9 +40,4 @@ public class ShaderJson /// The source profile of the shader. /// public string? SourceProfile { get; set; } = string.Empty; - - /// - /// The rules to apply to the shader. - /// - public RulesJson? Rules { get; set; } } diff --git a/RisShaderToolkit/RisShaderToolkit/JsonReader.cs b/RisShaderToolkit/RisShaderToolkit/JsonReader.cs index f104de9..4a0d844 100644 --- a/RisShaderToolkit/RisShaderToolkit/JsonReader.cs +++ b/RisShaderToolkit/RisShaderToolkit/JsonReader.cs @@ -1,23 +1,28 @@ -using AutoMapper; -using Microsoft.Extensions.Logging.Abstractions; -using RisShaderToolkit.Dto; +using RisShaderToolkit.Dto; using RisShaderToolkit.Json; using System.Text.Json; namespace RisShaderToolkit { + /// + /// This class is responsible for reading a JSON file that describes shader compilation tasks and + /// converting it into a list of `ShaderCompileTaskDto` objects that can be used by the rest of the application. + /// internal class JsonReader { - private readonly IMapper _mapper; - - internal JsonReader() + private static AnyProfile? ResolveProfile(string? profileStr) { - MapperConfiguration config = new (cfg => + if (String.IsNullOrEmpty(profileStr)) + { + return null; + } + + if (Enum.TryParse(profileStr, ignoreCase: true, out AnyProfile value)) { - cfg.AddProfile(); - }, new NullLoggerFactory()); + return value; + } - _mapper = config.CreateMapper(); + throw new InvalidOperationException($"Unsupported profile type: {profileStr}."); } /// @@ -42,17 +47,35 @@ public IReadOnlyList LoadJson(string jsonFilePath) throw new InvalidOperationException("Failed to deserialize compile JSON."); } - List results = new (); + List results = new(); - foreach (ShaderJson shader in compileJson.Shaders) + foreach (ShaderJson shaderJson in compileJson.Shaders) { // Create a task for each shader - string inputFilePath = Path.IsPathRooted(shader.Name) - ? shader.Name - : Path.Combine(directory, shader.Name); + string inputFilePath = Path.IsPathRooted(shaderJson.Name) + ? shaderJson.Name + : Path.Combine(directory, shaderJson.Name); + + ShaderCompileTaskDto compileTask = new() + { + OutputFilePath = shaderJson.OutputFile, + EntryPoints = shaderJson.EntryPoints, + + }; + + if(shaderJson.Stage.HasValue) + { + compileTask.Stages.Add(shaderJson.Stage.Value); + } + + if(shaderJson.Stages != null) + { + compileTask.Stages.AddRange(shaderJson.Stages); + } - ShaderCompileTaskDto compileTask = _mapper.Map(shader); compileTask.InputFilePath = inputFilePath.Replace("\\", "/"); + compileTask.SourceProfile = ResolveProfile(shaderJson.SourceProfile); + compileTask.Profile = ResolveProfile(shaderJson.Profile) ?? throw new InvalidOperationException("JSON entry in 'shaders' is missing 'profile' property."); results.Add(compileTask); } diff --git a/RisShaderToolkit/RisShaderToolkit/RisShaderToolkit.csproj b/RisShaderToolkit/RisShaderToolkit/RisShaderToolkit.csproj index 63806ba..a4fcaad 100644 --- a/RisShaderToolkit/RisShaderToolkit/RisShaderToolkit.csproj +++ b/RisShaderToolkit/RisShaderToolkit/RisShaderToolkit.csproj @@ -7,17 +7,24 @@ true RisShaderToolkit True - 0.0.1-preview.5 + 0.2.0 - - - - + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + \ No newline at end of file diff --git a/RisShaderToolkit/RisShaderToolkit/Rules/ReplaceStageInputNameRule.cs b/RisShaderToolkit/RisShaderToolkit/Rules/ReplaceStageInputNameRule.cs deleted file mode 100644 index 8a48270..0000000 --- a/RisShaderToolkit/RisShaderToolkit/Rules/ReplaceStageInputNameRule.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace RisShaderToolkit.Rules; - -/// -/// The rule to replace stage input names. -/// -public class ReplaceStageInputNameRule -{ - /// - /// The prefix to add to the stage input names. - /// - public string? Prefix { get; set; } -} diff --git a/RisShaderToolkit/RisShaderToolkit/Rules/ReplaceStageOutputNameRule.cs b/RisShaderToolkit/RisShaderToolkit/Rules/ReplaceStageOutputNameRule.cs deleted file mode 100644 index cdf8a2e..0000000 --- a/RisShaderToolkit/RisShaderToolkit/Rules/ReplaceStageOutputNameRule.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace RisShaderToolkit.Rules; - -/// -/// The rule to replace stage output names. -/// -public class ReplaceStageOutputNameRule -{ - /// - /// The prefix to add to the stage output names. - /// - public string? Prefix { get; set; } - - /// - /// Sets whether to trim the entry point name from output variable names. - /// - public bool TrimEntryPointName { get; set; } -} diff --git a/RisShaderToolkit/RisShaderToolkit/ShaderCompiler.cs b/RisShaderToolkit/RisShaderToolkit/ShaderCompiler.cs index 374a265..92863d6 100644 --- a/RisShaderToolkit/RisShaderToolkit/ShaderCompiler.cs +++ b/RisShaderToolkit/RisShaderToolkit/ShaderCompiler.cs @@ -2,7 +2,6 @@ using RisShaderToolkit.CObjects; using RisShaderToolkit.Dto; -using RisShaderToolkit.Rules; using System.Runtime.InteropServices; namespace RisShaderToolkit; @@ -20,28 +19,8 @@ public class ShaderCompiler : IDisposable [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] static extern void free_compiler(IntPtr compiler); - //[DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - //static unsafe extern IntPtr compile_slang_to_glsl( - // IntPtr compilerPtr, - // IntPtr inputFilePath, - // GlslProfile glslProfile, - // ShaderStage shaderStage, - // IntPtr entryPoint, - // IntPtr inputRule, - // IntPtr outputRule); - - //[DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - //static unsafe extern IntPtr compile_slang_source_code_to_glsl( - // IntPtr compilerPtr, - // IntPtr slangSourceCode, - // GlslProfile glslProfile, - // ShaderStage shaderStage, - // IntPtr entryPoint, - // IntPtr inputRule, - // IntPtr outputRule); - [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - static unsafe extern IntPtr compile_slang_to_wgsl( + static unsafe extern IntPtr compile_slang_to_wgsl_ext( IntPtr compilerPtr, IntPtr sourceCode, ShaderStage* shaderStages, @@ -50,7 +29,14 @@ static unsafe extern IntPtr compile_slang_to_wgsl( uint entryPointsCount); [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - static unsafe extern IntPtr compile_slang_to_spirv( + static unsafe extern IntPtr compile_slang_to_wgsl( + IntPtr compilerPtr, + IntPtr sourceCode, + ShaderStage* shaderStages, + uint shaderStagesCount); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + static unsafe extern IntPtr compile_slang_to_spirv_ext( IntPtr compilerPtr, IntPtr sourceCode, ShaderStage* shaderStages, @@ -60,6 +46,32 @@ static unsafe extern IntPtr compile_slang_to_spirv( SpirVProfile spirVProfile ); + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + static unsafe extern IntPtr compile_slang_to_spirv( + IntPtr compilerPtr, + IntPtr sourceCode, + ShaderStage* shaderStages, + uint shaderStagesCount, + SpirVProfile spirVProfile + ); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + static unsafe extern IntPtr compile_slang_to_glsl_ext( + IntPtr compilerPtr, + IntPtr sourceCode, + ShaderStage shaderStage, + IntPtr entryPoint, + GlslProfile glslProfile + ); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + static unsafe extern IntPtr compile_slang_to_glsl( + IntPtr compilerPtr, + IntPtr sourceCode, + ShaderStage shaderStage, + GlslProfile glslProfile + ); + private readonly IntPtr NativePtr; private readonly JsonReader _jsonReader = new(); @@ -84,29 +96,27 @@ public ShaderCompiler() } } - private void WriteGlslShaderToFile(ShaderCompileTaskDto task, CompileResult result, GlslProfile glslProfile) + private void WriteGlslShaderToFile(ShaderCompileTaskDto task, CompileResult result, GlslProfile glslProfile, ShaderStage stage) { - throw new InvalidOperationException("hello"); - - //if (!result.Success) - //{ - // return; - //} - - //string? outputFilePath = task.OutputFilePath; - //if (string.IsNullOrEmpty(outputFilePath)) - //{ - // string[] split = task.InputFilePath.Split('.'); - // split = split[..^1]; // Remove extension - - // string name = String.Join("", split); - // outputFilePath = $"{name}_{_shortStageName[task.Stage.Value]}_{glslProfile.ToString().ToLower()}.glsl"; - //} - - //if (result.Success && result.SourceCode != null) - //{ - // File.WriteAllText(outputFilePath, result.SourceCode); - //} + if (!result.Success) + { + return; + } + + string? outputFilePath = task.OutputFilePath; + if (string.IsNullOrEmpty(outputFilePath)) + { + string[] split = task.InputFilePath.Split('.'); + split = split[..^1]; // Remove extension + + string name = String.Join("", split); + outputFilePath = $"{name}_{_shortStageName[stage]}_{glslProfile.ToString().ToLower()}.glsl"; + } + + if (result.Success && result.SourceCode != null) + { + File.WriteAllText(outputFilePath, result.SourceCode); + } } private void WriteWgslShaderToFile(ShaderCompileTaskDto task, CompileResult result) @@ -156,11 +166,11 @@ private bool HandleSpirVProfile(List results, ShaderCompileTaskDt { string sourceCode = File.ReadAllText(compileTask.InputFilePath); - CompileResult result = CompileSlangToSpirV( + var result = CompileSlangToSpirV( sourceCode, - compileTask.Stages ?? [ShaderStage.VERTEX, ShaderStage.FRAGMENT], + compileTask.Stages.ToArray(), spirVProfile, - compileTask.EntryPoints ?? [] + compileTask?.EntryPoints ); results.Add(result); @@ -172,6 +182,27 @@ private bool HandleSpirVProfile(List results, ShaderCompileTaskDt return false; } + private bool HandleGlslProfile(List results, ShaderCompileTaskDto compileTask) + { + if (ProfileResolver.IsGlslProfile(compileTask.Profile, out GlslProfile glslProfile)) + { + string sourceCode = File.ReadAllText(compileTask.InputFilePath); + foreach (ShaderStage shaderStage in compileTask.Stages) + { + var result = CompileSlangToGlsl( + sourceCode, + shaderStage, + glslProfile, + compileTask?.EntryPoints != null && compileTask.EntryPoints.Length > 0 ? compileTask.EntryPoints[0] : null + ); + results.Add(result); + WriteGlslShaderToFile(compileTask, result, glslProfile, shaderStage); + } + return true; + } + return false; + } + /// /// Compiles shaders based on the given JSON file. /// @@ -190,28 +221,19 @@ public AggregatedCompileResult CompileFromJson(string jsonFilePath) CompileResult result = CompileSlangToWgsl( sourceCode, - compileTask.Stages ?? [ShaderStage.VERTEX, ShaderStage.FRAGMENT], + compileTask.Stages.ToArray(), compileTask.EntryPoints ?? []); results.Add(result); WriteWgslShaderToFile(compileTask, result); } - else if(HandleSpirVProfile(results, compileTask)) + else if (HandleSpirVProfile(results, compileTask)) { continue; } - else if (compileTask.SourceProfile == AnyProfile.SLANG && ProfileResolver.IsGlslProfile(compileTask.Profile, out GlslProfile glslProfile)) + else if (HandleGlslProfile(results, compileTask)) { - //CompileResult result = CompileSlangToGlsl( - // compileTask.InputFilePath, - // glslProfile, - // compileTask.Stage.Value, - // compileTask.EntryPoint, - // compileTask.InputNameRule, - // compileTask.OutputNameRule); - - //results.Add(result); - //WriteGlslShaderToFile(compileTask, result, glslProfile); + continue; } else { @@ -223,145 +245,6 @@ public AggregatedCompileResult CompileFromJson(string jsonFilePath) return new(results); } - /// - /// Transpiles the given Slang shader to GLSL. - /// - /// The input file path. - /// The . - /// The . - /// The entry point. - /// The optional . - /// The optional . - /// - /// - //public CompileResult CompileSlangToGlsl( - // string inputFilePath, - // GlslProfile glslProfile, - // ShaderStage shaderStage, - // string entryPoint = "main", - // ReplaceStageInputNameRule? inputNameRule = null, - // ReplaceStageOutputNameRule? outputNameRule = null - // ) - //{ - // IntPtr inputFilePathPtr = Marshal.StringToHGlobalAnsi(inputFilePath); - // IntPtr entryPointPtr = Marshal.StringToHGlobalAnsi(entryPoint); - // IntPtr inputRulePtr = CReplaceStageInputNameRule.AllocNative(inputNameRule); - // IntPtr outputRulePtr = CReplaceStageOutputNameRule.AllocNative(outputNameRule); - // try - // { - // IntPtr resultPtr = compile_slang_to_glsl( - // NativePtr, - // inputFilePathPtr, - // glslProfile, - // shaderStage, - // entryPointPtr, - // inputRulePtr, - // outputRulePtr); - - // if (resultPtr == IntPtr.Zero) - // { - // throw new InvalidOperationException("Compilation failed: No result returned."); - // } - - // CCompileResult cCompileResult = Marshal.PtrToStructure(resultPtr); - - // CompileResult result = new CompileResult - // { - // Success = cCompileResult.Success, - // SourceCode = cCompileResult.Success - // ? Marshal.PtrToStringAnsi(cCompileResult.SourceCode) ?? string.Empty - // : null, - // ErrorMessage = cCompileResult.Success - // ? null - // : Marshal.PtrToStringAnsi(cCompileResult.ErrorMessage) ?? "Unknown error.", - // EntryPoints = [entryPoint], - // InputFilePath = inputFilePath, - // GlslProfile = glslProfile, - // ShaderStages = [shaderStage] - // }; - // cCompileResult.Dispose(); - // return result; - - // } - // finally - // { - // Marshal.FreeHGlobal(inputFilePathPtr); - // Marshal.FreeHGlobal(entryPointPtr); - // CReplaceStageInputNameRule.FreeNative(inputRulePtr); - // CReplaceStageOutputNameRule.FreeNative(outputRulePtr); - // } - //} - - /// - /// Transpiles the given Slang shader to GLSL. - /// - /// The Slang source code. - /// The . - /// The . - /// The entry point. - /// The optional . - /// The optional . - /// - /// - //public CompileResult CompileSlangSourceCodeToGlsl( - // string slangSourceCode, - // GlslProfile glslProfile, - // ShaderStage shaderStage, - // string entryPoint = "main", - // ReplaceStageInputNameRule? inputNameRule = null, - // ReplaceStageOutputNameRule? outputNameRule = null - // ) - //{ - // IntPtr slangSourceCodePtr = Marshal.StringToHGlobalAnsi(slangSourceCode); - // IntPtr entryPointPtr = Marshal.StringToHGlobalAnsi(entryPoint); - // IntPtr inputRulePtr = CReplaceStageInputNameRule.AllocNative(inputNameRule); - // IntPtr outputRulePtr = CReplaceStageOutputNameRule.AllocNative(outputNameRule); - // try - // { - // IntPtr resultPtr = compile_slang_source_code_to_glsl( - // NativePtr, - // slangSourceCodePtr, - // glslProfile, - // shaderStage, - // entryPointPtr, - // inputRulePtr, - // outputRulePtr); - - // if (resultPtr == IntPtr.Zero) - // { - // throw new InvalidOperationException("Compilation failed: No result returned."); - // } - - // CCompileResult cCompileResult = Marshal.PtrToStructure(resultPtr); - - // CompileResult result = new CompileResult - // { - // Success = cCompileResult.Success, - // SourceCode = cCompileResult.Success - // ? Marshal.PtrToStringAnsi(cCompileResult.SourceCode) ?? string.Empty - // : null, - // ErrorMessage = cCompileResult.Success - // ? null - // : Marshal.PtrToStringAnsi(cCompileResult.ErrorMessage) ?? "Unknown error.", - // EntryPoints = [entryPoint], - // InputFilePath = slangSourceCode, - // GlslProfile = glslProfile, - // ShaderStages = [shaderStage] - // }; - // cCompileResult.Dispose(); - // return result; - - // } - // finally - // { - // Marshal.FreeHGlobal(slangSourceCodePtr); - // Marshal.FreeHGlobal(entryPointPtr); - // CReplaceStageInputNameRule.FreeNative(inputRulePtr); - // CReplaceStageOutputNameRule.FreeNative(outputRulePtr); - // } - // } - - /// /// Compiles the given Slang shader to WGSL. /// @@ -386,19 +269,36 @@ params string[] entryPoints shaderStagesPtr[i] = shaderStages[i]; } - IntPtr* entryPointsPtr = stackalloc IntPtr[entryPoints.Length]; - for (int i = 0; i < entryPoints.Length; i++) + // Generate default entry points if not provided + if (entryPoints == null || entryPoints.Length == 0) { - entryPointsPtr[i] = Marshal.StringToHGlobalAnsi(entryPoints[i]); + entryPoints = new string[shaderStages.Length]; + for (int i = 0; i < shaderStages.Length; i++) + { + entryPoints[i] = $"main_{_shortStageName[shaderStages[i]]}"; + } } try { - IntPtr resultPtr = compile_slang_to_wgsl( - NativePtr, - slangSourceCodePtr, - shaderStagesPtr, (uint)shaderStages.Length, - entryPointsPtr, (uint)entryPoints.Length); + IntPtr resultPtr = IntPtr.Zero; + var entryPointsCount = entryPoints.Length; + IntPtr* entryPointsPtr = stackalloc IntPtr[entryPointsCount]; + for (int i = 0; i < entryPointsCount; i++) + { + entryPointsPtr[i] = Marshal.StringToHGlobalAnsi(entryPoints[i]); + } + + resultPtr = compile_slang_to_wgsl_ext( + NativePtr, + slangSourceCodePtr, + shaderStagesPtr, (uint)shaderStages.Length, + entryPointsPtr, (uint)entryPointsCount); + + for (int i = 0; i < entryPointsCount; i++) + { + Marshal.FreeHGlobal(entryPointsPtr[i]); + } if (resultPtr == IntPtr.Zero) { @@ -423,13 +323,20 @@ params string[] entryPoints return result; } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + return new CompileResult + { + Success = false, + ErrorMessage = $"An error occurred during compilation: {ex.Message}", + EntryPoints = entryPoints.ToArray(), + ShaderStages = shaderStages + }; + } finally { Marshal.FreeHGlobal(slangSourceCodePtr); - for (int i = 0; i < entryPoints.Length; i++) - { - Marshal.FreeHGlobal(entryPointsPtr[i]); - } } } } @@ -438,7 +345,7 @@ params string[] entryPoints /// Compiles the given Slang shader to WGSL. /// /// The slang source code. - /// The 's to compile. + /// The s to compile. /// The optional . Default is SPIRV_1_2. /// The optional entry points. /// The . @@ -449,6 +356,8 @@ public CompileResult CompileSlangToSpirV( params string[] entryPoints ) { + entryPoints = entryPoints ?? Array.Empty(); + IntPtr slangSourceCodePtr = Marshal.StringToHGlobalAnsi(slangSourceCode); CCompileResult compileResult = default; @@ -460,20 +369,43 @@ params string[] entryPoints shaderStagesPtr[i] = shaderStages[i]; } - IntPtr* entryPointsPtr = stackalloc IntPtr[entryPoints.Length]; - for (int i = 0; i < entryPoints.Length; i++) - { - entryPointsPtr[i] = Marshal.StringToHGlobalAnsi(entryPoints[i]); - } - try { - IntPtr resultPtr = compile_slang_to_spirv( - NativePtr, - slangSourceCodePtr, - shaderStagesPtr, (uint)shaderStages.Length, - entryPointsPtr, (uint)entryPoints.Length, - spirVProfile); + IntPtr resultPtr; + + if (entryPoints.Length > 0) + { + int entryPointsCount = entryPoints.Length; + IntPtr* entryPointsPtr = stackalloc IntPtr[entryPointsCount]; + for (int i = 0; i < entryPointsCount; i++) + { + entryPointsPtr[i] = Marshal.StringToHGlobalAnsi(entryPoints[i]); + } + + resultPtr = compile_slang_to_spirv_ext( + NativePtr, + slangSourceCodePtr, + shaderStagesPtr, + (uint)shaderStages.Length, + entryPointsPtr, + (uint)entryPoints.Length, + spirVProfile); + + for (int i = 0; i < entryPoints.Length; i++) + { + Marshal.FreeHGlobal(entryPointsPtr[i]); + } + } + else + { + // If no entry points are provided, pass null pointers and zero count + resultPtr = compile_slang_to_spirv( + NativePtr, + slangSourceCodePtr, + shaderStagesPtr, + (uint)shaderStages.Length, + spirVProfile); + } if (resultPtr == IntPtr.Zero) { @@ -509,10 +441,96 @@ params string[] entryPoints finally { Marshal.FreeHGlobal(slangSourceCodePtr); - for (int i = 0; i < entryPoints.Length; i++) + + } + } + } + + /// + /// Compiles the given Slang shader to GLSL. + /// + /// The slang source code. + /// The to compile. + /// The optional . Default is GLSL_450. + /// The optional entry point. + /// The . + /// + public CompileResult CompileSlangToGlsl( + string slangSourceCode, + ShaderStage shaderStage, + GlslProfile glslProfile = GlslProfile.GLSL_450, + string? entryPoint = null + ) + { + IntPtr slangSourceCodePtr = Marshal.StringToHGlobalAnsi(slangSourceCode); + + CCompileResult compileResult = default; + unsafe + { + try + { + IntPtr resultPtr; + if (!String.IsNullOrEmpty(entryPoint)) { - Marshal.FreeHGlobal(entryPointsPtr[i]); + var entryPointPtr = Marshal.StringToHGlobalAnsi(entryPoint); + + resultPtr = compile_slang_to_glsl_ext( + NativePtr, + slangSourceCodePtr, + shaderStage, + entryPointPtr, + glslProfile); + + Marshal.FreeHGlobal(entryPointPtr); + } + else + { + resultPtr = compile_slang_to_glsl( + NativePtr, + slangSourceCodePtr, + shaderStage, + glslProfile); } + + if (resultPtr == IntPtr.Zero) + { + throw new InvalidOperationException("Compilation failed: No result returned."); + } + + compileResult = Marshal.PtrToStructure(resultPtr); + + CompileResult result = new CompileResult + { + Success = compileResult.Success, + SourceCode = compileResult.Success + ? Marshal.PtrToStringAnsi(compileResult.SourceCode) ?? string.Empty + : null, + ErrorMessage = compileResult.Success + ? null + : Marshal.PtrToStringAnsi(compileResult.ErrorMessage) ?? "Unknown error.", + EntryPoints = !string.IsNullOrEmpty(entryPoint) ? [entryPoint] : Array.Empty(), + ShaderStages = [shaderStage], + }; + + result.SourceCode = Marshal.PtrToStringAnsi(compileResult.SourceCode); + + compileResult.Dispose(); + return result; + + } + catch (Exception ex) + { + return new CompileResult + { + Success = false, + ErrorMessage = $"An error occurred during compilation: {ex.Message}", + EntryPoints = !string.IsNullOrEmpty(entryPoint) ? [entryPoint] : Array.Empty(), + ShaderStages = [shaderStage], + }; + } + finally + { + Marshal.FreeHGlobal(slangSourceCodePtr); } } } diff --git a/RisShaderToolkit/RisShaderToolkit/Slang/SlangGlobalSession.cs b/RisShaderToolkit/RisShaderToolkit/Slang/SlangGlobalSession.cs index 3a18bae..bc0bbf0 100644 --- a/RisShaderToolkit/RisShaderToolkit/Slang/SlangGlobalSession.cs +++ b/RisShaderToolkit/RisShaderToolkit/Slang/SlangGlobalSession.cs @@ -78,6 +78,11 @@ public SlangProfileID FindProfile(string profileName) } } + /// + /// Creates a new Slang session. + /// + /// The . + /// The . public SlangSession CreateSession(SlangSessionDescription sessionDescription) { unsafe diff --git a/RisShaderToolkit/RisShaderToolkit/runtimes/linux-x64/native/libshader_toolkit_c.so b/RisShaderToolkit/RisShaderToolkit/runtimes/linux-x64/native/libshader_toolkit_c.so old mode 100644 new mode 100755 index 8186c74..8ab092a Binary files a/RisShaderToolkit/RisShaderToolkit/runtimes/linux-x64/native/libshader_toolkit_c.so and b/RisShaderToolkit/RisShaderToolkit/runtimes/linux-x64/native/libshader_toolkit_c.so differ diff --git a/RisShaderToolkit/RisShaderToolkit/runtimes/osx-arm64/native/libshader_toolkit_c.dylib b/RisShaderToolkit/RisShaderToolkit/runtimes/osx-arm64/native/libshader_toolkit_c.dylib index 2659136..44429a7 100644 Binary files a/RisShaderToolkit/RisShaderToolkit/runtimes/osx-arm64/native/libshader_toolkit_c.dylib and b/RisShaderToolkit/RisShaderToolkit/runtimes/osx-arm64/native/libshader_toolkit_c.dylib differ diff --git a/RisShaderToolkit/RisShaderToolkit/runtimes/win-x64/native/shader_toolkit_c.dll b/RisShaderToolkit/RisShaderToolkit/runtimes/win-x64/native/shader_toolkit_c.dll index 204f205..30b7988 100644 Binary files a/RisShaderToolkit/RisShaderToolkit/runtimes/win-x64/native/shader_toolkit_c.dll and b/RisShaderToolkit/RisShaderToolkit/runtimes/win-x64/native/shader_toolkit_c.dll differ diff --git a/RisShaderToolkit/RisShaderToolkit/runtimes/win-x64/native/slang.dll b/RisShaderToolkit/RisShaderToolkit/runtimes/win-x64/native/slang.dll index f467a48..3b12b67 100644 Binary files a/RisShaderToolkit/RisShaderToolkit/runtimes/win-x64/native/slang.dll and b/RisShaderToolkit/RisShaderToolkit/runtimes/win-x64/native/slang.dll differ diff --git a/ShaderToolkitC/README.md b/ShaderToolkitC/README.md index 05b31b0..757696e 100644 --- a/ShaderToolkitC/README.md +++ b/ShaderToolkitC/README.md @@ -71,7 +71,6 @@ The Windows script installs dependencies with vcpkg and configures CMake. .\scripts\build_windows.ps1 ``` - Build output: `cmake-build-debugvisualstudio/` Notes: diff --git a/ShaderToolkitC/include/compiler/c_compiler.hpp b/ShaderToolkitC/include/compiler/c_compiler.hpp index 95f73d5..bd9416a 100644 --- a/ShaderToolkitC/include/compiler/c_compiler.hpp +++ b/ShaderToolkitC/include/compiler/c_compiler.hpp @@ -18,33 +18,19 @@ extern "C" { API_EXPORT char* get_last_error_message(); - // API_EXPORT - //void* compile_slang_to_glsl( - // void* compilerPtr, - // const char* inputFilePath, - // int profile, // GlslProfile as int - // int shaderStage, // ShaderStage as int - // const char* entryPoint, - // c_ReplaceStageInputNameRule* inputRule, - // c_ReplaceStageOutputNameRule* outputRule - //); - - //API_EXPORT - //void* compile_slang_source_code_to_glsl( - //void* compilerPtr, - //const char* slangSourceCode, - //int profile, // GlslProfile as int - //int shaderStage, // ShaderStage as int - //const char* entryPoint, - //c_ReplaceStageInputNameRule* inputRule, - //c_ReplaceStageOutputNameRule* outputRule - //); - API_EXPORT void* compile_slang_to_wgsl( void* compilerPtr, const char* slangSourceCode, - int32_t* shaderStages, + int32_t* shaderStages, + uint32_t shaderStagesCount + ); + + API_EXPORT + void* compile_slang_to_wgsl_ext( + void* compilerPtr, + const char* slangSourceCode, + int32_t* shaderStages, uint32_t shaderStagesCount, const char** entryPoints, uint32_t entryPointsCount @@ -52,6 +38,15 @@ extern "C" { API_EXPORT void* compile_slang_to_spirv( + void* compilerPtr, + const char* slangSourceCode, + int32_t* shaderStages, + uint32_t shaderStagesCount, + ris_shader_toolkit::SpirVProfile profile + ); + + API_EXPORT + void* compile_slang_to_spirv_ext( void* compilerPtr, const char* slangSourceCode, int32_t* shaderStages, @@ -59,7 +54,26 @@ extern "C" { const char** entryPoints, uint32_t entryPointsCount, ris_shader_toolkit::SpirVProfile profile - ); + ); + + API_EXPORT + void* compile_slang_to_glsl( + void* compilerPtr, + const char* slangSourceCode, + int32_t shaderStage, + ris_shader_toolkit::GlslProfile profile + ); + + API_EXPORT + void* compile_slang_to_glsl_ext( + void* compilerPtr, + const char* slangSourceCode, + int32_t shaderStage, + const char* entryPoint, + ris_shader_toolkit::GlslProfile profile + ); + + //! Frees the Compiler instance. //! @param compilerPtr A pointer to the Compiler instance to free. diff --git a/ShaderToolkitC/include/compiler/compile_result.hpp b/ShaderToolkitC/include/compiler/compile_result.hpp index 39de46f..cf5dfe5 100644 --- a/ShaderToolkitC/include/compiler/compile_result.hpp +++ b/ShaderToolkitC/include/compiler/compile_result.hpp @@ -26,12 +26,12 @@ namespace ris_shader_toolkit { //! @param success True if the compilation was successful, false otherwise. //! @param sourceCode The compiled source code in binary format if the compilation was successful. //! @param error The error message if the compilation failed. - CompileResult(bool success, const std::vector& sourceCode, const std::string& error = ""); + CompileResult(bool success, const std::vector& sourceCode, const std::string& error = ""); //! Constructs a CompileResult object with reflection data. //! @param sourceCode The compiled source code in binary format if the compilation was successful. //! @param reflectionData The reflection data from the compilation. - CompileResult(const std::vector& sourceCode, ShaderReflection reflectionData); + CompileResult(const std::vector& sourceCode, ShaderReflection reflectionData); //! Returns true if the compilation was successful, false otherwise. //! @return True if the compilation was successful, false otherwise. @@ -43,7 +43,7 @@ namespace ris_shader_toolkit { //! Returns the compiled source code in binary format if the compilation was successful. //! @return The compiled source code in binary format if the compilation was successful or an empty string otherwise. - const std::vector& getBinaryCode() const { return _binarySourceCode; } + const std::vector& getBinaryCode() const { return _binarySourceCode; } //! Returns the compiled source code if the compilation was successful. //! @return The compiled source code if the compilation was successful or an empty string otherwise. @@ -63,7 +63,7 @@ namespace ris_shader_toolkit { //! @param binaryCode The compiled source code in binary format. //! @param reflectionData The reflection data from the compilation. //! @return A successful CompileResult object. - static CompileResult successResult(const std::vector& binaryCode, ShaderReflection reflectionData); + static CompileResult successResult(const std::vector& binaryCode, ShaderReflection reflectionData); //! Creates a failed CompileResult object. //! @param errorMessage The error message. @@ -73,7 +73,7 @@ namespace ris_shader_toolkit { private: bool _success; std::string _sourceCode; - std::vector _binarySourceCode; + std::vector _binarySourceCode; std::string _errorMessage; ShaderReflection _reflectionData; }; diff --git a/ShaderToolkitC/include/compiler/compiler.hpp b/ShaderToolkitC/include/compiler/compiler.hpp index 374ff3e..ecbf05f 100644 --- a/ShaderToolkitC/include/compiler/compiler.hpp +++ b/ShaderToolkitC/include/compiler/compiler.hpp @@ -6,6 +6,8 @@ #include "compiler/compile_result.hpp" #include "rules.hpp" #include +#include +#include namespace ris_shader_toolkit { class Compiler { @@ -47,38 +49,18 @@ namespace ris_shader_toolkit { );*/ //! Compiles a Slang shader file to GLSL source code. - //! @param inputFilePath The path to the input Slang shader file. - //! @param profile The GLSL profile to compile to (e.g., GlslProfile::GLSL_450). Default is GlslProfile::GLSL_450. - //! @param shaderStage The shader stage (e.g., ShaderStage::Vertex). Default is ShaderStage::Vertex. - //! @param entryPoint The entry point function name (default is "main"). - //! @param inputRule An optional rule to replace stage input names. - //! @param outputRule An optional rule to replace stage output names. - //! @return A CompileResult object containing the result of the compilation. - /*CompileResult compileSlangToGlsl( - const std::string& inputFilePath, - GlslProfile profile = GlslProfile::GLSL_450, - ShaderStage shaderStage = ShaderStage::Vertex, - const std::string& entryPoint = "main", - ReplaceStageInputNameRule* inputRule = nullptr, - ReplaceStageOutputNameRule* outputRule = nullptr - );*/ - - //! Compiles a Slang shader file to GLSL source code. - //! @param slangSource The slang shader source code.. + //! @param sourceCode The Slang shader source code. + //! @param shaderStage Thee shader stage. + //! @param entryPoint The entry points for shader stage. Optional if slang source code is decorated with @shader attributes to specify entry point names. If not specified, 'main' will be used as the default entry point name. //! @param profile The GLSL profile to compile to (e.g., GlslProfile::GLSL_450). Default is GlslProfile::GLSL_450. - //! @param shaderStage The shader stage (e.g., ShaderStage::Vertex). Default is ShaderStage::Vertex. - //! @param entryPoint The entry point function name (default is "main"). - //! @param inputRule An optional rule to replace stage input names. - //! @param outputRule An optional rule to replace stage output names. //! @return A CompileResult object containing the result of the compilation. - /*CompileResult compileSlangSourceCodeToGlsl( - const std::string& slangSource, - GlslProfile profile = GlslProfile::GLSL_450, - ShaderStage shaderStage = ShaderStage::Vertex, - const std::string& entryPoint = "main", - ReplaceStageInputNameRule* inputRule = nullptr, - ReplaceStageOutputNameRule* outputRule = nullptr - );*/ + //! The result includes success status and source code if successful, or an error message if failed. + CompileResult compileSlangToGlsl( + const std::string& sourceCode, + ShaderStage stage, + std::string entryPoint, + GlslProfile profile = GlslProfile::GLSL_450 + ); //! Compiles a Slang shader file to Spir-V source code. //! @param sourceCode The Slang shader source code. @@ -89,19 +71,30 @@ namespace ris_shader_toolkit { //! The result includes success status and source code if successful, or an error message if failed. CompileResult compileSlangToSpirV( const std::string& sourceCode, - std::vector stages, - std::vector entryPoints, + std::vector& stages, + std::vector& entryPoints, SpirVProfile profile = SpirVProfile::SPIRV_1_2 ); + //! Compiles a Slang shader file to Spir-V source code. + //! @param sourceCode The Slang shader source code. + //! @param shaderStages Thee shader stages. + //! @param profile The Spir-V profile to compile to (e.g., SpirVProfile::SPIRV_1_2). Default is SpirVProfile::SPIRV_1_2. + //! @return A CompileResult object containing the result of the compilation. + //! The result includes success status and source code if successful, or an error message if failed. + CompileResult compileSlangToSpirV( + const std::string& sourceCode, + std::vector& stages, + SpirVProfile profile = SpirVProfile::SPIRV_1_2); + //! Compiles a Slang shader file to WGSL source code. //! @param sourceCode The Slang shader source code. //! @param shaderStages Thee shader stages. //! @param entryPoints The entry points for each shader stage. Must be the same size as shaderStages parameter or empty (in which case 'shader' attribute entry point names will be used). CompileResult compileSlangToWgsl( const std::string& sourceCode, - std::vector stages, - std::vector entryPoints + const std::vector stages, + const std::vector entryPoints ); //! Compiles a Slang shader file to WGSL source code. @@ -110,11 +103,12 @@ namespace ris_shader_toolkit { //! @param entryPoints The entry points for each shader stage. Must be the same size as shaderStages parameter or empty (in which case 'shader' attribute entry point names will be used). CompileResult compileSlangToWgsl( const std::string& sourceCode, - std::vector stages + std::vector& stages ); private: FxcCompiler _fxcCompiler; + static std::shared_ptr _logger; //! Creates a file with the specified content. //! @param filePath The path to the file to create. diff --git a/ShaderToolkitC/include/slang/slang_compile_result.hpp b/ShaderToolkitC/include/slang/slang_compile_result.hpp index d94ad55..28e6171 100644 --- a/ShaderToolkitC/include/slang/slang_compile_result.hpp +++ b/ShaderToolkitC/include/slang/slang_compile_result.hpp @@ -6,6 +6,7 @@ #define SLANG_COMPILE_RESULT_H #include +#include #include "data/shader_reflection.hpp" namespace ris_shader_toolkit { @@ -26,7 +27,9 @@ namespace ris_shader_toolkit { //! @param binaryCode The compiled source code as binary if the compilation was successful. Binary code is used for targets like SPIR-V. //! @param shaderReflection The shader reflection data if the compilation was successful. //! @param error The error message if the compilation failed. - SlangCompileResult(bool success, const std::vector& binaryCode, ShaderReflection shaderReflection, const std::string& error = ""); + SlangCompileResult(bool success, + const std::vector& binaryCode, + ShaderReflection shaderReflection, const std::string& error = ""); //! Returns true if the compilation was successful, false otherwise. //! @return True if the compilation was successful, false otherwise. @@ -37,8 +40,12 @@ namespace ris_shader_toolkit { std::string getSourceCode() const { return _sourceCode; } //! Returns the compiled binary source code if the compilation was successful. + //! @param shaderStage The shader stage to get the binary code for. //! @return The compiled binary source code if the compilation was successful. - const std::vector& getBinaryCode() const { return _binaryCode; } + const std::vector& getBinaryCode() const + { + return _binaryCode; + } //! Returns the error message if the compilation failed. //! @return The error message if the compilation failed. @@ -54,7 +61,9 @@ namespace ris_shader_toolkit { //! @param binaryCode The compiled source code in binary format. //! @param reflectionData The reflection data from the compilation. //! @return A successful SlangCompileResult object. - static SlangCompileResult successResult(const std::vector& binaryCode, ShaderReflection reflectionData); + static SlangCompileResult successResult( + const std::vector& binaryCodePerStage, + ShaderReflection reflectionData); //! Creates a failed SlangCompileResult object. //! @param errorMessage The error message. @@ -64,7 +73,7 @@ namespace ris_shader_toolkit { private: bool _success = false; std::string _sourceCode; - std::vector _binaryCode; + std::vector _binaryCode; std::string _errorMessage; ShaderReflection _reflectionData; }; diff --git a/ShaderToolkitC/include/slang/slang_session.hpp b/ShaderToolkitC/include/slang/slang_session.hpp index b5c609e..b15de55 100644 --- a/ShaderToolkitC/include/slang/slang_session.hpp +++ b/ShaderToolkitC/include/slang/slang_session.hpp @@ -9,6 +9,8 @@ #include "data/enums.hpp" #include #include +#include +#include using Slang::ComPtr; @@ -29,24 +31,6 @@ namespace ris_shader_toolkit { //! @return The Slang global session. ComPtr getGlobalSession() const { return _globalSession; } - /// - /// Returns the final compiled code from the Slang compile request. - /// It handles multiple entry points and concatenates the results if necessary. - /// - /// The component to read from. - /// The entry points count. - /// The concatinated target code. - std::string readCompiledCode(ComPtr component, size_t entryPointCount); - - /// - /// Returns the final compiled code from the Slang compile request. - /// It handles multiple entry points and concatenates the results if necessary. - /// - /// The component to read from. - /// The entry points count. - /// The concatinated target code. - std::vector readCompiledBinaryCode(ComPtr component, size_t entryPointCount); - //! Compiles a Slang shader file to the specified target and profile. //! This is low-level function that directly uses the Slang API. //! @param sourceCode The Slang shader source code. @@ -54,12 +38,15 @@ namespace ris_shader_toolkit { //! @param profile The profile to compile to (e.g., "sm_5_0"). Can be empty string when compiling to shader targets that do not require profile such as WGSL. //! @param stages The shader stages (e.g., SLANG_STAGE_VERTEX). //! @param entryPoints The entry points info function name. Must match the stages count. + //! @param options The session options for compilation. Default is SlangSessionOptions(). + //! @return A SlangCompileResult object containing the result of the compilation. SlangCompileResult compile( const std::string& sourceCode, SlangCompileTarget compileTarget, std::string profile, - std::vector stages, - std::vector entryPoints + const std::vector stages, + const std::vector entryPoints, + SlangSessionOptions options = SlangSessionOptions() ); //! Compiles an HLSL shader file to the specified profile and stage. @@ -73,91 +60,99 @@ namespace ris_shader_toolkit { const std::string& entryPoint = "main", HlslProfile profile = HlslProfile::SM_5_0);*/ - //! Compiles an GLSL shader file to the specified profile and stage. - //! @param filePath The path to the GLSL shader file. + + + + //! Compiles a Metal shader file to the specified profile and stage. + //! @param filePath The path to the Metal shader file. //! @param stage The shader stage (e.g., ShaderStage::Vertex). By default, it is ShaderStage::Vertex. //! @param entryPoint The entry point function name (default is "main"). - //! @param profile The GLSL profile to compile to (e.g., "450"). By default, it is GlslProfile::GL_450. - /* SlangCompileResult compileToGlsl( - const std::string& filePath, - ShaderStage stage = ShaderStage::Vertex, - const std::string& entryPoint = "main", - GlslProfile profile = GlslProfile::GLSL_450);*/ - - //! Compiles an GLSL shader file to the specified profile and stage. - //! @param slangSourceCode The Slang shader source code. - //! @param stage The shader stage (e.g., ShaderStage::Vertex). By default, it is ShaderStage::Vertex. - //! @param entryPoint The entry point function name (default is "main"). - //! @param profile The GLSL profile to compile to (e.g., "450"). By default, it is GlslProfile::GL_450. - //SlangCompileResult compileSourceCodeToGlsl( - // const std::string& slangSourceCode, - // ShaderStage stage = ShaderStage::Vertex, - // const std::string& entryPoint = "main", - // GlslProfile profile = GlslProfile::GLSL_450); - - //! Compiles a Metal shader file to the specified profile and stage. - //! @param filePath The path to the Metal shader file. - //! @param stage The shader stage (e.g., ShaderStage::Vertex). By default, it is ShaderStage::Vertex. - //! @param entryPoint The entry point function name (default is "main"). - //! @param profile The Metal profile to compile to (e.g., "metal2.0"). By default, it is MetalProfile::MSL_2_0. - //SlangCompileResult compileToMetal( - // const std::string& filePath, - // ShaderStage stage = ShaderStage::Vertex, - // const std::string& entryPoint = "main", - // MetalProfile profile = MetalProfile::MSL_2_0 - //); - - //! Compiles a Metal shader file to the specified profile and stages. - //! @param filePath The path to the Metal shader file. - //! @param stages The shader stages (e.g., ShaderStage::Vertex). - //! @param entryPoints The entry points info function name. Must match the stages count. - //! @param profile The Metal profile to compile to (e.g., "metal2.0"). By default, it is MetalProfile::MSL_2_0. - //! @return A SlangCompileResult object containing the result of the compilation. - //SlangCompileResult compileToMetal( - // const std::string& filePath, - // std::vector stages, - // std::vector entryPoints, - // MetalProfile profile = MetalProfile::MSL_2_0 - //); - - //! Compiles a WGSL shader file to the specified stages. - //! @param slangSourceCode The Slang shader source code. - //! @param stages The shader stages (e.g., ShaderStage::Vertex). - //! @param entryPoints The entry points info function name. Can be empty if Slang source code is decorated with @entryPoint attributes. - //! @return A SlangCompileResult object containing the result of the compilation. + //! @param profile The Metal profile to compile to (e.g., "metal2.0"). By default, it is MetalProfile::MSL_2_0. + //SlangCompileResult compileToMetal( + // const std::string& filePath, + // ShaderStage stage = ShaderStage::Vertex, + // const std::string& entryPoint = "main", + // MetalProfile profile = MetalProfile::MSL_2_0 + //); + + //! Compiles a Metal shader file to the specified profile and stages. + //! @param filePath The path to the Metal shader file. + //! @param stages The shader stages (e.g., ShaderStage::Vertex). + //! @param entryPoints The entry points info function name. Must match the stages count. + //! @param profile The Metal profile to compile to (e.g., "metal2.0"). By default, it is MetalProfile::MSL_2_0. + //! @return A SlangCompileResult object containing the result of the compilation. + //SlangCompileResult compileToMetal( + // const std::string& filePath, + // std::vector stages, + // std::vector entryPoints, + // MetalProfile profile = MetalProfile::MSL_2_0 + //); + + //! Compiles a WGSL shader file to the specified stages. + //! @param slangSourceCode The Slang shader source code. + //! @param stages The shader stages (e.g., ShaderStage::Vertex). + //! @param entryPoints The entry points info function name. Can be empty if Slang source code is decorated with @entryPoint attributes. + //! @return A SlangCompileResult object containing the result of the compilation. SlangCompileResult compileToWgsl( const std::string& slangSourceCode, - std::vector stages, - std::vector entryPoints + const std::vector stages, + const std::vector entryPoints ); //! Compiles a ASlang shader to the SPIR-V shader file. - //! @param filePath The path to the Slang shader file. + //! @param slangSourceCode The Slang shader source code. //! @param stages The shader stages (e.g., ShaderStage::Vertex). //! @param entryPoints The entry points info function name. Must match the stages count. //! @param profile The SpirV profile to compile to (e.g., "spirv_1_2"). By default, it is SpirVProfile::SPIRV_1_2. + //! @param options The session options for compilation. Default is SlangSessionOptions(). //! @return A SlangCompileResult object containing the result of the compilation. SlangCompileResult compileToSpirV( - const std::string& filePath, + const std::string& slangSourceCode, std::vector stages, std::vector entryPoints, - SpirVProfile profile = SpirVProfile::SPIRV_1_2 + SpirVProfile profile = SpirVProfile::SPIRV_1_2, + SlangSessionOptions options = SlangSessionOptions() ); - + //! Compiles an GLSL shader file to the specified profile and stage. + //! @param slangSourceCode The Slang shader source code. + //! @param stage The shader stage (e.g., ShaderStage::Vertex). By default, it is ShaderStage::Vertex. + //! @param entryPoint The entry point function name (default is "main"). + //! @param profile The GLSL profile to compile to (e.g., "450"). By default, it is GlslProfile::GL_450. + SlangCompileResult compileToGlsl( + const std::string& slangSourceCode, + ShaderStage stages, + std::string entryPoint, + GlslProfile profile = GlslProfile::GLSL_450); private: ComPtr _globalSession; - std::map shaderStageMap; - std::map hlslProfileMap; - std::map glslProfileMap; - std::map metalProfileMap; - std::map spirvProfileMap; + std::map _shaderStageMap; + std::map _hlslProfileMap; + std::map _glslProfileMap; + std::map _metalProfileMap; + std::map _spirvProfileMap; std::map< slang::TypeReflection::Kind, BindingType> _bindingTypeMap; //! Updates the shader code before compilation if needed. void modifyShader(slang::ICompileRequest* request, ShaderReflection* reflection); + + //! Find the stage of the entry point. + //! @param entryPoint The entry point to find the stage for. + //! @return The stage of entry point. + std::optional findEntryPointStage(ComPtr entryPoint); + + //! Reads the compiled code for the given component and entry point count. + //! @param component The component to read from. + //! @param entryPointCount The number of entry points. + //! @return The concatenated target code. + std::string readCompiledCode(ComPtr component, size_t entryPointCount); + + //! Reads the compiled binary code for the given component and entry point count. + //! @param component The component to read from. + //! @return The binary code. + std::vector readCompiledBinaryCode(ComPtr component); }; }; diff --git a/ShaderToolkitC/include/slang/slang_session_options.hpp b/ShaderToolkitC/include/slang/slang_session_options.hpp new file mode 100644 index 0000000..af29cde --- /dev/null +++ b/ShaderToolkitC/include/slang/slang_session_options.hpp @@ -0,0 +1,24 @@ + +#ifndef SLANG_SESSION_OPTIONS_HPP +#define SLANG_SESSION_OPTIONS_HPP + +#include + +namespace ris_shader_toolkit +{ + struct SlangSessionOptions { + + + SlangSessionOptions() : matrixLayoutMode(SLANG_MATRIX_LAYOUT_MODE_UNKNOWN) {} + + //! The matrix layout mode to use for the Slang session. + //! This option determines how matrices are laid out in memory and how they are accessed in shaders. + //! The default value is SLANG_MATRIX_LAYOUT_MODE_UNKNOWN, which means that the matrix layout mode + //! will be determined by the Slang compiler based on the target and profile. + //! You can set this option to SLANG_MATRIX_LAYOUT_ROW_MAJOR or SLANG_MATRIX_LAYOUT_COLUMN_MAJOR to + //! explicitly specify the matrix layout mode for the session. + SlangMatrixLayoutMode matrixLayoutMode; + }; +} + +#endif //SLANG_SESSION_OPTIONS_HPP diff --git a/ShaderToolkitC/include/spirv-cross/spirv_cross_compiler.hpp b/ShaderToolkitC/include/spirv-cross/spirv_cross_compiler.hpp index a8d6c82..4fc725f 100644 --- a/ShaderToolkitC/include/spirv-cross/spirv_cross_compiler.hpp +++ b/ShaderToolkitC/include/spirv-cross/spirv_cross_compiler.hpp @@ -17,45 +17,37 @@ namespace ris_shader_toolkit { SpirVCrossCompiler(); //! Compiles a SPIR-V binary file to the specified shading language. - //! @param filePath The path to the SPIR-V binary file. + //! @param spirv The SPIR-V binary data. //! @param profile The target shading language profile. - //! @param replaceStageInputNameRule An optional rule to replace stage input names. - //! @param replaceStageOutputNameRule An optional rule to replace stage output names. //! @return A SpirVCrossCompileResult object containing the result of the compilation. - SpirVCrossCompileResult compile(const std::string& filePath, GlslProfile profile, - ReplaceStageInputNameRule* replaceStageInputNameRule = nullptr, - ReplaceStageOutputNameRule* replaceStageOutputNameRule = nullptr - ); + SpirVCrossCompileResult compile(const std::vector& spirv, GlslProfile profile, ShaderStage stage); - //! Compiles a SPIR-V binary file to the specified shading language. + protected: + void handleImageAndSamplersGlsl(spirv_cross::CompilerGLSL& compiler, spirv_cross::ShaderResources& shaderResources); + + //! Handles shader attributes such as input/output variables and their locations for GLSL. + //! @param vertexCompiler The GLSL compiler instance which will compile vertex shader. + //! @param fragmentCompiler The GLSL compiler instance which will compile fragment shader. + //! @param profile The target GLSL profile. + //! @param stage The shader stage (vertex, fragment, etc.) for which to handle the attributes. + void handleAttributes(spirv_cross::CompilerGLSL& vertexCompiler, spirv_cross::CompilerGLSL& fragmentCompiler, GlslProfile profile, ShaderStage stage); + + //! Handles uniform variables for GLSL, including uniform buffers and plain uniforms, + //! and applies necessary transformations based on the target GLSL profile and shader stage. + //! @param compiler The GLSL compiler instance for which to handle the uniforms. + void handleUniforms(spirv_cross::CompilerGLSL& compiler); + + //! Compiles SPIR-V binary data to GLSL source code for lower GLSL profiles (e.g., GLSL 3.3, 4.0, etc.) + //! where attributes need to be handled differently ( no support for layout(location = {id}) for input/output variables, etc.). //! @param spirv The SPIR-V binary data. - //! @param profile The target shading language profile. - //! @param replaceStageInputNameRule An optional rule to replace stage input names. - //! @param replaceStageOutputNameRule An optional rule to replace stage output names. - //! @return A SpirVCrossCompileResult object containing the result of the compilation. - SpirVCrossCompileResult compile(const std::vector& spirv, GlslProfile profile, - ReplaceStageInputNameRule* replaceStageInputNameRule = nullptr, - ReplaceStageOutputNameRule* replaceStageOutputNameRule = nullptr - ); + //! @param profile The target GLSL profile. + //! @param stage The shader stage (vertex, fragment, etc.) for which to compile the GLSL source code. + SpirVCrossCompileResult compileForLowerProfiles(const std::vector& spirv, GlslProfile profile, ShaderStage stage); - protected: - FileReader fileReader; - std::map glslVersionMap; - - void applyReplaceStageInputNameRule( - spirv_cross::CompilerGLSL& compiler, - spirv_cross::ShaderResources& shaderResources, - ReplaceStageInputNameRule* replaceStageInputNameRule); - - void applyReplaceStageOutputNameRule( - spirv_cross::CompilerGLSL& compiler, - spirv_cross::ShaderResources& shaderResources, - ReplaceStageOutputNameRule* replaceStageOutputNameRule); - - void handleImageAndSamplersGlsl( - spirv_cross::CompilerGLSL& compiler, - spirv_cross::ShaderResources& shaderResources); + FileReader _fileReader; + std::map _glslVersionMap; + std::map _executionModelMap; }; } diff --git a/ShaderToolkitC/src/compiler/c_compiler.cpp b/ShaderToolkitC/src/compiler/c_compiler.cpp index 38fdfab..034cc7d 100644 --- a/ShaderToolkitC/src/compiler/c_compiler.cpp +++ b/ShaderToolkitC/src/compiler/c_compiler.cpp @@ -1,4 +1,5 @@ #include "compiler/c_compiler.hpp" +#include void* create_compiler() { @@ -10,94 +11,18 @@ char* get_last_error_message() return lastErrorMessage.empty() ? nullptr : const_cast(lastErrorMessage.c_str()); } -//void* compile_slang_to_glsl( -// void* compilerPtr, -// const char* inputFilePath, -// int profile, // GlslProfile as int -// int shaderStage, // ShaderStage as int -// const char* entryPointPtr, -// c_ReplaceStageInputNameRule* inputRulePtr, -// c_ReplaceStageOutputNameRule* outputRulePtr -//) -//{ -// if (compilerPtr == nullptr) -// { -// return errorResult("Compiler instance is null."); -// } -// -// ris_shader_toolkit::Compiler* compiler = static_cast(compilerPtr); -// -// if (inputFilePath == nullptr) -// { -// return errorResult("Input file path or entry point is null."); -// } -// -// ris_shader_toolkit::GlslProfile glslProfile = static_cast(profile); -// ris_shader_toolkit::ShaderStage stage = static_cast(shaderStage); -// -// // Use "main" as default entry point if none is provided -// std::string entryPoint = (entryPointPtr != nullptr) ? std::string(entryPointPtr) : "main"; -// ris_shader_toolkit::ReplaceStageInputNameRule* inputRule = c_to_cpp_ReplaceStageInputNameRule(inputRulePtr); -// ris_shader_toolkit::ReplaceStageOutputNameRule* outputRule = c_to_cpp_ReplaceStageOutputNameRule(outputRulePtr); -// -// ris_shader_toolkit::CompileResult result = compiler->compileSlangToGlsl( -// std::string(inputFilePath), -// glslProfile, stage, -// std::string(entryPoint), -// inputRule, outputRule -// ); -// -// delete inputRule; -// delete outputRule; -// -// return c_to_cpp_CompileResult(result); -//} - -// -//void* compile_slang_source_code_to_glsl( -// void* compilerPtr, -// const char* slangSourceCode, -// int profile, // GlslProfile as int -// int shaderStage, // ShaderStage as int -// const char* entryPointPtr, -// c_ReplaceStageInputNameRule* inputRulePtr, -// c_ReplaceStageOutputNameRule* outputRulePtr -//) -//{ -// if (compilerPtr == nullptr) -// { -// return errorResult("Compiler instance is null."); -// } -// -// ris_shader_toolkit::Compiler* compiler = static_cast(compilerPtr); -// -// if (slangSourceCode == nullptr) -// { -// return errorResult("Slang source code is not defined."); -// } -// -// ris_shader_toolkit::GlslProfile glslProfile = static_cast(profile); -// ris_shader_toolkit::ShaderStage stage = static_cast(shaderStage); -// -// // Use "main" as default entry point if none is provided -// std::string entryPoint = (entryPointPtr != nullptr) ? std::string(entryPointPtr) : "main"; -// ris_shader_toolkit::ReplaceStageInputNameRule* inputRule = c_to_cpp_ReplaceStageInputNameRule(inputRulePtr); -// ris_shader_toolkit::ReplaceStageOutputNameRule* outputRule = c_to_cpp_ReplaceStageOutputNameRule(outputRulePtr); -// -// ris_shader_toolkit::CompileResult result = compiler->compileSlangSourceCodeToGlsl( -// std::string(slangSourceCode), -// glslProfile, stage, -// std::string(entryPoint), -// inputRule, outputRule -// ); -// -// delete inputRule; -// delete outputRule; -// -// return c_to_cpp_CompileResult(result); -//} +#pragma region WGSL void* compile_slang_to_wgsl( + void* compilerPtr, + const char* slangSourceCode, + int32_t* shaderStages, + uint32_t shaderStagesCount +) { + return compile_slang_to_wgsl_ext(compilerPtr, slangSourceCode, shaderStages, shaderStagesCount, nullptr, 0); +} + +void* compile_slang_to_wgsl_ext( void* compilerPtr, const char* sourceCode, int32_t* shaderStages, @@ -108,14 +33,18 @@ void* compile_slang_to_wgsl( { if (compilerPtr == nullptr) { - return errorResult("Compiler instance is null."); + std::string message = "Compiler instance is null."; + spdlog::error(message); + return errorResult(message.c_str()); } ris_shader_toolkit::Compiler* compiler = static_cast(compilerPtr); if (sourceCode == nullptr) { - return errorResult("Slang source code is not defined."); + std::string message = "Slang source code is not defined."; + spdlog::error(message); + return errorResult(message.c_str()); } std::vector stages; @@ -130,13 +59,30 @@ void* compile_slang_to_wgsl( entryPointNames.push_back(std::string(entryPoints[i])); } + spdlog::info("Compiling Slang to WGSL with {} shader stages and {} entry points.", shaderStagesCount, entryPointsCount); + ris_shader_toolkit::CompileResult result = compiler->compileSlangToWgsl( std::string(sourceCode), stages, entryPointNames); + spdlog::info("Compilation completed. Success: {}. Source code size: {}. Error message size: {}.", + result.isSuccess(), result.getSourceCode().size(), result.getErrorMessage().size()); + return c_to_cpp_CompileResult(result); } +#pragma endregion + void* compile_slang_to_spirv( + void* compilerPtr, + const char* slangSourceCode, + int32_t* shaderStages, + uint32_t shaderStagesCount, + ris_shader_toolkit::SpirVProfile profile) +{ + return compile_slang_to_spirv_ext(compilerPtr, slangSourceCode, shaderStages, shaderStagesCount, nullptr, 0, profile); +} + +void* compile_slang_to_spirv_ext( void* compilerPtr, const char* slangSourceCode, int32_t* shaderStages, @@ -169,8 +115,49 @@ void* compile_slang_to_spirv( entryPointNames.push_back(std::string(entryPoints[i])); } - ris_shader_toolkit::CompileResult result = compiler->compileSlangToSpirV( - std::string(slangSourceCode), stages, entryPointNames, profile); + ris_shader_toolkit::CompileResult result = compiler->compileSlangToSpirV(std::string(slangSourceCode), stages, entryPointNames, profile); + + return c_to_cpp_CompileResult(result); +} + +void* compile_slang_to_glsl( + void* compilerPtr, + const char* slangSourceCode, + int32_t shaderStage, + ris_shader_toolkit::GlslProfile profile +) { + return compile_slang_to_glsl_ext(compilerPtr, slangSourceCode, shaderStage, nullptr, profile); +} + +void* compile_slang_to_glsl_ext( + void* compilerPtr, + const char* slangSourceCode, + int32_t shaderStage, + const char* entryPoint, + ris_shader_toolkit::GlslProfile profile +) +{ + if (compilerPtr == nullptr) + { + return errorResult("Compiler instance is null."); + } + + ris_shader_toolkit::Compiler* compiler = static_cast(compilerPtr); + + if (slangSourceCode == nullptr) + { + return errorResult("Slang source code is not defined."); + } + + ris_shader_toolkit::ShaderStage stage = static_cast(shaderStage); + + std::string entryPointName = ""; + if (entryPoint != nullptr) + { + entryPointName = std::string(entryPoint); + } + + ris_shader_toolkit::CompileResult result = compiler->compileSlangToGlsl(std::string(slangSourceCode), stage, entryPointName, profile); return c_to_cpp_CompileResult(result); } diff --git a/ShaderToolkitC/src/compiler/c_compiler_result.cpp b/ShaderToolkitC/src/compiler/c_compiler_result.cpp index 40927c1..bb16401 100644 --- a/ShaderToolkitC/src/compiler/c_compiler_result.cpp +++ b/ShaderToolkitC/src/compiler/c_compiler_result.cpp @@ -48,7 +48,7 @@ c_CompileResult* c_to_cpp_CompileResult(const ris_shader_toolkit::CompileResult& c_CompileResult* errorResult(const char* errorMessage) { - c_CompileResult* result = new c_CompileResult(); + c_CompileResult* result = (c_CompileResult*)malloc(sizeof(c_CompileResult)); result->success = false; result->sourceCode = nullptr; result->binaryCode = nullptr; diff --git a/ShaderToolkitC/src/compiler/compile_result.cpp b/ShaderToolkitC/src/compiler/compile_result.cpp index bd0b302..12ae6f2 100644 --- a/ShaderToolkitC/src/compiler/compile_result.cpp +++ b/ShaderToolkitC/src/compiler/compile_result.cpp @@ -15,7 +15,7 @@ namespace ris_shader_toolkit CompileResult::CompileResult( bool success, - const std::vector& binaryCode, + const std::vector& binaryCode, const std::string& error) : _success(success), _binarySourceCode(binaryCode), @@ -34,7 +34,7 @@ namespace ris_shader_toolkit } CompileResult::CompileResult( - const std::vector& binaryCode, + const std::vector& binaryCode, ShaderReflection reflection) : _success(true), _binarySourceCode(binaryCode), @@ -51,7 +51,7 @@ namespace ris_shader_toolkit } CompileResult CompileResult::successResult( - const std::vector& binaryCode, + const std::vector& binaryCode, ShaderReflection reflection ) { diff --git a/ShaderToolkitC/src/compiler/compiler.cpp b/ShaderToolkitC/src/compiler/compiler.cpp index bc47fc5..95f25b4 100644 --- a/ShaderToolkitC/src/compiler/compiler.cpp +++ b/ShaderToolkitC/src/compiler/compiler.cpp @@ -5,8 +5,19 @@ #include #include + namespace ris_shader_toolkit { + +std::shared_ptr Compiler::_logger = nullptr; + Compiler::Compiler() : _fxcCompiler() { + + if(_logger == nullptr) { + // TODO: this is currently issue when loading library multiple times. Figure out solution for it. + //spdlog::basic_logger_mt("dll_logger", "log.txt"); + //spdlog::set_default_logger(_logger); + //spdlog::set_level(spdlog::level::trace); + } } //bool Compiler::createFile( @@ -81,165 +92,117 @@ namespace ris_shader_toolkit { // } // return CompileResult::successResult(outputFilePath, "", ShaderReflection()); // } -// -// CompileResult Compiler::compileSlangSourceCodeToGlsl( -// const std::string& slangSourceCode, -// GlslProfile profile, -// ShaderStage shaderStage, -// const std::string& entryPoint, -// ReplaceStageInputNameRule* replaceStageInputNameRule, -// ReplaceStageOutputNameRule* replaceStageOutputNameRule -// ) -// { -// spdlog::info("Compiling Slang shader to GLSL: {}", slangSourceCode); -// // First we need to determine if we can use Slang directly to GLSL or if we need to go through SPIR-V -// -// // For GLES profiles, we need to go through SPIR-V -// if (profile == GlslProfile::GLES_300 -// || profile == GlslProfile::GLES_310 -// || profile == GlslProfile::GLES_320 -// ) { -// CompileResult spirvResult = compileSlangSourceCodeToSpirV( -// slangSourceCode, -// shaderStage, -// SpirVProfile::SPIRV_1_5, -// entryPoint -// ); -// if (!spirvResult.isSuccess()) { -// return spirvResult; -// } -// -// // Convert SPIR-V to GLSL using SpirVCrossCompiler -// std::vector spirvBinary = stringToBinary(spirvResult.getSourceCode()); -// SpirVCrossCompiler spirvCompiler; -// SpirVCrossCompileResult glslResult = spirvCompiler.compile( -// spirvBinary, -// profile, -// replaceStageInputNameRule, -// replaceStageOutputNameRule -// ); -// if (!glslResult.isSuccess()) { -// return CompileResult::errorResult("SPIR-V to GLSL compilation failed: " + glslResult.getErrorMessage()); -// } -// -// return CompileResult::successResult("", glslResult.getSourceCode(), ShaderReflection()); -// } -// -// SlangSession slangSession; -// if (!slangSession.initialize()) { -// return CompileResult::errorResult("Failed to initialize Slang session."); -// } -// -// SlangCompileResult slangResult = slangSession.compileSourceCodeToGlsl( -// slangSourceCode, -// shaderStage, -// entryPoint, -// profile -// ); -// if (!slangResult.isSuccess()) { -// return CompileResult::errorResult("Slang compilation to GLSL failed: " + slangResult.getErrorMessage()); -// } -// std::string glslSourceCode = slangResult.getSourceCode(); -// return CompileResult::successResult("", glslSourceCode, ShaderReflection()); -// } -// -// -// CompileResult Compiler::compileSlangToGlsl( -// const std::string& inputFilePath, -// GlslProfile profile, -// ShaderStage shaderStage, -// const std::string& entryPoint, -// ReplaceStageInputNameRule* replaceStageInputNameRule, -// ReplaceStageOutputNameRule* replaceStageOutputNameRule -// ) -// { -// spdlog::info("Compiling Slang shader to GLSL: {}", inputFilePath); -// // First we need to determine if we can use Slang directly to GLSL or if we need to go through SPIR-V -// -// // For GLES profiles, we need to go through SPIR-V -// if (profile == GlslProfile::GLES_300 -// || profile == GlslProfile::GLES_310 -// || profile == GlslProfile::GLES_320 -// ) { -// spdlog::info("Need to compile via SPIR-V for GLES profile first."); -// CompileResult spirvResult = compileSlangToSpirV( -// inputFilePath, -// shaderStage, -// SpirVProfile::SPIRV_1_5, -// entryPoint -// ); -// if (!spirvResult.isSuccess()) { -// return spirvResult; -// } -// -// // Convert SPIR-V to GLSL using SpirVCrossCompiler -// std::vector spirvBinary = stringToBinary(spirvResult.getSourceCode()); -// SpirVCrossCompiler spirvCompiler; -// SpirVCrossCompileResult glslResult = spirvCompiler.compile( -// spirvBinary, -// profile, -// replaceStageInputNameRule, -// replaceStageOutputNameRule -// ); -// if (!glslResult.isSuccess()) { -// return CompileResult::errorResult("SPIR-V to GLSL compilation failed: " + glslResult.getErrorMessage()); -// } -// -// spdlog::info("Successfully compiled Slang shader to GLSL via SPIR-V."); -// return CompileResult::successResult("", glslResult.getSourceCode(), ShaderReflection()); -// } -// -// SlangSession slangSession; -// if (!slangSession.initialize()) { -// return CompileResult::errorResult("Failed to initialize Slang session."); -// } -// -// SlangCompileResult slangResult = slangSession.compileToGlsl( -// inputFilePath, -// shaderStage, -// entryPoint, -// profile -// ); -// if (!slangResult.isSuccess()) { -// return CompileResult::errorResult("Slang compilation to GLSL failed: " + slangResult.getErrorMessage()); -// } -// std::string glslSourceCode = slangResult.getSourceCode(); -// return CompileResult::successResult("", glslSourceCode, ShaderReflection()); -// } -// + + CompileResult Compiler::compileSlangToGlsl( + const std::string& sourceCode, + ShaderStage stage, + std::string entryPoint, + GlslProfile profile) + { + + // For GLES profiles, we need to go through SPIR-V + if (profile == GlslProfile::GLES_300 + || profile == GlslProfile::GLES_310 + || profile == GlslProfile::GLES_320 + ) { + + std::vector stages = { stage }; + + // For GLES 3.0, we need to compile vertex and fragment shaders together to ensure the input/output + // variable names match between vertex and fragment shaders for linking. + // So if the profile is GLES 3.0 and the stage is vertex or fragment shader, we will compile both vertex and fragment shaders together. + if(profile == GlslProfile::GLES_300) + { + stages = { ShaderStage::Vertex, ShaderStage::Fragment }; + } + + std::vector entryPoints; + if (!entryPoint.empty() && entryPoint != "") + { + entryPoints.push_back(entryPoint); + } + CompileResult spirvResult = compileSlangToSpirV(sourceCode, stages, entryPoints); + if (!spirvResult.isSuccess()) + { + return spirvResult; + } + + // Convert SPIR-V to GLSL using SpirVCrossCompiler + SpirVCrossCompiler spirvCompiler; + SpirVCrossCompileResult glslResult = spirvCompiler.compile( + spirvResult.getBinaryCode(), + profile, stage + ); + if (!glslResult.isSuccess()) { + return CompileResult::errorResult("SPIR-V to GLSL compilation failed: " + glslResult.getErrorMessage()); + } + + return CompileResult::successResult(glslResult.getSourceCode(), ShaderReflection()); + } + + SlangSession slangSession; + if (!slangSession.initialize()) { + return CompileResult::errorResult("Failed to initialize Slang session."); + } + + SlangCompileResult slangResult = slangSession.compileToGlsl( + sourceCode, + stage, + entryPoint, + profile + ); + if (!slangResult.isSuccess()) { + return CompileResult::errorResult("Slang compilation to GLSL failed: " + slangResult.getErrorMessage()); + } + std::string glslSourceCode = slangResult.getSourceCode(); + return CompileResult::successResult(glslSourceCode, ShaderReflection()); + } + +#pragma region SPIR-V + CompileResult Compiler::compileSlangToSpirV( const std::string& sourceCode, - std::vector shaderStages, - std::vector entryPoints, + std::vector& stages, + std::vector& entryPoints, SpirVProfile profile ) { SlangSession slangSession; if (!slangSession.initialize()) { - std::string errorMsg = "Failed to initialize Slang session."; - spdlog::error(errorMsg); - return CompileResult::errorResult(errorMsg); + return CompileResult::errorResult("Failed to initialize Slang session."); } SlangCompileResult slangResult = slangSession.compileToSpirV( sourceCode, - shaderStages, + stages, entryPoints, profile ); if (!slangResult.isSuccess()) { - std::string errorMsg = "Slang compilation failed: " + slangResult.getErrorMessage(); - spdlog::error(errorMsg); - return CompileResult::errorResult(errorMsg); + return CompileResult::errorResult("Slang compilation to GLSL failed: " + slangResult.getErrorMessage()); } auto& spirvCode = slangResult.getBinaryCode(); return CompileResult::successResult(spirvCode, ShaderReflection()); } + CompileResult Compiler::compileSlangToSpirV( + const std::string& sourceCode, + std::vector& stages, + SpirVProfile profile + ) + { + std::vector entryPoints; + return compileSlangToSpirV(sourceCode, stages, entryPoints, profile); + } + +#pragma endregion + +#pragma region WGSL + CompileResult Compiler::compileSlangToWgsl( const std::string& sourceCode, - std::vector shaderStages, - std::vector entryPoints + const std::vector shaderStages, + const std::vector entryPoints ) { SlangSession slangSession; @@ -257,8 +220,11 @@ namespace ris_shader_toolkit { CompileResult Compiler::compileSlangToWgsl( const std::string& sourceCode, - std::vector shaderStages) + std::vector& shaderStages) { - return compileSlangToWgsl(sourceCode, shaderStages, {}); + std::vector entryPoints; + return compileSlangToWgsl(sourceCode, shaderStages, entryPoints); } } + +#pragma endregion diff --git a/ShaderToolkitC/src/slang/slang_compile_result.cpp b/ShaderToolkitC/src/slang/slang_compile_result.cpp index c690aa2..a6c4ff2 100644 --- a/ShaderToolkitC/src/slang/slang_compile_result.cpp +++ b/ShaderToolkitC/src/slang/slang_compile_result.cpp @@ -9,7 +9,7 @@ namespace ris_shader_toolkit { _reflectionData = shaderReflection; } - SlangCompileResult::SlangCompileResult(bool success, const std::vector& binaryCode, ShaderReflection shaderReflection, const std::string& error) + SlangCompileResult::SlangCompileResult(bool success, const std::vector& binaryCode, ShaderReflection shaderReflection, const std::string& error) : _success(success), _binaryCode(binaryCode), _errorMessage(error), _sourceCode("") { _reflectionData = shaderReflection; @@ -20,7 +20,7 @@ namespace ris_shader_toolkit { return SlangCompileResult(true, sourceCode, shaderReflection); } - SlangCompileResult SlangCompileResult::successResult(const std::vector& binaryCode, ShaderReflection shaderReflection) + SlangCompileResult SlangCompileResult::successResult(const std::vector& binaryCode, ShaderReflection shaderReflection) { return SlangCompileResult(true, binaryCode, shaderReflection); } diff --git a/ShaderToolkitC/src/slang/slang_session.cpp b/ShaderToolkitC/src/slang/slang_session.cpp index 457fafc..fbff803 100644 --- a/ShaderToolkitC/src/slang/slang_session.cpp +++ b/ShaderToolkitC/src/slang/slang_session.cpp @@ -1,20 +1,21 @@ -#include "slang/slang_session.hpp" +#include "slang/slang_session.hpp" #include #include namespace ris_shader_toolkit { + SlangSession::SlangSession() { - shaderStageMap = { - { ShaderStage::Vertex, SLANG_STAGE_VERTEX }, - { ShaderStage::Fragment, SLANG_STAGE_FRAGMENT }, - { ShaderStage::Compute, SLANG_STAGE_COMPUTE }, + _shaderStageMap = { + { ShaderStage::Vertex, SlangStage::SLANG_STAGE_VERTEX }, + { ShaderStage::Fragment, SlangStage::SLANG_STAGE_FRAGMENT }, + { ShaderStage::Compute, SlangStage::SLANG_STAGE_COMPUTE }, //{ ShaderStage::Geometry, SLANG_STAGE_GEOMETRY }, //{ ShaderStage::Hull, SLANG_STAGE_HULL }, //{ ShaderStage::Domain, SLANG_STAGE_DOMAIN } }; - hlslProfileMap = { + _hlslProfileMap = { { HlslProfile::SM_4_0, "sm_4_0" }, { HlslProfile::SM_4_1, "sm_4_1" }, { HlslProfile::SM_5_0, "sm_5_0" }, @@ -27,7 +28,7 @@ namespace ris_shader_toolkit { { HlslProfile::SM_6_5, "sm_6_5" }, }; - glslProfileMap = { + _glslProfileMap = { { GlslProfile::GLSL_330, "glsl_330" }, { GlslProfile::GLSL_400, "glsl_400" }, { GlslProfile::GLSL_410, "glsl_410" }, @@ -38,7 +39,7 @@ namespace ris_shader_toolkit { { GlslProfile::GLSL_460, "glsl_460" }, }; - metalProfileMap = { + _metalProfileMap = { { MetalProfile::MSL_1_0, "metallib_1_0" }, { MetalProfile::MSL_1_1, "metallib_1_1" }, { MetalProfile::MSL_1_2, "metallib_1_2" }, @@ -49,7 +50,7 @@ namespace ris_shader_toolkit { { MetalProfile::MSL_2_4, "metallib_2_4" } }; - spirvProfileMap = { + _spirvProfileMap = { { SpirVProfile::SPIRV_1_0, "spirv_1_0" }, { SpirVProfile::SPIRV_1_1, "spirv_1_1" }, { SpirVProfile::SPIRV_1_2, "spirv_1_2" }, @@ -100,27 +101,30 @@ namespace ris_shader_toolkit { } - std::vector SlangSession::readCompiledBinaryCode(ComPtr component, size_t entryPointCount) + std::vector SlangSession::readCompiledBinaryCode(ComPtr component) { - std::vector finalCode; - for (size_t i = 0; i < entryPointCount; i++) + std::vector finalCode; + + ComPtr shaderBlob = nullptr; + ComPtr diagnosticBlob = nullptr; + SlangResult result = component->getTargetCode(0, shaderBlob.writeRef(), diagnosticBlob.writeRef()); + + if (SLANG_SUCCEEDED(result)) { - ComPtr shaderBlob = nullptr; - ComPtr diagnosticBlob = nullptr; - SlangResult result = component->getEntryPointCode(i, 0, shaderBlob.writeRef(), diagnosticBlob.writeRef()); - if (SLANG_SUCCEEDED(result)) { + auto* data = reinterpret_cast(shaderBlob->getBufferPointer()); + size_t wordCount = shaderBlob->getBufferSize() / sizeof(uint32_t); - auto ptr = (uint8_t*) shaderBlob->getBufferPointer(); - for (size_t i = 0; i < shaderBlob->getBufferSize(); i++) - { - finalCode.push_back(ptr[i]); - } - } - else { - std::string error = diagnosticBlob ? std::string((const char*)diagnosticBlob->getBufferPointer(), diagnosticBlob->getBufferSize()) : "Unknown error"; - spdlog::error("Failed to get compiled code for entry point index {}. {}", i, error); + for (size_t j = 0; j < wordCount; j++) + { + finalCode.push_back(data[j]); } } + else + { + std::string error = diagnosticBlob ? std::string((const char*)diagnosticBlob->getBufferPointer(), diagnosticBlob->getBufferSize()) : "Unknown error"; + spdlog::error("Failed to get compiled code. {}", error); + } + return finalCode; } @@ -128,8 +132,9 @@ namespace ris_shader_toolkit { const std::string& sourceCode, SlangCompileTarget compileTarget, std::string profile, - std::vector stages, - std::vector entryPoints + const std::vector stages, + const std::vector entryPoints, + SlangSessionOptions options ) { // 1. CREATE A SLANG SESSION slang::TargetDesc targetDesc; @@ -138,7 +143,7 @@ namespace ris_shader_toolkit { slang::SessionDesc sessionDesc; sessionDesc.targets = &targetDesc; sessionDesc.targetCount = 1; - sessionDesc.defaultMatrixLayoutMode = SLANG_MATRIX_LAYOUT_COLUMN_MAJOR; + sessionDesc.defaultMatrixLayoutMode = options.matrixLayoutMode; if (!profile.empty()) { @@ -171,12 +176,15 @@ namespace ris_shader_toolkit { { if (diagnosticBlob.readRef()) { - std::string error = std::string((const char*)diagnosticBlob->getBufferPointer(), diagnosticBlob->getBufferSize()); - return SlangCompileResult(false, "", ShaderReflection(), error); + std::string slangErr = std::string((const char*)diagnosticBlob->getBufferPointer(), diagnosticBlob->getBufferSize()); + std::string error = "Failed to create slang module. " + slangErr; + return SlangCompileResult::errorResult(error); } else { - return SlangCompileResult(false, "", ShaderReflection(), "Failed to load module from source string, and no diagnostics available."); + std::string error ="Failed to load module from source string, and no diagnostics available."; + spdlog::error(error); + return SlangCompileResult::errorResult(error); } } componentTypes.push_back(module); @@ -184,7 +192,7 @@ namespace ris_shader_toolkit { // 3. LOOK UP ENTRY POINTS AND COMPILE // If we have entry points, we will use that to read, otherwise we will simply lookup vertex stages. - std::vector > entryPointInterfaces; + std::vector> entryPointInterfaces; if (entryPoints.size() > 0) { for (size_t i = 0; i < entryPoints.size(); i++) @@ -212,16 +220,78 @@ namespace ris_shader_toolkit { // This requires the Slang source code to be decorated with @shader attributes to specify the stage for each entry point. else { - for (size_t i = 0; i < stages.size(); i++) + SlangInt32 entryPointCount = module->getDefinedEntryPointCount(); + + spdlog::trace("Entry point count: " + std::to_string(entryPointCount)); + if(entryPointCount == 0) + { + std::string msg = "Module has no entry point."; + spdlog::error(msg); + return SlangCompileResult::errorResult(msg); + } + + for (SlangInt32 i = 0; i < entryPointCount; i++) { ComPtr entryPoint = nullptr; SlangResult result = module->getDefinedEntryPoint(i, entryPoint.writeRef()); + - entryPointInterfaces.push_back(entryPoint); - componentTypes.push_back(entryPoint.get()); + if (SLANG_FAILED(result)) + { + if (diagnosticBlob) + { + std::string bufferMsg = std::string((const char*)diagnosticBlob->getBufferPointer(), diagnosticBlob->getBufferSize()); + std::string error ="Failed to find entry point by index: " + std::to_string(i) + ". " + bufferMsg; + spdlog::error(error); + return SlangCompileResult::errorResult(error); + } + else + { + std::string error ="Failed to find entry point: by index" + std::to_string(i) + ". No diagnostics available."; + spdlog::error(error); + return SlangCompileResult::errorResult(error); + } + } + + spdlog::trace("Found entry point: " + std::to_string(i)); + + auto slangStage = findEntryPointStage(entryPoint); + + if (!slangStage.has_value()) + { + std::string error = "Unable to find entry point stage."; + spdlog::error(error); + return SlangCompileResult::errorResult(error); + } + + spdlog::trace("Entry point stage: " + std::to_string(slangStage.value())); + + for (size_t i = 0; i < stages.size(); i++) + { + // Check if we have a stage. + SlangStage a = stages[i]; + SlangStage b = slangStage.value(); + + spdlog::trace("Comparing " + std::to_string(a) + ", " + std::to_string(b)); + + if (a == b) + { + spdlog::trace("Pushing"); + entryPointInterfaces.push_back(entryPoint); + componentTypes.push_back(entryPoint.get()); + } + } + } + + if (entryPointInterfaces.size() == 0) + { + std::string error = "Unable to find entry point."; + spdlog::error(error); + return SlangCompileResult::errorResult(error); } } + // 4. CREATE A COMPOSITE COMPONENT TYPE // Link the module to a composite component type, which represents the final shader program that can be executed. ComPtr program; @@ -243,13 +313,14 @@ namespace ris_shader_toolkit { // BINARY FORMATS if (compileTarget == SlangCompileTarget::SLANG_SPIRV) { - auto binary = readCompiledBinaryCode(program, entryPointCount); + // We can read multiple binary entry points, but for SPIR-V we only + // ever support single entry point, so we will just read the first one. + auto binary = readCompiledBinaryCode(program); return SlangCompileResult(true, binary, ShaderReflection()); } - else + else { // Get entry points. - std::string code = readCompiledCode(program, entryPointCount); return SlangCompileResult(true, code, ShaderReflection()); } @@ -263,18 +334,18 @@ namespace ris_shader_toolkit { slang::ProgramLayout* layout = program->getLayout(0); // Modify entry points. - int entryPointCount = layout->getEntryPointCount(); - for (int i = 0; i < entryPointCount; i++) + SlangUInt entryPointCount = layout->getEntryPointCount(); + for (SlangUInt i = 0; i < entryPointCount; i++) { slang::EntryPointLayout* entryPointLayout = layout->getEntryPointByIndex(i); const char* entryPointName = entryPointLayout->getName(); } // Modify parameters - int paramCount = layout->getParameterCount(); - for (int i = 0; i < paramCount; i++) + SlangUInt paramCount = layout->getParameterCount(); + for (SlangUInt i = 0; i < paramCount; i++) { - slang::VariableLayoutReflection* param = layout->getParameterByIndex(i); + slang::VariableLayoutReflection* param = layout->getParameterByIndex(static_cast(i)); slang::TypeReflection* type = param->getType(); slang::TypeReflection::Kind kind = type->getKind(); SlangStage stage = param->getStage(); @@ -282,7 +353,7 @@ namespace ris_shader_toolkit { ShaderBinding binding; binding.name = param->getName(); - binding.set = param->getSemanticIndex(); + binding.set = static_cast(param->getSemanticIndex()); binding.binding = param->getBindingIndex(); if (_bindingTypeMap.find(kind) == _bindingTypeMap.end()) @@ -296,24 +367,53 @@ namespace ris_shader_toolkit { } } - /*SlangCompileResult SlangSession::compileSourceCodeToGlsl( - const std::string& slangSourceCode, - ShaderStage stage, - const std::string& entryPoint, - GlslProfile profile) + std::optional SlangSession::findEntryPointStage(ComPtr entryPoint) { - SlangStage slangStage = shaderStageMap[stage]; - std::string glslProfile = glslProfileMap[profile]; + auto functionReflection = entryPoint->getFunctionReflection(); + if (functionReflection == nullptr) + { + spdlog::error("Failed to get function reflection for entry point."); + return std::nullopt; + } + + spdlog::trace("Found reflection object for entry point"); - return compile( - slangSourceCode, - SLANG_GLSL, - glslProfile, - { slangStage }, - { entryPoint } - ); + // We need to check if entry point has a "shader" attribute for example, [shader("vertex")] VSOutput main_vs(VSInput input) + auto shaderAttr = functionReflection->findAttributeByName(_globalSession.get(), "shader"); + if (shaderAttr == nullptr) + { + spdlog::error("Failed to find 'shader' attribute."); + return std::nullopt; + } + + spdlog::trace("Found 'shader' attribute from reflection object of entry point."); + + // shader attribute will contain value such as "vertex" eg. shader("vertex") + size_t nameLength = 0; + const char* namePtr = shaderAttr->getArgumentValueString(0, &nameLength); + std::string nameStr(namePtr, nameLength); + if (nameLength == 0) + { + spdlog::error("Failed to retrieve 'shader' attribute argument value."); + return std::nullopt; + } + + spdlog::trace("Value of 'shader' attribute is '" + nameStr + "'."); + + // NOTE!: Windows returns without "", while macos returns with. + if(nameStr == "vertex" || nameStr == "\"vertex\"") + return SLANG_STAGE_VERTEX; + else if(nameStr == "fragment" || nameStr == "\"fragment\"") + return SLANG_STAGE_FRAGMENT; + else if(nameStr == "compute" || nameStr == "\"compute\"") + return SLANG_STAGE_COMPUTE; + + spdlog::error("'shader' attribute with value of " "'" + nameStr + "'is not handled"); + return std::nullopt; } + /* + SlangCompileResult SlangSession::compileToHlsl( const std::string& filePath, ShaderStage stage, @@ -374,13 +474,13 @@ namespace ris_shader_toolkit { SlangCompileResult SlangSession::compileToWgsl( const std::string& sourceCode, - std::vector stages, - std::vector entryPoints) + const std::vector stages, + const std::vector entryPoints) { std::vector slangStages; for (const auto& stage : stages) { - slangStages.push_back(shaderStageMap[stage]); + slangStages.push_back(_shaderStageMap[stage]); } return compile( @@ -396,23 +496,50 @@ namespace ris_shader_toolkit { const std::string& sourceCode, std::vector stages, std::vector entryPoints, - SpirVProfile profile + SpirVProfile profile, + SlangSessionOptions options ) { std::vector slangStages; for (const auto& stage : stages) { - slangStages.push_back(shaderStageMap[stage]); + slangStages.push_back(_shaderStageMap[stage]); } - std::string spirVProfile = spirvProfileMap[profile]; + std::string spirVProfile = _spirvProfileMap[profile]; return compile( sourceCode, SLANG_SPIRV, spirVProfile, slangStages, + entryPoints, + options + ); + } + + SlangCompileResult SlangSession::compileToGlsl( + const std::string& sourceCode, + ShaderStage stages, + std::string entryPoint, + GlslProfile profile) { + + SlangStage slangStage = _shaderStageMap[stages]; + std::string glslProfile = _glslProfileMap[profile]; + + std::vector entryPoints; + if (!entryPoint.empty()) + { + entryPoints.push_back(entryPoint); + } + + return compile( + sourceCode, + SLANG_GLSL, + glslProfile, + { slangStage }, entryPoints ); + } -} \ No newline at end of file +} diff --git a/ShaderToolkitC/src/spirv-cross/spirv_cross_compiler.cpp b/ShaderToolkitC/src/spirv-cross/spirv_cross_compiler.cpp index 0682888..9b7810c 100644 --- a/ShaderToolkitC/src/spirv-cross/spirv_cross_compiler.cpp +++ b/ShaderToolkitC/src/spirv-cross/spirv_cross_compiler.cpp @@ -5,7 +5,7 @@ namespace ris_shader_toolkit { SpirVCrossCompiler::SpirVCrossCompiler() { - glslVersionMap = { + _glslVersionMap = { { GlslProfile::GLSL_330, 330 }, { GlslProfile::GLSL_400, 400 }, { GlslProfile::GLSL_410, 410 }, @@ -18,142 +18,222 @@ namespace ris_shader_toolkit { { GlslProfile::GLES_310, 310 }, { GlslProfile::GLES_320, 320 } }; + + _executionModelMap = { + { ShaderStage::Vertex, spv::ExecutionModel::ExecutionModelVertex }, + { ShaderStage::Fragment, spv::ExecutionModel::ExecutionModelFragment }, + { ShaderStage::Compute, spv::ExecutionModel::ExecutionModelGLCompute }, + //{ ShaderStage::Geometry, spv::ExecutionModel::ExecutionModelGeometry }, + //{ ShaderStage::Hull, spv::ExecutionModel::ExecutionModelTessellationControl }, + //{ ShaderStage::Domain, spv::ExecutionModel::ExecutionModelTessellationEvaluation } + }; } - void SpirVCrossCompiler::applyReplaceStageInputNameRule( + void SpirVCrossCompiler::handleImageAndSamplersGlsl( spirv_cross::CompilerGLSL& compiler, - spirv_cross::ShaderResources& shaderResources, - ReplaceStageInputNameRule* rule) { + spirv_cross::ShaderResources& shaderResources) { + + // Since output of SpirV to glsl might have name such as _31, we keep name of sampler, + // to replace that with sampler name instead. + std::map samplerIdNames; + for (auto& sampler : shaderResources.separate_samplers) + { + samplerIdNames[sampler.id] = compiler.get_name(sampler.id); + } + compiler.build_combined_image_samplers(); // Finalize combined samplers for GLSL - // By default, names have "input_" prefix, we will remove it here - // Rename vertex inputs from "input_{name}" to just "{name}" - for (auto& input : shaderResources.stage_inputs) + for (auto& cs : compiler.get_combined_image_samplers()) { - std::string name = compiler.get_name(input.id); - std::string newName = name; + // Rename _31 to something meaningful, e.g., diffuseTexture + compiler.set_name(cs.combined_id, samplerIdNames[cs.sampler_id]); + } + } - // Remove "input_" prefix if it exists - if (name.rfind("input.", 0) == 0) // starts with "input_" + void SpirVCrossCompiler::handleAttributes( + spirv_cross::CompilerGLSL& vertexCompiler, + spirv_cross::CompilerGLSL& fragmentCompiler, + GlslProfile glslProfile, + ShaderStage stage) + { + + // For GLES 3.0, we need to ensure that the input/output variable names match between vertex and fragment shaders. + // So input attribute of fragment shader should have name to corresponding output attribute of vertex shader. + // This is required for linking shaders in GLES 3.0. + if (glslProfile == GlslProfile::GLES_300 && stage == ShaderStage::Fragment) + { + std::map inputIdNames; + uint32_t location = 0; + auto vertexShaderResources = vertexCompiler.get_shader_resources(); + for (auto& outAttr : vertexShaderResources.stage_outputs) { - newName = name.substr(6); // remove first 6 chars + std::string name = outAttr.name; + inputIdNames[location] = name; + location++; } - if (rule != nullptr) { - // Apply prefix from rule - newName = rule->getPrefix() + newName; + location = 0; + auto fragmentShaderResources = fragmentCompiler.get_shader_resources(); + for (auto& inAttr : fragmentShaderResources.stage_inputs) + { + std::string name = inputIdNames[location]; + fragmentCompiler.set_name(inAttr.id, name); + location++; } - - compiler.set_name(input.id, newName); } } - void SpirVCrossCompiler::applyReplaceStageOutputNameRule( - spirv_cross::CompilerGLSL& compiler, - spirv_cross::ShaderResources& shaderResources, - ReplaceStageOutputNameRule* rule) { - - // By default, names have "entryPointParam_" prefix, we will remove it here - // Rename vertex inputs from "entryPointParam_{entryName}.{name}" to just "{entryName}.{name}" - for (auto& input : shaderResources.stage_outputs) + void SpirVCrossCompiler::handleUniforms(spirv_cross::CompilerGLSL& compiler) + { + auto resources = compiler.get_shader_resources(); + for (auto& ubo : resources.uniform_buffers) { - std::string name = compiler.get_name(input.id); - std::string newName = name; + spdlog::debug("UBO: " + ubo.name); - // Remove "input_" prefix if it exists - if (name.rfind("entryPointParam_", 0) == 0) // starts with "entryPointParam_" + uint32_t type_id = ubo.base_type_id; + + // If it's row major, then it applies to the whole block, otherwise we need to check each member. + if (compiler.has_decoration(type_id, spv::DecorationRowMajor)) { - newName = name.substr(16); // remove first 16 chars + compiler.unset_decoration(type_id, spv::DecorationRowMajor); + compiler.set_decoration(type_id, spv::DecorationColMajor); + spdlog::debug(" has RowMajor decoration, changed to ColMajor"); } + else + { + auto& type = compiler.get_type(ubo.base_type_id); + for (uint32_t i = 0; i < type.member_types.size(); i++) + { + spdlog::debug(" member " + std::to_string(i)); + + if (compiler.has_member_decoration(ubo.base_type_id, i, spv::DecorationRowMajor)) + { + spdlog::debug(" has RowMajor decoration"); + } - if (rule != nullptr) { - - if(rule->getTrimEntryPointName()) { - // Further trim entry point name if exists - size_t dotPos = newName.find('.'); - if (dotPos != std::string::npos) { - newName = newName.substr(dotPos + 1); // remove up to and including the dot + if (compiler.has_member_decoration(ubo.base_type_id, i, spv::DecorationColMajor)) + { + spdlog::debug(" has ColMajor decoration"); } - } - // Apply prefix from rule - newName = rule->getPrefix() + newName; + std::cout << "\n"; + } } - - compiler.set_name(input.id, newName); } } - void SpirVCrossCompiler::handleImageAndSamplersGlsl( - spirv_cross::CompilerGLSL& compiler, - spirv_cross::ShaderResources& shaderResources) { + SpirVCrossCompileResult SpirVCrossCompiler::compileForLowerProfiles(const std::vector& spirv, GlslProfile profile, ShaderStage stage) + { + // Load SPIR-V + try { + spirv_cross::CompilerGLSL vertexCompiler(spirv); + spirv_cross::CompilerGLSL fragmentCompiler(spirv); - // Since output of SpirV to glsl might have name such as _31, we keep name of sampler, - // to replace that with sampler name instead. - std::map samplerIdNames; - for (auto& sampler : shaderResources.separate_samplers) - { - samplerIdNames[sampler.id] = compiler.get_name(sampler.id); - } + // Default to vertex compiler, will switch to fragment compiler if stage is fragment shader. + spirv_cross::CompilerGLSL* targetCompiler = &vertexCompiler; + if (stage == ShaderStage::Fragment) + { + targetCompiler = &fragmentCompiler; + } - compiler.build_combined_image_samplers(); // Finalize combined samplers for GLSL + // Set GLSL options + spirv_cross::CompilerGLSL::Options options; + options.version = _glslVersionMap[profile]; + options.force_zero_initialized_variables = false; + options.enable_storage_image_qualifier_deduction = true; + if (profile == GlslProfile::GLES_300 + || profile == GlslProfile::GLES_310 + || profile == GlslProfile::GLES_320) { + options.es = true; + } - for (auto& cs : compiler.get_combined_image_samplers()) - { - // Rename _31 to something meaningful, e.g., diffuseTexture - compiler.set_name(cs.combined_id, samplerIdNames[cs.sampler_id]); + auto entry_points = vertexCompiler.get_entry_points_and_stages(); + for (size_t i = 0; i < entry_points.size(); ++i) + { + auto executionModel = entry_points[i].execution_model; + + if (spv::ExecutionModel::ExecutionModelVertex == executionModel) + { + auto entryPointName = entry_points[i].name; + vertexCompiler.set_entry_point(entryPointName, executionModel); + } + else if (spv::ExecutionModel::ExecutionModelFragment == executionModel) + { + auto entryPointName = entry_points[i].name; + fragmentCompiler.set_entry_point(entryPointName, executionModel); + } + else + { + std::string msg = "Unsupported execution model: " + std::to_string(executionModel); + spdlog::error(msg); + return SpirVCrossCompileResult::errorResult(msg); + } + } + + auto vertexShaderResources = vertexCompiler.get_shader_resources(); + auto fragmentShaderResources = fragmentCompiler.get_shader_resources(); + + handleImageAndSamplersGlsl(fragmentCompiler, fragmentShaderResources); + handleAttributes(vertexCompiler, fragmentCompiler, profile, stage); + handleUniforms(vertexCompiler); + handleUniforms(fragmentCompiler); + vertexCompiler.set_common_options(options); + fragmentCompiler.set_common_options(options); + + // Compile to GLSL + std::string glslSource = targetCompiler->compile(); + return SpirVCrossCompileResult(true, glslSource, ""); + } + catch (const std::exception& e) { + std::string msg = "SPIRV-Cross compilation failed: " + std::string(e.what()); + spdlog::error(msg); + return SpirVCrossCompileResult::errorResult(msg); } } - SpirVCrossCompileResult SpirVCrossCompiler::compile( - const std::string& filePath, GlslProfile profile, - ReplaceStageInputNameRule* replaceStageInputNameRule, - ReplaceStageOutputNameRule* replaceStageOutputNameRule - ) { + SpirVCrossCompileResult SpirVCrossCompiler::compile(const std::vector& spirv, GlslProfile profile, ShaderStage stage) { - // Load SPIR-V - std::vector spirv = fileReader.readAsU32(filePath); - return compile(spirv, profile, replaceStageInputNameRule); - } + if (profile == GlslProfile::GLES_300) + { + return compileForLowerProfiles(spirv, profile, stage); + } - SpirVCrossCompileResult SpirVCrossCompiler::compile( - const std::vector& spirv, GlslProfile profile, - ReplaceStageInputNameRule* replaceStageInputNameRule, - ReplaceStageOutputNameRule* replaceStageOutputNameRule - ) { // Load SPIR-V try { - - spdlog::info("Compiling SPIR-V to GLSL using SPIRV-Cross."); - spirv_cross::CompilerGLSL compiler(spirv); + // Set GLSL options spirv_cross::CompilerGLSL::Options options; - options.version = glslVersionMap[profile]; + options.version = _glslVersionMap[profile]; options.force_zero_initialized_variables = false; - if (profile == GlslProfile::GLES_300 || profile == GlslProfile::GLES_310 || profile == GlslProfile::GLES_320) { - spdlog::info("Using OpenGL ES profile for GLSL."); + if (profile == GlslProfile::GLES_310 || profile == GlslProfile::GLES_320) { options.es = true; } + options.vertex.fixup_clipspace = true; - compiler.set_common_options(options); + auto entry_points = compiler.get_entry_points_and_stages(); + for (size_t i = 0; i < entry_points.size(); ++i) + { + auto executionModel = entry_points[i].execution_model; + auto desiredExecutionModel = _executionModelMap[stage]; - // Get shader resources - spirv_cross::ShaderResources resources = compiler.get_shader_resources(); + if (desiredExecutionModel == executionModel) + { + auto entryPointName = entry_points[i].name; + compiler.set_entry_point(entryPointName, executionModel); + } + } - applyReplaceStageInputNameRule(compiler, resources, replaceStageInputNameRule); - applyReplaceStageOutputNameRule(compiler, resources, replaceStageOutputNameRule); - handleImageAndSamplersGlsl(compiler, resources); + auto shaderResources = compiler.get_shader_resources(); + handleImageAndSamplersGlsl(compiler, shaderResources); + compiler.set_common_options(options); // Compile to GLSL std::string glslSource = compiler.compile(); - spdlog::info("Successfully compiled SPIR-V to GLSL."); - return SpirVCrossCompileResult(true, glslSource, ""); } catch (const std::exception& e) { - spdlog::error("SPIRV-Cross compilation failed: {}", e.what()); return SpirVCrossCompileResult(false, "", e.what()); } diff --git a/ShaderToolkitC/tests/c_compiler_slang_to_glsl_tests.cpp b/ShaderToolkitC/tests/c_compiler_slang_to_glsl_tests.cpp new file mode 100644 index 0000000..c21a183 --- /dev/null +++ b/ShaderToolkitC/tests/c_compiler_slang_to_glsl_tests.cpp @@ -0,0 +1,117 @@ +#include +#include +#include + +using namespace ris_shader_toolkit; + + +const std::string SOURCE_CODE = R"(struct VSInput +{ + float3 position : POSITION; + float2 texCoord : TEXCOORD0; + float4 color : COLOR0; +} + +struct VSOutput +{ + float4 position : SV_POSITION; + float2 texCoord : TEXCOORD0; + float4 color : COLOR0; +} + +ConstantBuffer viewProjection : register(b0, space0); + +[shader("vertex")] +VSOutput main_vs(VSInput input) +{ + VSOutput output; + output.position = mul(viewProjection, float4(input.position, 1.0)); + output.texCoord = input.texCoord; + output.color = input.color; + return output; +} + +Texture2D diffuseTexture: register(t0, space1); +SamplerState diffuseTexSampler : register(s1, space1); + +[shader("fragment")] +float4 main_fs(VSOutput input) : SV_TARGET +{ + float4 textureColor = diffuseTexture.Sample(diffuseTexSampler, input.texCoord); + return textureColor * input.color; +})"; + + +//! Test if the compiler can successfully compile Slang source code to GLSL 4.5, +bool c_compile_slang_to_glsl_450_vs() +{ + void* compilerPtr = create_compiler(); + + c_CompileResult* resultPtr = (c_CompileResult*)compile_slang_to_glsl_ext( + compilerPtr, + SOURCE_CODE.c_str(), + static_cast(ShaderStage::Vertex), + "main_vs", + ris_shader_toolkit::GlslProfile::GLSL_450 + ); + + bool isSuccess = resultPtr->success; + std::string sourceCodeStr(resultPtr->sourceCode); + bool containsMain = sourceCodeStr.find("main") != std::string::npos; + + free_compile_result(resultPtr); + free_compiler(compilerPtr); + + return isSuccess && containsMain; +} + +//! Test if the compiler can successfully compile Slang source code to GLSL 4.5, +bool c_compile_slang_to_glsl_450_fs() +{ + void* compilerPtr = create_compiler(); + + c_CompileResult* resultPtr = (c_CompileResult*)compile_slang_to_glsl( + compilerPtr, + SOURCE_CODE.c_str(), + static_cast(ShaderStage::Fragment), + ris_shader_toolkit::GlslProfile::GLSL_450 + ); + + bool isSuccess = resultPtr->success; + std::string sourceCodeStr(resultPtr->sourceCode); + bool containsMain = sourceCodeStr.find("main") != std::string::npos; + + free_compile_result(resultPtr); + free_compiler(compilerPtr); + + return isSuccess && containsMain; +} + +//! Test if the compiler can successfully compile Slang source code to GLSL 300 es +bool c_compile_slang_to_glsl_300_es_vs() +{ + void* compilerPtr = create_compiler(); + + c_CompileResult* resultPtr = (c_CompileResult*)compile_slang_to_glsl( + compilerPtr, + SOURCE_CODE.c_str(), + static_cast(ShaderStage::Vertex), + ris_shader_toolkit::GlslProfile::GLES_300 + ); + + bool isSuccess = resultPtr->success; + std::string sourceCodeStr(resultPtr->sourceCode); + bool containsMain = sourceCodeStr.find("main") != std::string::npos; + + free_compile_result(resultPtr); + free_compiler(compilerPtr); + + return isSuccess && containsMain; +} + +TEST_CASE("c compiler slang to glsl tests", "[c_compile_slang_to_glsl_450_vs, c_compile_slang_to_glsl_450_fs, c_compile_slang_to_glsl_300_es_vs]") +{ + REQUIRE(c_compile_slang_to_glsl_450_vs()); + REQUIRE(c_compile_slang_to_glsl_450_fs()); + REQUIRE(c_compile_slang_to_glsl_300_es_vs()); +} diff --git a/ShaderToolkitC/tests/c_compiler_slang_to_wgsl_tests.cpp b/ShaderToolkitC/tests/c_compiler_slang_to_wgsl_tests.cpp new file mode 100644 index 0000000..669281a --- /dev/null +++ b/ShaderToolkitC/tests/c_compiler_slang_to_wgsl_tests.cpp @@ -0,0 +1,90 @@ +#include +#include +#include + +using namespace ris_shader_toolkit; + + +const std::string SOURCE_CODE = R"(struct VSInput +{ + float3 position : POSITION; + float2 texCoord : TEXCOORD0; + float4 color : COLOR0; +} + +struct VSOutput +{ + float4 position : SV_POSITION; + float2 texCoord : TEXCOORD0; + float4 color : COLOR0; +} + +ConstantBuffer viewProjection : register(b0, space0); + +[shader("vertex")] +VSOutput main_vs(VSInput input) +{ + VSOutput output; + output.position = mul(viewProjection, float4(input.position, 1.0)); + output.texCoord = input.texCoord; + output.color = input.color; + return output; +} + +Texture2D diffuseTexture: register(t0, space1); +SamplerState diffuseTexSampler : register(s1, space1); + +[shader("fragment")] +float4 main_fs(VSOutput input) : SV_TARGET +{ + float4 textureColor = diffuseTexture.Sample(diffuseTexSampler, input.texCoord); + return textureColor * input.color; +})"; + + +//! Test if the compiler can successfully compile Slang source code to GLSL 4.5, +bool c_compile_slang_to_wgsl() +{ + void* compilerPtr = create_compiler(); + + int32_t shaderStages[] = { static_cast(ShaderStage::Vertex), static_cast(ShaderStage::Fragment) }; + + c_CompileResult* resultPtr = (c_CompileResult*)compile_slang_to_wgsl( + compilerPtr, + SOURCE_CODE.c_str(), + shaderStages, + 2 + ); + + bool isSuccess = resultPtr->success; + + free_compile_result(resultPtr); + free_compiler(compilerPtr); + + return isSuccess; +} + +bool c_compile_slang_to_wgsl_with_entry_points() +{ + void* compilerPtr = create_compiler(); + int32_t shaderStages[] = { static_cast(ShaderStage::Vertex), static_cast(ShaderStage::Fragment) }; + const char* entryPoints[] = { "main_vs", "main_fs" }; + c_CompileResult* resultPtr = (c_CompileResult*)compile_slang_to_wgsl_ext( + compilerPtr, + SOURCE_CODE.c_str(), + shaderStages, + 2, + entryPoints, + 2 + ); + bool isSuccess = resultPtr->success; + free_compile_result(resultPtr); + free_compiler(compilerPtr); + return isSuccess; +} + +TEST_CASE("c compiler slang to glsl tests", "[c_compile_slang_to_wgsl, c_compile_slang_to_wgsl_with_entry_points]") +{ + REQUIRE(c_compile_slang_to_wgsl()); + REQUIRE(c_compile_slang_to_wgsl_with_entry_points()); +} diff --git a/ShaderToolkitC/tests/compiler_slang_to_glsl_tests.cpp b/ShaderToolkitC/tests/compiler_slang_to_glsl_tests.cpp new file mode 100644 index 0000000..e096754 --- /dev/null +++ b/ShaderToolkitC/tests/compiler_slang_to_glsl_tests.cpp @@ -0,0 +1,95 @@ +#include +#include +#include + +using namespace ris_shader_toolkit; + + +const std::string SOURCE_CODE = R"(struct VSInput +{ + float3 position : POSITION; + float2 texCoord : TEXCOORD0; + float4 color : COLOR0; +} + +struct VSOutput +{ + float4 position : SV_POSITION; + float2 texCoord : TEXCOORD0; + float4 color : COLOR0; +} + +ConstantBuffer viewProjection : register(b0, space0); + +[shader("vertex")] +VSOutput main_vs(VSInput input) +{ + VSOutput output; + output.position = mul(viewProjection, float4(input.position, 1.0)); + output.texCoord = input.texCoord; + output.color = input.color; + return output; +} + +Texture2D diffuseTexture: register(t0, space1); +SamplerState diffuseTexSampler : register(s1, space1); + +[shader("fragment")] +float4 main_fs(VSOutput input) : SV_TARGET +{ + float4 textureColor = diffuseTexture.Sample(diffuseTexSampler, input.texCoord); + return textureColor * input.color; +})"; + + +//! Test if the compiler can successfully compile Slang source code to GLSL 4.5, +bool compile_slang_to_glsl_450() +{ + Compiler compiler; + + CompileResult vertexResult = compiler.compileSlangToGlsl( + SOURCE_CODE, + ShaderStage::Vertex, + "main_vs", + GlslProfile::GLSL_450 + ); + + CompileResult fragmentResult = compiler.compileSlangToGlsl( + SOURCE_CODE, + ShaderStage::Fragment, + "main_fs", + GlslProfile::GLSL_450 + ); + + return vertexResult.isSuccess() && vertexResult.getSourceCode().find("main") != std::string::npos + && fragmentResult.isSuccess() && fragmentResult.getSourceCode().find("main") != std::string::npos; +} + +//! Test if the compiler can successfully compile Slang source code to GLSL 300 es +bool compile_slang_to_glsl_300_es() +{ + Compiler compiler; + + CompileResult vertexResult = compiler.compileSlangToGlsl( + SOURCE_CODE, + ShaderStage::Vertex, + "", + GlslProfile::GLES_300 + ); + + CompileResult fragmentResult = compiler.compileSlangToGlsl( + SOURCE_CODE, + ShaderStage::Fragment, + "", + GlslProfile::GLES_300 + ); + + return vertexResult.isSuccess() && vertexResult.getSourceCode().find("main") != std::string::npos + && fragmentResult.isSuccess() && fragmentResult.getSourceCode().find("main") != std::string::npos; +} + +TEST_CASE("compiler slang to glsl tests", "[compile_slang_to_glsl_450, compile_slang_to_glsl_300_es]") +{ + REQUIRE(compile_slang_to_glsl_450()); + REQUIRE(compile_slang_to_glsl_300_es()); +} diff --git a/ShaderToolkitC/tests/compiler_slang_to_spirv_tests.cpp b/ShaderToolkitC/tests/compiler_slang_to_spirv_tests.cpp index 6babd26..3da8221 100644 --- a/ShaderToolkitC/tests/compiler_slang_to_spirv_tests.cpp +++ b/ShaderToolkitC/tests/compiler_slang_to_spirv_tests.cpp @@ -50,12 +50,8 @@ bool compile_slang_to_spir_v() { Compiler compiler; - CompileResult result = compiler.compileSlangToSpirV( - SOURCE_CODE, - { ShaderStage::Vertex, ShaderStage::Fragment }, - { } - ); - + std::vector stages = { ShaderStage::Vertex, ShaderStage::Fragment }; + auto result = compiler.compileSlangToSpirV(SOURCE_CODE, stages); return result.isSuccess() && result.getBinaryCode().size() > 0; } @@ -66,11 +62,9 @@ bool compile_slang_to_spir_v_2() { Compiler compiler; - CompileResult result = compiler.compileSlangToSpirV( - SOURCE_CODE, - { ShaderStage::Vertex, ShaderStage::Fragment }, - { "main_vs", "main_fs" } - ); + std::vector stages = { ShaderStage::Vertex, ShaderStage::Fragment }; + std::vector entryPoints = { "main_vs", "main_fs" }; + auto result = compiler.compileSlangToSpirV(SOURCE_CODE, stages, entryPoints); return result.isSuccess() && result.getBinaryCode().size() > 0; } diff --git a/ShaderToolkitC/tests/compiler_slang_to_wgsl_tests.cpp b/ShaderToolkitC/tests/compiler_slang_to_wgsl_tests.cpp index 3a20cc0..07ba412 100644 --- a/ShaderToolkitC/tests/compiler_slang_to_wgsl_tests.cpp +++ b/ShaderToolkitC/tests/compiler_slang_to_wgsl_tests.cpp @@ -50,11 +50,9 @@ bool compile_slang_to_wgsl() { Compiler compiler; - CompileResult result = compiler.compileSlangToWgsl( - SOURCE_CODE, - { ShaderStage::Vertex, ShaderStage::Fragment }, - { } - ); + std::vector stages = { ShaderStage::Vertex, ShaderStage::Fragment }; + std::vector entryPoints; + CompileResult result = compiler.compileSlangToWgsl(SOURCE_CODE,stages,entryPoints); return result.isSuccess() && result.getSourceCode().find("fn main_vs") != std::string::npos && result.getSourceCode().find("fn main_fs") != std::string::npos; } @@ -66,11 +64,9 @@ bool compile_slang_to_wgsl_2() { Compiler compiler; - CompileResult result = compiler.compileSlangToWgsl( - SOURCE_CODE, - { ShaderStage::Vertex, ShaderStage::Fragment }, - { "main_vs", "main_fs" } - ); + std::vector stages = { ShaderStage::Vertex, ShaderStage::Fragment }; + std::vector entryPoints = { "main_vs", "main_fs" }; + CompileResult result = compiler.compileSlangToWgsl(SOURCE_CODE, stages, entryPoints); return result.isSuccess() && result.getSourceCode().find("fn main_vs") != std::string::npos && result.getSourceCode().find("fn main_fs") != std::string::npos; } diff --git a/ShaderToolkitC/tests/process_tests.cpp b/ShaderToolkitC/tests/process_tests.cpp index 35e8850..d7d78f1 100644 --- a/ShaderToolkitC/tests/process_tests.cpp +++ b/ShaderToolkitC/tests/process_tests.cpp @@ -1,50 +1,50 @@ -#include -#include -#include "process/process.hpp" -#include - -using namespace ris_shader_toolkit; - -// Path to fxc.exe -// TODO: find a better way to locate fxc.exe -const std::string SDK_PATH = "C:\\Program Files (x86)\\Windows Kits\\10\\bin\\10.0.22621.0\\x64\\fxc.exe"; - -bool process_compile_hlsl_to_fxc() -{ -#if _WIN32 - std::string input = "D:/Projects/ShaderToolkitSharp/ShaderToolkitC/test_files/sprite_vs.hlsl"; - std::string output = "D:/Projects/ShaderToolkitSharp/ShaderToolkitC/test_files/sprite_vs_fxc.cso"; - - std::string fxc_output; - bool result = ris_shader_toolkit::Process::launchWin(SDK_PATH + " /T vs_5_0 /E main /Fo " + output + " " + input, fxc_output); - - std::cout << "FXC Output: " << fxc_output << std::endl; - - return result; -#else - return true; // No need to run on non-Windows platforms -#endif -} - -bool process_compile_metal_to_air() -{ - // xcrun is only available on macOS -#if __APPLE__ - std::string input = "D:/Projects/ShaderToolkitSharp/ShaderToolkitC/test_files/sprite.metal"; - std::string output = "D:/Projects/ShaderToolkitSharp/ShaderToolkitC/test_files/sprite.air"; - - std::string xcrun_output; - bool result = ris_shader_toolkit::Process::launchMac("xcrun -sdk macosx metal " + input + " -o " + output, xcrun_output); - - std::cout << "xcrun Output: " << xcrun_output << std::endl; - - return result; -#else - return true; -#endif -} - -TEST_CASE("process tests", "[process_compile_hlsl_to_fxc], [process_compile_metal_to_air]") { - REQUIRE(process_compile_hlsl_to_fxc()); - REQUIRE(process_compile_metal_to_air()); -} \ No newline at end of file +//#include +//#include +//#include "process/process.hpp" +//#include +// +//using namespace ris_shader_toolkit; +// +//// Path to fxc.exe +//// TODO: find a better way to locate fxc.exe +//const std::string SDK_PATH = "C:\\Program Files (x86)\\Windows Kits\\10\\bin\\10.0.22621.0\\x64\\fxc.exe"; +// +//bool process_compile_hlsl_to_fxc() +//{ +//#if _WIN32 +// std::string input = "D:/Projects/ShaderToolkitSharp/ShaderToolkitC/test_files/sprite_vs.hlsl"; +// std::string output = "D:/Projects/ShaderToolkitSharp/ShaderToolkitC/test_files/sprite_vs_fxc.cso"; +// +// std::string fxc_output; +// bool result = ris_shader_toolkit::Process::launchWin(SDK_PATH + " /T vs_5_0 /E main /Fo " + output + " " + input, fxc_output); +// +// std::cout << "FXC Output: " << fxc_output << std::endl; +// +// return result; +//#else +// return true; // No need to run on non-Windows platforms +//#endif +//} +// +//bool process_compile_metal_to_air() +//{ +// // xcrun is only available on macOS +//#if __APPLE__ +// std::string input = "D:/Projects/ShaderToolkitSharp/ShaderToolkitC/test_files/sprite.metal"; +// std::string output = "D:/Projects/ShaderToolkitSharp/ShaderToolkitC/test_files/sprite.air"; +// +// std::string xcrun_output; +// bool result = ris_shader_toolkit::Process::launchMac("xcrun -sdk macosx metal " + input + " -o " + output, xcrun_output); +// +// std::cout << "xcrun Output: " << xcrun_output << std::endl; +// +// return result; +//#else +// return true; +//#endif +//} +// +//TEST_CASE("process tests", "[process_compile_hlsl_to_fxc], [process_compile_metal_to_air]") { +// REQUIRE(process_compile_hlsl_to_fxc()); +// REQUIRE(process_compile_metal_to_air()); +//} \ No newline at end of file diff --git a/ShaderToolkitC/tests/spirv_cross_compiler_tests.cpp b/ShaderToolkitC/tests/spirv_cross_compiler_tests.cpp new file mode 100644 index 0000000..8f62700 --- /dev/null +++ b/ShaderToolkitC/tests/spirv_cross_compiler_tests.cpp @@ -0,0 +1,107 @@ +#include +#include +#include +#include + +using namespace ris_shader_toolkit; + + +const std::string SOURCE_CODE = R"(struct VSInput +{ + float3 position : POSITION; + float2 texCoord : TEXCOORD0; + float4 color : COLOR0; +} + +struct VSOutput +{ + float4 position : SV_POSITION; + float2 texCoord : TEXCOORD0; + float4 color : COLOR0; +} + +ConstantBuffer viewProjection : register(b0, space0); + +[shader("vertex")] +VSOutput main_vs(VSInput input) +{ + VSOutput output; + output.position = mul(viewProjection, float4(input.position, 1.0)); + output.texCoord = input.texCoord; + output.color = input.color; + return output; +} + +Texture2D diffuseTexture: register(t0, space1); +SamplerState diffuseTexSampler : register(s1, space1); + +[shader("fragment")] +float4 main_fs(VSOutput input) : SV_TARGET +{ + float4 textureColor = diffuseTexture.Sample(diffuseTexSampler, input.texCoord); + return textureColor * input.color; +})"; + + +//! Test if the compiler can successfully compile Spir-V source code to GLSL 4.5, +bool compile_spirv_to_glsl_450_vs() +{ + Compiler compiler; + + std::vector stages = { ShaderStage::Vertex }; + auto spirVResult = compiler.compileSlangToSpirV( + SOURCE_CODE, + stages + ); + + SpirVCrossCompiler crossCompiler; + auto glslResult = crossCompiler.compile(spirVResult.getBinaryCode(), GlslProfile::GLSL_450, ShaderStage::Vertex); + + return glslResult.isSuccess() && glslResult.getSourceCode().find("main") != std::string::npos; +} + +//! Test if the compiler can successfully compile Spir-V source code to GLSL 4.5, +bool compile_spirv_to_glsl_450_fs() +{ + Compiler compiler; + + std::vector stages = { ShaderStage::Fragment }; + auto spirVResult = compiler.compileSlangToSpirV( + SOURCE_CODE, + stages + ); + + SpirVCrossCompiler crossCompiler; + auto glslResult = crossCompiler.compile(spirVResult.getBinaryCode(), GlslProfile::GLSL_450, ShaderStage::Fragment); + + return glslResult.isSuccess() && glslResult.getSourceCode().find("main") != std::string::npos; +} + +//! Test if the compiler can successfully compile Spir-V source code to GLSL 300 es +bool compile_spirv_to_glsl_300_es() +{ + Compiler compiler; + + std::vector stages = { ShaderStage::Vertex, ShaderStage::Fragment }; + std::vector entryPoints = { "main_vs", "main_fs" }; + auto spirVResult = compiler.compileSlangToSpirV( + SOURCE_CODE, + stages, + entryPoints + ); + + SpirVCrossCompiler crossCompiler; + auto glslVertexResult = crossCompiler.compile(spirVResult.getBinaryCode(), GlslProfile::GLES_300, ShaderStage::Vertex); + auto glslFragmentResult = crossCompiler.compile(spirVResult.getBinaryCode(), GlslProfile::GLES_300, ShaderStage::Fragment); + + return glslVertexResult.isSuccess() && glslVertexResult.getSourceCode().find("main") != std::string::npos && + glslFragmentResult.isSuccess() && glslFragmentResult.getSourceCode().find("main") != std::string::npos; +} + +TEST_CASE("compiler spirv to glsl tests", + "[compile_spirv_to_glsl_450_vs, compile_spirv_to_glsl_450_fs, compile_spirv_to_glsl_300_es_vs,compile_spirv_to_glsl_300_es_fs ]") +{ + REQUIRE(compile_spirv_to_glsl_450_vs()); + REQUIRE(compile_spirv_to_glsl_450_fs()); + REQUIRE(compile_spirv_to_glsl_300_es()); +} diff --git a/ShaderToolkitC/tests/spirv_cross_tests.cpp b/ShaderToolkitC/tests/spirv_cross_tests.cpp deleted file mode 100644 index efb68dc..0000000 --- a/ShaderToolkitC/tests/spirv_cross_tests.cpp +++ /dev/null @@ -1,37 +0,0 @@ -//#include -//#include -//#include "spirv-cross/spirv_cross_compiler.hpp" -//#include "slang/slang_session.hpp" -//#include -// -//using namespace ris_shader_toolkit; -// -//bool compile_spirv_to_glsl_300_es() -//{ -// ris_shader_toolkit::SlangSession slangSession; -// slangSession.initialize(); -// SlangCompileResult slangResult = slangSession.compileToSpirV("D:/Projects/ShaderToolkitSharp/ShaderToolkitC/test_files/sprite.slang", -// { ShaderStage::Vertex }, -// { "main_vs" } -// ); -// -// std::vector input; -// if (slangResult.isSuccess()) { -// // Copy slangResult source code to uint32_t vector -// const std::string& spirvBinary = slangResult.getSourceCode(); -// size_t size = spirvBinary.size(); -// input.resize(size / sizeof(uint32_t)); -// std::memcpy(input.data(), spirvBinary.data(), size); -// } -// -// ris_shader_toolkit::SpirVCrossCompiler compiler; -// ris_shader_toolkit::SpirVCrossCompileResult result = compiler.compile(input, GlslProfile::GLES_300); -// std::cout << "Glsl Source Code: " << result.getSourceCode() << std::endl; -// -// return result.isSuccess(); -//} -// -// -//TEST_CASE("spirv_cross tests", "[compile_spirv_to_glsl_300_es]") { -// REQUIRE(compile_spirv_to_glsl_300_es()); -//}