diff --git a/.gitignore b/.gitignore index 5344bd22..9b5846d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,10 @@ +################# +## Checker +################# + +Checker/.out.spv +tests/out/ + ################# ## Eclipse ################# diff --git a/Checker/checker-linux.fsproj b/Checker/checker-linux.fsproj new file mode 100644 index 00000000..b8bc3d70 --- /dev/null +++ b/Checker/checker-linux.fsproj @@ -0,0 +1,32 @@ + + + + Exe + net48 + + + + + + + + + ..\lib\Argu.dll + + + ..\lib\FSharp.Core.dll + + + ..\lib\OpenTK.dll + + + Shader Minifier Library + True + + + + + $(MSBuildProjectDirectory)/.. + + + diff --git a/Checker/compression_test.fs b/Checker/compression_test.fs index bfc22d56..1785a153 100644 --- a/Checker/compression_test.fs +++ b/Checker/compression_test.fs @@ -1,5 +1,6 @@ module CompressionTests +open ShaderMinifier open System.Runtime.InteropServices open System.IO open System.Text @@ -47,16 +48,17 @@ let log fmt = Printf.ksprintf logger fmt -let compressionTest args files = - Options.init(args) +let compressionTest args filenames = + let options = Options.init(args) let minified = use out = new StringWriter() - let shaders, exportedNames = ShaderMinifier.minifyFiles [|for f in files -> "tests/real/" + f|] - Formatter.print out shaders exportedNames Options.Text + let files = [|for f in filenames -> f, File.ReadAllText("tests/real/" + f)|] + let minifier = Minifier(options, files) + minifier.Format(out) out.ToString().ToCharArray() let pointer = &&minified.[0] - log "%-40s " (match files with [f] -> f | f::_ -> f + "..." | [] -> "?") + log "%-40s " (match filenames with [f] -> f | f::_ -> f + " (and others)" | [] -> "?") log "%5d " minified.Length let compressedSize = Crinkler.ApproximateModels4k(pointer, minified.Length) log "=> %8.3f\n" compressedSize diff --git a/Checker/glslang.exe b/Checker/glslang.exe new file mode 100644 index 00000000..15af68a1 Binary files /dev/null and b/Checker/glslang.exe differ diff --git a/Checker/main.fs b/Checker/main.fs index 541d30b6..b899d915 100644 --- a/Checker/main.fs +++ b/Checker/main.fs @@ -1,61 +1,157 @@ open OpenTK.Graphics.OpenGL -open Options.Globals open System open System.Diagnostics open System.IO open Argu open System.Text.RegularExpressions +open ShaderMinifier type CliArguments = | Update_Golden - | Skip_GLSL_Compile + | Compile_Golden + | Skip_Glslang_Compile + | GLSL_Driver_Compile | Skip_Performance_Tests interface IArgParserTemplate with member s.Usage = match s with - | Update_Golden _ -> "Update the golden tests" - | Skip_GLSL_Compile _ -> "Skip the GLSL compilation of shaders" - | Skip_Performance_Tests _ -> "Skip the tests of performance" + | Update_Golden -> "Update the golden tests" + | Compile_Golden -> "Always test compilation of the golden tests" + | Skip_Glslang_Compile -> "Skip testing compilation of shaders with glslang" + | GLSL_Driver_Compile -> "Test GLSL compilation of shaders agaist current OpenGL driver" + | Skip_Performance_Tests -> "Skip the tests of performance" let cliArgs = ArgumentParser.Create().ParseCommandLine() //let cliArgs = ArgumentParser.Create().ParseCommandLine([|"--update-golden"|]) let initOpenTK () = // OpenTK requires a GameWindow - if not (cliArgs.Contains(Skip_GLSL_Compile)) then + if cliArgs.Contains(GLSL_Driver_Compile) then new OpenTK.GameWindow() |> ignore -// Return true if the file can be compiled as a GLSL shader. -let canBeCompiled content = - if cliArgs.Contains(Skip_GLSL_Compile) then +// Return true if the file can be compiled as a GLSL shader by the current OpenGL driver +let canBeCompiledByDriver stage (content: string) = + if not (cliArgs.Contains(GLSL_Driver_Compile)) then true else - let fragmentShader = GL.CreateShader(ShaderType.FragmentShader) - GL.ShaderSource(fragmentShader, content) - GL.CompileShader(fragmentShader) - let info = GL.GetShaderInfoLog(fragmentShader) - GL.DeleteShader(fragmentShader) - if info = "" then - true + let fragmentShader = GL.CreateShader( + match stage with + | "vert" -> ShaderType.VertexShader + | "geom" -> ShaderType.GeometryShader + | "frag" -> ShaderType.FragmentShader + | "comp" -> ShaderType.ComputeShader + | _ -> ShaderType.FragmentShader + ) + GL.ShaderSource(fragmentShader, content) + GL.CompileShader(fragmentShader) + let driverinfo = sprintf "%s / %s / %s / %s" (GL.GetString(StringName.Vendor)) (GL.GetString(StringName.Renderer)) (GL.GetString(StringName.Version)) (GL.GetString(StringName.ShadingLanguageVersion)) + let mutable status = 0 + GL.GetShader(fragmentShader, ShaderParameter.CompileStatus, &status) + let info = GL.GetShaderInfoLog(fragmentShader) + let info = if info.EndsWith("\n") then info else info + "\n" + GL.DeleteShader(fragmentShader) + if status = 1 then + // getting warnings is not reliable: the shader may be cached but the warnings are not + //if info.Length > 1 then + // printf "%s\nDriver compilation warnings: %s" driverinfo info + true + else + printf "%s\nDriver compilation failed:\n%s" driverinfo info + false + +let canBeCompiledByGlslang lang stage (content: string) = + if cliArgs.Contains(Skip_Glslang_Compile) then + true + else + let mutable tgtopt = "" + let mutable versopt = "-d" + if lang = "hlsl" then + versopt <- "-D --hlsl-dx9-compatible" + tgtopt <- "--target-env spirv1.6 -V" + else + // glslang bug workarounds: + // after 330, spir-v targeting must be enabled or else not all OpenGL GLSL features are supported + // for 1xx languages, spir-v targeting must be disabled + if Regex.Match(content, @"^\s*#version\s+[34]", RegexOptions.Multiline).Success then + tgtopt <- "--target-env opengl --amb --aml -o Checker/.out.spv" + // 300es is not supported, override it to 310es when detected + if Regex.Match(content, @"^\s*#version\s+300\s+es", RegexOptions.Multiline).Success then + versopt <- "--glsl-version 310es" + let si = ProcessStartInfo() + si.FileName <- + if Environment.OSVersion.Platform = PlatformID.Win32NT then + "Checker/glslang.exe" else - printfn "compilation failed: %s" info - false + // must be installed externally + "glslang" + si.Arguments <- sprintf "%s %s --stdin -S %s" versopt tgtopt stage + si.UseShellExecute <- false + si.RedirectStandardInput <- true + si.RedirectStandardOutput <- true + si.RedirectStandardError <- true + let proc = Process.Start(si) + proc.StandardInput.Write(content) + proc.StandardInput.Close() + let info = (proc.StandardOutput.ReadToEnd()) + (proc.StandardError.ReadToEnd()) + proc.WaitForExit() + if proc.ExitCode = 0 then + true + else + printf "glslang compilation failed:\n%s\n" info + false + +// Note that "100" is ESSL, "#version 100 es" is not valid! +let shaderlangs = set ("110 120 130 140 150 330 400 410 420 430 440 450 460 100 300es hlsl shadertoy".Split ' ') -let doMinify content = - let arr = ShaderMinifier.minify [|"input", content|] |> fst |> Array.map (fun s -> s.code) - Printer.printText arr.[0] +let canBeCompiled lang stage content = + if not (shaderlangs.Contains(lang)) then raise (ArgumentException(sprintf "unknown lang %s" lang)) + let mutable lang = lang + let mutable stage = stage + let mutable fullsrc = content + if lang <> "hlsl" then + let mutable lineadj = 0 + let setline num file = + // "#line line srcnum" sets line numbering in error messages + sprintf "\n#line %d %d\n" (num + lineadj) file + if lang = "shadertoy" then + let header = File.ReadAllText "tests/shadertoy.h.glsl" + fullsrc <- header + (setline 1 1) + fullsrc + lang <- "300es" + stage <- "frag" + let mutable verstr = Regex.Replace(lang, @"(\d+)(\w*)", @"$1 $2") // "450" -> "450", "300es" -> "300 es" + let versmatch = Regex.Match(fullsrc, @"^\s*#version\s+(\d.*)", RegexOptions.Multiline) + if versmatch.Success then + verstr <- versmatch.Groups[1].Value + // #line directive is off-by-one before GLSL 330 + if Regex.Match(verstr, @"1[1-5]").Success then + lineadj <- -1 + // If there is no "#version" line, add one + if not versmatch.Success then + fullsrc <- "#version " + verstr + (setline 1 1) + fullsrc + // If there is no "void main", add one (at the end, to not disturb any #version line) + if not (Regex.Match(" " + fullsrc, @"(?s)[^\w]void\s+main\s*(\s*)").Success) then + fullsrc <- fullsrc + (setline 1 2) + "void main(){}\n" + canBeCompiledByGlslang lang stage fullsrc && ((lang = "hlsl") || canBeCompiledByDriver stage fullsrc) -let testMinifyAndCompile (file: string) = +let doMinify options file content = + let minifier = Minifier(options, [|file, content|]) + use tw = new System.IO.StringWriter() + minifier.Format(tw) + tw.ToString() + +let testMinifyAndCompile options lang (file: string) = try let content = File.ReadAllText file - if not (canBeCompiled content) then + let stage = Regex.Match(file, @"[^\.]+$").Value + let compile = not (Regex.Match(content, @"^//NOCOMPILE").Success) + if compile && not (canBeCompiled lang stage content) then printfn "Invalid input file '%s'" file false else - let minified = doMinify content + "\n" - if not (canBeCompiled minified) then + let minified = doMinify options file content + "\n" + if compile && not (canBeCompiled lang stage minified) then printfn "Minification broke the file '%s'" file printfn "%s" minified false @@ -71,28 +167,45 @@ let testPerformance files = let contents = files |> Array.map File.ReadAllText let stopwatch = Stopwatch.StartNew() for str in contents do - doMinify str |> ignore + let options = Options.init([|"--format"; "text"; "--no-remove-unused"|]) + doMinify options "perf test" str |> ignore let time = stopwatch.Elapsed printfn "%i files minified in %f seconds." files.Length time.TotalSeconds // Generated files may contain the Shader Minifier version. // Ignore version changes in the tests. -let versionRegex = new Regex(@"\bShader Minifier \d(\.\d+)+"); +let versionRegex = new Regex(@"\bShader Minifier \d(\.\d+)+") let runCommand argv = let cleanString (s: string) = let s = s.Replace("\r\n", "\n").Trim() versionRegex.Replace(s, "") - Options.init 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 minifier = Minifier(options, files) let result = use out = new StringWriter() - let shaders, exportedNames = ShaderMinifier.minifyFiles options.filenames - Formatter.print out shaders exportedNames options.outputFormat + minifier.Format(out) out.ToString() |> cleanString + + let options = { options with outputFormat = Options.OutputFormat.IndentedText; exportKkpSymbolMaps = false} + if filenames.Length = 1 then + let shader = minifier.GetShaders[0] + let resultindented = + use out = new StringWriter() + 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 @@ -108,12 +221,41 @@ let runCommand argv = let testGolden () = let commands = File.ReadAllLines "tests/commands.txt" |> Array.choose (fun line -> let line = line.Trim() - if line.Length = 0 || line.[0] = '#' then + if line.Length = 0 || line.[0] = '#' then None else Some (line.Split([|' '|])) ) - commands |> Array.sumBy runCommand + commands |> Array.Parallel.map runCommand |> Array.sum + +let runCompiler (argv: string array) = + if (cliArgs.Contains(Skip_Glslang_Compile)) && (not (cliArgs.Contains(GLSL_Driver_Compile))) then + 0 + else + let lang = argv[0] + let stage = argv[1] + let filename = argv[2] + let content = File.ReadAllText filename + let info = sprintf "%s (%s %s)" filename lang stage + if canBeCompiled lang stage content then + printfn "Compiled: %s" info + 0 + else + printfn "Compile failure: %s" info + 1 + +let testCompiled () = + if not (cliArgs.Contains(Update_Golden) || cliArgs.Contains(Compile_Golden)) then + 0 + else + let commands = File.ReadAllLines "tests/compile.txt" |> Array.choose (fun line -> + let line = line.Trim() + if line.Length = 0 || line.[0] = '#' then + None + else + Some (line.Split([|' '|])) + ) + commands |> Array.Parallel.map runCompiler |> Array.sum [] let main argv = @@ -121,18 +263,22 @@ let main argv = // CompressionTests.run () initOpenTK() - let mutable failures = testGolden() - Options.init([|"--format"; "text"; "fake.frag"|]) - let unitTests = Directory.GetFiles("tests/unit", "*.frag") - let realTests = Directory.GetFiles("tests/real", "*.frag"); + let mutable failures = 0 + failures <- failures + testGolden() + failures <- failures + testCompiled() + let srcfilter e = Regex.Match(e, @"\.(vert|geom|frag|comp)$").Success + let unitTests = Directory.GetFiles("tests/unit", "*") |> Array.filter srcfilter + let realTests = Directory.GetFiles("tests/real", "*.frag") for f in unitTests do - if not (testMinifyAndCompile f) then + // tests with no #version default to 110 + 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) if failures = 0 then printfn "All good." else printfn "%d failures." failures - + //System.Console.ReadLine() |> ignore if failures = 0 then 0 else 1 diff --git a/Example/Example.csproj b/Example/Example.csproj new file mode 100644 index 00000000..2a1316b1 --- /dev/null +++ b/Example/Example.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/Example/Program.cs b/Example/Program.cs new file mode 100644 index 00000000..aeb15274 --- /dev/null +++ b/Example/Program.cs @@ -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); diff --git a/README.md b/README.md index ec3d72d1..9f359e0b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Shader Minifier -[![Build status](https://ci.appveyor.com/api/projects/status/chwlpnssgd5kdl4x/branch/master?svg=true)](https://ci.appveyor.com/project/laurentlb/shader-minifier/branch/master) +[![Build status](https://ci.appveyor.com/api/projects/status/9sekrxuiway58su3?svg=true)](https://ci.appveyor.com/project/laurentlb/shader-minifier-r5gmt) Shader Minifier is a tool that minifies and obfuscates shader code (GLSL and HLSL) without affecting its behaviour. It is also suitable for reducing the size @@ -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 @@ -100,7 +103,7 @@ USAGE: Shader Minifier [--help] [-o ] [-v] [--hlsl] [--preserve-all-globals] [--no-inlining] [--aggressive-inlining] [--no-renaming] [--no-renaming-list ] [--no-sequence] - [--smoothstep] [--no-remove-unused] + [--no-remove-unused] [--move-declarations] [...] FILENAMES: @@ -134,10 +137,11 @@ OPTIONS: --no-renaming-list Comma-separated list of functions to preserve --no-sequence Do not use the comma operator trick - --smoothstep Use IQ's smoothstep trick --no-remove-unused Do not remove unused code --move-declarations Move declarations to group them --preprocess Evaluate some of the file preprocessor directives + --export-kkp-symbol-maps + Export kkpView symbol maps --help display this list of options. ``` @@ -278,6 +282,12 @@ the output. If two functions have a different number of arguments, they may have the same name in the output. This reduces the number of identifiers used by the shader and make it more compression friendly. +### kkpView symbol maps + +Shader Minifier can export symbol files that map the minified code back to names +from the original source code. This lets you visualize shader size using +https://github.com/ConspiracyHu/rekkrunchy-with-analytics + ### Shader Minifier performance On my machine, it takes around 1s to minify a shader. A lot of that time comes @@ -305,12 +315,14 @@ them all at the same time by listing them all on the command-line. - [Automatic inlining](TRANSFORMATIONS.md#Automatic-inlining) - [Aggressive inlining](TRANSFORMATIONS.md#Aggressive-inlining) - [Explicit inlining](TRANSFORMATIONS.md#Explicit-inlining) +- [Reassignment merging](TRANSFORMATIONS.md#Reassignment-merging) +- [Variable reuse](TRANSFORMATIONS.md#Variable-reuse) - [Vector constructors](TRANSFORMATIONS.md#Vector-constructors) - [Function predeclarations](TRANSFORMATIONS.md#Function-predeclarations) - [Unused local variables](TRANSFORMATIONS.md#Unused-local-variables) - [Dead code removal](TRANSFORMATIONS.md#Dead-code-removal) - [Unused function removal](TRANSFORMATIONS.md#Unused-function-removal) -- [Smoothstep transformation](TRANSFORMATIONS.md#Smoothstep-transformation) +- [Other functions](TRANSFORMATIONS.md#Other-functions) - [Renaming](TRANSFORMATIONS.md#Renaming) diff --git a/SMBolero.Client/Main.fs b/SMBolero.Client/Main.fs index c2e9f943..826ee787 100644 --- a/SMBolero.Client/Main.fs +++ b/SMBolero.Client/Main.fs @@ -5,20 +5,29 @@ open Bolero open Bolero.Html open Bolero.Remoting.Client open Bolero.Templating.Client +open ShaderMinifier /// Routing endpoints definition. type Page = | [] Home | [] FlagPage - | [] Help + | [] About + +type Flags = { + mutable format: string + mutable inlining: bool + mutable removeUnused: bool + mutable renaming: bool + mutable other: string +} /// The Elmish application's model. type Model = { page: Page shaderInput: string - shaderOutput: string - flags: string + shaderSize: int + flags: Flags error: string option } @@ -26,8 +35,8 @@ let initModel = { page = Home shaderInput = "out vec4 fragColor;\nvoid main() {\n fragColor = vec4(1.,1.,1.,1.);\n}" - shaderOutput = "" - flags = "--format text" + shaderSize = 0 + flags = {Flags.inlining=true; removeUnused=true; renaming=true; other=""; format="text"} error = None } @@ -36,33 +45,50 @@ type Message = | SetPage of Page | Minify | SetShader of string - | SetFlags of string | Error of exn | ClearError let minify flags content = - try - Options.init flags - let shaders, exportedNames = ShaderMinifier.minify [|"input", content|] - let out = new System.IO.StringWriter() - Formatter.print out shaders exportedNames Options.Globals.options.outputFormat - out.ToString() - with - | e -> e.Message - -let update message model = + let options = Minifier.ParseOptions(flags) + let minifier = Minifier(options, [|"input", content|]) + let out = new System.IO.StringWriter() + minifier.Format(out) + + let withLoc = new System.IO.StringWriter() + minifier.FormatWithLocations(withLoc) + + out.ToString(), minifier.GetSize, withLoc.ToString() + +module API = + [] + let minify flags content = + let result, _, _ = minify flags content + result + +let update (jsRuntime: Microsoft.JSInterop.IJSRuntime) message model = match message with | SetPage page -> { model with page = page }, Cmd.none | Minify -> - printfn "Minify %s" model.flags - let out = minify (model.flags.Split(' ')) model.shaderInput - { model with shaderOutput = out }, Cmd.none + let allFlags = [| + yield! ["--format"; model.flags.format] + 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([|' '|], System.StringSplitOptions.RemoveEmptyEntries) + |] + printfn "Minify %s" (String.concat " " allFlags) + try + let out, size, withLoc = minify allFlags model.shaderInput + jsRuntime.InvokeAsync("updateShader", [|model.shaderInput; withLoc|]) |> ignore + { model with shaderSize = size }, Cmd.none + with + | e -> + printfn "Error: %s" e.Message + jsRuntime.InvokeAsync("updateShader", [|model.shaderInput; e.Message|]) |> ignore + { model with shaderSize = 0 }, Cmd.none | SetShader value -> { model with shaderInput = value }, Cmd.none - | SetFlags value -> - { model with flags = value }, Cmd.none - | Error exn -> { model with error = Some exn.Message }, Cmd.none | ClearError -> @@ -86,8 +112,12 @@ let homePage model dispatch = Main.Home() .Minify(fun _ -> dispatch Minify) .ShaderInput(model.shaderInput, fun v -> dispatch (SetShader v)) - .ShaderOutput(model.shaderOutput) - .Flags(model.flags, fun v -> dispatch (SetFlags v)) + .ShaderSize(if model.shaderSize = 0 then "" else $"generated, size: {model.shaderSize}") + .Format(model.flags.format, fun v -> model.flags.format <- v) + .Inlining(model.flags.inlining, fun v -> model.flags.inlining <- v) + .RemoveUnused(model.flags.removeUnused, fun v -> model.flags.removeUnused <- v) + .Renaming(model.flags.renaming, fun v -> model.flags.renaming <- v) + .OtherFlags(model.flags.other, fun v -> model.flags.other <- v) .Elt() let menuItem (model: Model) (page: Page) (text: string) = @@ -101,12 +131,12 @@ let view model dispatch = .Menu(concat { menuItem model Home "Minifier" menuItem model FlagPage "Flags" - menuItem model Help "Help" + menuItem model About "About" }) .Body( cond model.page <| function | Home -> homePage model dispatch - | Help -> aboutPage model dispatch + | About -> aboutPage model dispatch | FlagPage -> flagPage model dispatch ) .Error( @@ -124,7 +154,7 @@ type MyApp() = inherit ProgramComponent() override this.Program = - Program.mkProgram (fun _ -> initModel, Cmd.ofMsg (Message.SetPage Home)) update view + Program.mkProgram (fun _ -> initModel, Cmd.ofMsg (Message.SetPage Home)) (update this.JSRuntime) view |> Program.withRouter router #if DEBUG |> Program.withHotReload diff --git a/SMBolero.Client/wwwroot/css/index.css b/SMBolero.Client/wwwroot/css/index.css index 346ef354..f28a0e2b 100644 --- a/SMBolero.Client/wwwroot/css/index.css +++ b/SMBolero.Client/wwwroot/css/index.css @@ -1,7 +1,11 @@ :root { - --bg-main: #f1b99c; - --bg-light: #ffece3; - --border: #971f1f; + --bg-main: #101020; + --bg-light: #202040; + --border: #404080; + --text-color: #f0f0f0; + --alt-text-color: #aac; + scrollbar-width: thin; + scrollbar-color: var(--alt-text-color) transparent; } body { @@ -9,7 +13,7 @@ body { font-size: 1em; margin: 0px; margin-bottom: 10px; - color: #333; + color: var(--text-color); background-color: var(--bg-main); } @@ -17,9 +21,10 @@ body { width: 100%; } + .header { background-color: black; - color: var(--bg-light); + color: var(--alt-text-color); padding-left: 188px; padding-right: 0px; line-height: 1.6em; @@ -35,15 +40,12 @@ body { .left { font-size: 1em; width: 164px; - background: var(--bg-light) repeat-x top; - border-bottom: solid 3px black; - border-right: solid 3px black; - border-left: solid 1px black; - border-top: none; + background-color: var(--bg-light); + border: solid 1px #000; position: absolute; - padding-top: 6px; top: 0px; left: 10px; + color: var(--alt-text-color); } .center h1 { @@ -65,10 +67,6 @@ body { letter-spacing: 2px; } -.center form div { - margin-bottom: 5px; -} - .center h3 { font-size: 1.1em; margin-top: 1em; @@ -80,41 +78,31 @@ body { font-weight: bold; } -h5 { - font-size: 1em; +label { + min-width: 150px; + display: inline-block; } -h6 { - font-size: 1em; +summary { + cursor: pointer; } -label { - display: inline-block; - min-width: 150px; - font-variant: small-caps; - text-align: right; +.center { + margin: 10px 10px 10px 188px; } -.center big { - margin-top: 8px; +details ul { + padding-left: 1em; + list-style: none; } -.center { - margin-top: 10px; - margin-left: 188px; - margin-right: 10px; - margin-bottom: 10px; +details li { + display: flex; } #content { - background-color: white; - border-left: solid 1px black; - border-top: solid 1px black; - border-right: solid 3px black; - border-bottom: solid 3px black; - max-width: 800px; + max-width: 1500px; margin: 0px auto; - padding: 10px 20px; } #menu ul, #menu li { @@ -126,100 +114,58 @@ label { #menu a { display: block; margin: 0; - margin-left: 4px; - /* border-left: 4px transparent solid; */ - border-bottom: 1px dashed #A0C0F0; text-decoration: none; - color: #333; - padding-left: 5px; + color: var(--alt-text-color); + padding: 4px; } - #menu a:hover { - margin-left: 0px; - border-left: 4px solid #002299; - border-bottom: 1px solid #3388AA; - background-color: var(--bg-main); - color: black; - } - -.field legend:hover { - color: white; - background-color: var(--border); - border: solid 1px #225; +#menu a:hover { + background-color: var(--bg-main); } .center a { - color: #338; + color: #88f; text-decoration: none; font-weight: bold; } .center a:visited { - color: #068; text-decoration: none; font-weight: bold; } .center a:hover { - color: black; text-decoration: underline; font-weight: bold; } -.center .field a:hover { - text-decoration: none; -} - -.center p { - margin-right: 10px; - margin-left: 10px; -} - -.center .item, .center .block, fieldset { - margin-bottom: 20px; - margin-left: 20px; - margin-right: 20px; - background-color: white; - padding: 4px; - border: solid 1px var(--border); - display: block; -} - -.center .item a { - background-color: #AADDFF; +button, textarea, input, select { + background-color: #1a1a2a; + border: 1px solid var(--border); + color: var(--text-color); + font-size: 1em; } -.center .item a:after { - content: ':'; +button { + cursor: pointer; + background-color: #404080; + font-variant: small-caps; + font-size: 1em; } - textarea { box-sizing: border-box; width: 100%; + padding: 4px; + scrollbar-width: thin; } -#display { - width: 50em; - background-color: black; - display: block; - color: white; - font-family: monospace; - font-size: 11px; - border: 1px solid white; -} - -.code { - font-size: 11px; - white-space: pre; - font-family: monospace; - border-left: 3px double black; - margin: 5px; - padding-left: 5px; +code { + color: #ccf; + padding: 8px; } .center pre { - font-size: 11px; white-space: pre; font-family: monospace; border-left: solid 3px #666; @@ -228,68 +174,90 @@ textarea { padding-left: 8px; } -.box { - border: 2px black solid; - font-family: Verdana, Arial, Helvetica, sans-serif; - font-size: 0.8em; - padding: 6px; - background-color: #EEEEEE; - color: black; - font-variant: normal; +#minify-btn { + margin: 8px 0; + padding: 8px; + float: right; } -.box h4 { - font-family: Verdana, Arial, Helvetica, sans-serif; - font-size: 1em; - border: 0px black; - font-variant: normal; - color: white; - background-color: #AAAAAA; +.output-header { + font-size: 0.8em; + color: var(--alt-text-color); + width: 49.5%; text-align: center; } -.box2 { - border: 1px black solid; - font-family: Verdana, Arial, Helvetica, sans-serif; - font-size: 0.8em; - padding: 4px; - background-color: #EEEEEE; - color: black; - font-variant: normal; + +.clickable { + cursor: pointer; } -.center ul { - list-style-type: square; +/* Style for the source/minified columns. */ + +.comment { + color: #888; } -legend { - font-weight: bold; - border: solid 1px var(--border); +.output { + width: 100%; + display: flex; + flex-wrap: wrap; + place-content: space-between; + position: relative; } -.clickable { +.source, .minified { + padding: 3em 0em 3em 1em; + font-size: 1.2em; + font-family: monospace; + white-space: pre-wrap; + + line-break: anywhere; + box-sizing: border-box; + width: 49.5%; + + overflow-y: scroll; + scrollbar-width: thin; + max-height: 100vh; + background-color: rgb(26, 26, 42); + border: 1px solid var(--border); +} + +.highlight { + background-color: var(--bg-light); + color: #ff0 !important; + text-decoration-line: underline !important; +} + +.ident { + color: #88c; + font-weight: bold; cursor: pointer; } -img { - border: none; - background: transparent; +.animated-icon { + transition: transform 0.5s; + animation: rotate 0.5s; } +@keyframes rotate { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} +/* */ /* Let's be modern and support mobile*/ @media (max-width: 750px) { .left { - font-size: 1em; position: static; - background-color: var(--bg-light); margin: 1em; - padding: 10px; - left: 0px; width: auto; - border-top: solid 1px black; } #menu { @@ -316,7 +284,7 @@ img { margin: 1em; } - #header { + .header { padding-left: 1em; } diff --git a/SMBolero.Client/wwwroot/index.html b/SMBolero.Client/wwwroot/index.html index 97d1a100..12d5bba2 100644 --- a/SMBolero.Client/wwwroot/index.html +++ b/SMBolero.Client/wwwroot/index.html @@ -9,5 +9,6 @@
Loading...
+ diff --git a/SMBolero.Client/wwwroot/main.html b/SMBolero.Client/wwwroot/main.html index 174a6c7a..cd142878 100644 --- a/SMBolero.Client/wwwroot/main.html +++ b/SMBolero.Client/wwwroot/main.html @@ -1,5 +1,5 @@
Ctrl-Alt-Test - Shader Minifier
- +