Skip to content

Commit

Permalink
C# API (#425)
Browse files Browse the repository at this point in the history
Fixes #314
  • Loading branch information
laurentlb authored Jun 1, 2024
1 parent ff1cc94 commit a00caa9
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 70 deletions.
5 changes: 3 additions & 2 deletions Checker/compression_test.fs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module CompressionTests

open ShaderMinifier
open System.Runtime.InteropServices
open System.IO
open System.Text
Expand Down Expand Up @@ -52,8 +53,8 @@ let compressionTest args filenames =
let minified =
use out = new StringWriter()
let files = [|for f in filenames -> f, File.ReadAllText("tests/real/" + f)|]
let shaders, exportedNames = ShaderMinifier.minify options files
ShaderMinifier.format options out shaders exportedNames
let minifier = Minifier(options, files)
minifier.Format(out)
out.ToString().ToCharArray()

let pointer = &&minified.[0]
Expand Down
24 changes: 15 additions & 9 deletions Checker/main.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ open System.IO

open Argu
open System.Text.RegularExpressions
open ShaderMinifier

type CliArguments =
| Update_Golden
Expand Down Expand Up @@ -135,8 +136,10 @@ let canBeCompiled lang stage content =
canBeCompiledByGlslang lang stage fullsrc && ((lang = "hlsl") || canBeCompiledByDriver stage fullsrc)

let doMinify options file content =
let arr = ShaderMinifier.minify options [|file, content|] |> fst |> Array.map (fun s -> s.code)
ShaderMinifier.print arr.[0]
let minifier = Minifier(options, [|file, content|])
use tw = new System.IO.StringWriter()
minifier.Format(tw)
tw.ToString()

let testMinifyAndCompile options lang (file: string) =
try
Expand Down Expand Up @@ -164,7 +167,7 @@ let testPerformance files =
let contents = files |> Array.map File.ReadAllText
let stopwatch = Stopwatch.StartNew()
for str in contents do
let options = Options.init([|"--format"; "text"; "--no-remove-unused"; "fake.frag"|])
let options = Options.init([|"--format"; "text"; "--no-remove-unused"|])
doMinify options "perf test" str |> ignore<string>
let time = stopwatch.Elapsed
printfn "%i files minified in %f seconds." files.Length time.TotalSeconds
Expand All @@ -177,29 +180,32 @@ let runCommand argv =
let cleanString (s: string) =
let s = s.Replace("\r\n", "\n").Trim()
versionRegex.Replace(s, "")
let options, filenames = Options.initFiles argv
let options, filenames = Minifier.ParseOptionsWithFiles(argv)
let expected =
try File.ReadAllText options.outputName |> cleanString
with _ when cliArgs.Contains(Update_Golden) -> ""
| _ -> reraise ()
let files = [|for f in filenames -> f, File.ReadAllText(f)|]
let shaders, exportedNames = ShaderMinifier.minify options files
let minifier = Minifier(options, files)
let result =
use out = new StringWriter()
ShaderMinifier.format options out shaders exportedNames
minifier.Format(out)
out.ToString() |> cleanString

let options = { options with outputFormat = Options.OutputFormat.IndentedText; exportKkpSymbolMaps = false}
for shader in shaders do
if filenames.Length = 1 then
let shader = minifier.GetShaders[0]
let resultindented =
use out = new StringWriter()
ShaderMinifier.format options out [|shader|] exportedNames
minifier.Format(out, options)
out.ToString() |> cleanString
let outdir = "tests/out/" + Regex.Replace(options.outputName, @"^tests/(.*)/[^/]*$", @"$1") + "/"
let split = Regex.Match(shader.mangledFilename, @"(^.*)_([^_]+)$").Groups
let name = split[1].Value
let ext = split[2].Value
Directory.CreateDirectory outdir |> ignore
File.WriteAllText(outdir + name + ".minind." + ext, resultindented + "\n")

if result = expected then
printfn "Success: %s" options.outputName
0
Expand Down Expand Up @@ -265,7 +271,7 @@ let main argv =
let realTests = Directory.GetFiles("tests/real", "*.frag")
for f in unitTests do
// tests with no #version default to 110
let options = Options.init([|"--format"; "text"; "--no-remove-unused"; "fake.frag"|])
let options = Options.init([|"--format"; "text"; "--no-remove-unused"|])
if not (testMinifyAndCompile options "110" f) then
failures <- failures + 1
testPerformance (Seq.concat [realTests; unitTests] |> Seq.toArray)
Expand Down
14 changes: 14 additions & 0 deletions Example/Example.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Shader Minifier Library.fsproj" />
</ItemGroup>

</Project>
18 changes: 18 additions & 0 deletions Example/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Example of use of the ShaderMinifier library from C#.
*/

using ShaderMinifier;

var shader = """
out vec4 fragColor;
void main()
{
fragColor = vec4(1., 1., 1., 1.);
}
""";

var file = Tuple.Create("filename.frag", shader);
var options = Minifier.ParseOptions(new[] { "--format", "text" });
var minifier = new Minifier(options, new[] { file });
minifier.Format(System.Console.Out);
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ shader is minified by hand by experienced demosceners, Shader Minifier is often
able to optimize it further. See this [2010
report](https://www.ctrl-alt-test.fr/2010/glsl-minifier-smaller-and-smaller/).

To be notified of new releases, use the watch feature of GitHub.
Shader Minifier is available:
* As an online website: https://ctrl-alt-test.fr/minifier/
* As a command-line tool (download from [Releases](https://github.com/laurentlb/shader-minifier/releases))
* As a .NET library (see [Example](https://github.com/laurentlb/shader-minifier/tree/master/Example))

Try the online version here: https://ctrl-alt-test.fr/minifier/
To be notified of new releases, use the watch feature of GitHub.

## Features

Expand Down
13 changes: 7 additions & 6 deletions SMBolero.Client/Main.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ open Bolero
open Bolero.Html
open Bolero.Remoting.Client
open Bolero.Templating.Client
open ShaderMinifier

/// Routing endpoints definition.
type Page =
Expand Down Expand Up @@ -48,15 +49,15 @@ type Message =
| ClearError

let minify flags content =
let options = Options.init flags
let shaders, exportedNames = ShaderMinifier.minify options [|"input", content|]
let options = Minifier.ParseOptions(flags)
let minifier = Minifier(options, [|"input", content|])
let out = new System.IO.StringWriter()
ShaderMinifier.format options out shaders exportedNames
minifier.Format(out)

let withLoc = new System.IO.StringWriter()
ShaderMinifier.formatWithLocations options withLoc shaders exportedNames
minifier.FormatWithLocations(withLoc)

out.ToString(), ShaderMinifier.getSize shaders, withLoc.ToString()
out.ToString(), minifier.GetSize, withLoc.ToString()

module API =
[<Microsoft.JSInterop.JSInvokableAttribute("minify")>]
Expand All @@ -74,7 +75,7 @@ let update (jsRuntime: Microsoft.JSInterop.IJSRuntime) message model =
if not model.flags.inlining then yield "--no-inlining"
if not model.flags.removeUnused then yield "--no-remove-unused"
if not model.flags.renaming then yield "--no-renaming"
yield! model.flags.other.Split(' ')
yield! model.flags.other.Split([|' '|], System.StringSplitOptions.RemoveEmptyEntries)
|]
printfn "Minify %s" (String.concat " " allFlags)
try
Expand Down
14 changes: 14 additions & 0 deletions Shader Minifier.sln
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "SMBolero.Client", "SMBolero
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "SMBolero.Server", "SMBolero.Server\SMBolero.Server.fsproj", "{C1A4652F-0F92-4530-B1C8-2AAC9C383650}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example", "Example\Example.csproj", "{1C6C7E5F-41CF-456E-AEB2-8D501D43D2C5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -84,6 +86,18 @@ Global
{C1A4652F-0F92-4530-B1C8-2AAC9C383650}.Release|x64.Build.0 = Release|Any CPU
{C1A4652F-0F92-4530-B1C8-2AAC9C383650}.Release|x86.ActiveCfg = Release|Any CPU
{C1A4652F-0F92-4530-B1C8-2AAC9C383650}.Release|x86.Build.0 = Release|Any CPU
{1C6C7E5F-41CF-456E-AEB2-8D501D43D2C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1C6C7E5F-41CF-456E-AEB2-8D501D43D2C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1C6C7E5F-41CF-456E-AEB2-8D501D43D2C5}.Debug|x64.ActiveCfg = Debug|Any CPU
{1C6C7E5F-41CF-456E-AEB2-8D501D43D2C5}.Debug|x64.Build.0 = Debug|Any CPU
{1C6C7E5F-41CF-456E-AEB2-8D501D43D2C5}.Debug|x86.ActiveCfg = Debug|Any CPU
{1C6C7E5F-41CF-456E-AEB2-8D501D43D2C5}.Debug|x86.Build.0 = Debug|Any CPU
{1C6C7E5F-41CF-456E-AEB2-8D501D43D2C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1C6C7E5F-41CF-456E-AEB2-8D501D43D2C5}.Release|Any CPU.Build.0 = Release|Any CPU
{1C6C7E5F-41CF-456E-AEB2-8D501D43D2C5}.Release|x64.ActiveCfg = Release|Any CPU
{1C6C7E5F-41CF-456E-AEB2-8D501D43D2C5}.Release|x64.Build.0 = Release|Any CPU
{1C6C7E5F-41CF-456E-AEB2-8D501D43D2C5}.Release|x86.ActiveCfg = Release|Any CPU
{1C6C7E5F-41CF-456E-AEB2-8D501D43D2C5}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
86 changes: 48 additions & 38 deletions src/api.fs
Original file line number Diff line number Diff line change
@@ -1,41 +1,51 @@
module ShaderMinifier
namespace ShaderMinifier

open System.IO

let getSize (shaders: Ast.Shader[]) =
shaders |> Array.sumBy (fun s -> Printer.print s.code |> String.length)

let minify (options: Options.Options) (files: (string*string)[]) =
// like printfn when verbose option is set
let vprintf fmt = fprintf (if options.verbose then stdout else TextWriter.Null) fmt

let printSize (shaders: Ast.Shader[]) =
if options.verbose then
let length = getSize shaders
vprintf "Shader size is: %d\n" length

let names = String.concat "," [for n, c in files -> $"'{n}' ({c.Length}b)"]
options.trace $"----- minifying {names}"
vprintf "Input file size is: %d\n" (files |> Array.sumBy (fun (_, s) -> s.Length))

let parseAndRewrite (filename, content) =
let shader = Parse.runParser options filename content
let code =
if shader.reorderFunctions then
Rewriter.reorderFunctions options shader.code
else shader.code
{ shader with code = Rewriter.simplify options code }

let shaders = Array.Parallel.map parseAndRewrite files
vprintf "Rewrite tricks applied. "; printSize shaders

if options.noRenaming then
shaders, []
else
let exportedNames = Renamer.rename options shaders
vprintf "Identifiers renamed. "; printSize shaders
shaders, exportedNames

let format = Formatter.print
let formatWithLocations = Formatter.printWithLocations
let print = Printer.print
type Minifier(options, files) =
let getSize (shaders: Ast.Shader[]) =
shaders |> Seq.sumBy (fun s -> Printer.print s.code |> String.length)

let minify (options: Options.Options) (files: (string*string)[]) =
// like printfn when verbose option is set
let vprintf fmt = fprintf (if options.verbose then stdout else TextWriter.Null) fmt

let printSize (shaders: Ast.Shader[]) =
if options.verbose then
let length = getSize shaders
vprintf "Shader size is: %d\n" length

let names = String.concat "," [for n, c in files -> $"'{n}' ({c.Length}b)"]
options.trace $"----- minifying {names}"
vprintf "Input file size is: %d\n" (files |> Array.sumBy (fun (_, s) -> s.Length))

let parseAndRewrite (filename, content) =
let shader = Parse.runParser options filename content
let code =
if shader.reorderFunctions then
Rewriter.reorderFunctions options shader.code
else shader.code
{ shader with code = Rewriter.simplify options code }

let shaders = Array.Parallel.map parseAndRewrite files
vprintf "Rewrite tricks applied. "; printSize shaders

if options.noRenaming then
shaders, [||]
else
let exportedNames = Renamer.rename options (Seq.toArray shaders) |> List.toArray
vprintf "Identifiers renamed. "; printSize shaders
shaders, exportedNames

let shaders, exportedNames = minify options files

static member ParseOptions(flags) = Options.init flags
static member ParseOptionsWithFiles(flags) = Options.initFiles flags

member _.GetSize = getSize shaders
member _.GetShaders = shaders

member _.Format(writer) = Formatter.print options writer shaders exportedNames
member _.Format(writer, options) =
Formatter.print options writer shaders exportedNames
member _.FormatWithLocations(writer) = Formatter.printWithLocations options writer shaders exportedNames
10 changes: 5 additions & 5 deletions src/formatter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type private Impl(options: Options.Options, withLocations) =
fprintfn out "#ifndef %s" macroName
fprintfn out "# define %s" macroName

for value: Ast.ExportedName in List.sort exportedNames do
for value: Ast.ExportedName in Seq.sort exportedNames do
fprintfn out "# define %s_%s \"%s\"" ((formatPrefix value.prefix).ToUpper()) value.name value.newName

fprintfn out ""
Expand All @@ -70,7 +70,7 @@ type private Impl(options: Options.Options, withLocations) =
fprintfn out "#ifndef SHADER_MINIFIER_HEADER"
fprintfn out "# define SHADER_MINIFIER_HEADER"

for value: Ast.ExportedName in List.sort exportedNames do
for value: Ast.ExportedName in Seq.sort exportedNames do
fprintfn out "# define %s_%s \"%s\"" ((formatPrefix value.prefix).ToUpper()) value.name value.newName

fprintfn out "#endif"
Expand Down Expand Up @@ -106,7 +106,7 @@ type private Impl(options: Options.Options, withLocations) =
let printJSHeader out (shaders: Ast.Shader[]) exportedNames =
fprintfn out "// Generated with Shader Minifier %s (https://github.com/laurentlb/Shader_Minifier/)" Options.version

for value: Ast.ExportedName in List.sort exportedNames do
for value: Ast.ExportedName in Seq.sort exportedNames do
fprintfn out "var %s_%s = \"%s\"" (formatPrefix value.prefix) (value.name.ToUpper()) value.newName

fprintfn out ""
Expand All @@ -120,7 +120,7 @@ type private Impl(options: Options.Options, withLocations) =

fprintfn out "; Generated with Shader Minifier %s (https://github.com/laurentlb/Shader_Minifier/)" Options.version

for value: Ast.ExportedName in List.sort exportedNames do
for value: Ast.ExportedName in Seq.sort exportedNames do
fprintfn out "_%s_%s: db '%s', 0" (formatPrefix value.prefix) (value.name.ToUpper()) value.newName

fprintfn out ""
Expand All @@ -138,7 +138,7 @@ type private Impl(options: Options.Options, withLocations) =
let printRustHeader out (shaders: Ast.Shader[]) exportedNames =
fprintfn out "// Generated with Shader Minifier %s (https://github.com/laurentlb/Shader_Minifier/)" Options.version

for value: Ast.ExportedName in List.sort exportedNames do
for value: Ast.ExportedName in Seq.sort exportedNames do
fprintfn out "pub const %s_%s: &'static [u8] = b\"%s\\0\";" ((formatPrefix value.prefix).ToUpper()) (value.name.ToUpper()) value.newName

for shader in shaders do
Expand Down
9 changes: 5 additions & 4 deletions src/main.fs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module Main

open ShaderMinifier
open System.IO

let readFile file =
Expand All @@ -8,21 +9,21 @@ let readFile file =
else new StreamReader(file)
stream.ReadToEnd()

let minifyFiles (options: Options.Options) filenames =
let minifyFiles (options: Options.Options) filenames out =
let files = [|
for f in filenames do
let content = readFile f
let filename = if f = "" then "stdin" else f
yield filename, content |]
ShaderMinifier.minify options files
let minifier = Minifier(options, files)
minifier.Format(out)

let run (options: Options.Options) filenames =
use out =
if Options.debugMode || options.outputName = "" || options.outputName = "-" then stdout
else new StreamWriter(options.outputName) :> TextWriter
try
let shaders, exportedNames = minifyFiles options filenames
ShaderMinifier.format options out shaders exportedNames
minifyFiles options filenames out
0
with exn ->
printfn "%s" (exn.ToString())
Expand Down
7 changes: 6 additions & 1 deletion src/options.fs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,12 @@ let private initPrivate argv =

let flagsHelp = lazy (argParser.Value.PrintUsage(message = helpTextMessage))

let init argv = initPrivate argv |> fst
let init argv =
let options, filenames = initPrivate argv
if filenames.Length > 0 then
failwithf "Unexpected arguments: %A" (String.concat " " filenames)
options

let initFiles argv =
let options, filenames = initPrivate argv
if filenames.Length = 0 then
Expand Down
Loading

0 comments on commit a00caa9

Please sign in to comment.