github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/gpu/internal/convertshaders/hlsl.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package main 4 5 import ( 6 "bytes" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os/exec" 11 "path/filepath" 12 "runtime" 13 "strings" 14 ) 15 16 // FXC is hlsl compiler that targets ShaderModel 5.x and lower. 17 type FXC struct { 18 Bin string 19 WorkDir WorkDir 20 } 21 22 func NewFXC() *FXC { return &FXC{Bin: "fxc.exe"} } 23 24 // Compile compiles the input shader. 25 func (fxc *FXC) Compile(path, variant string, input []byte, entryPoint string, profileVersion string) (string, error) { 26 base := fxc.WorkDir.Path(filepath.Base(path), variant, profileVersion) 27 pathin := base + ".in" 28 pathout := base + ".out" 29 result := pathout 30 31 if err := fxc.WorkDir.WriteFile(pathin, input); err != nil { 32 return "", fmt.Errorf("unable to write shader to disk: %w", err) 33 } 34 35 cmd := exec.Command(fxc.Bin) 36 if runtime.GOOS != "windows" { 37 cmd = exec.Command("wine", fxc.Bin) 38 if err := winepath(&pathin, &pathout); err != nil { 39 return "", err 40 } 41 } 42 43 var profile string 44 switch filepath.Ext(path) { 45 case ".frag": 46 profile = "ps_" + profileVersion 47 case ".vert": 48 profile = "vs_" + profileVersion 49 case ".comp": 50 profile = "cs_" + profileVersion 51 default: 52 return "", fmt.Errorf("unrecognized shader type %s", path) 53 } 54 55 cmd.Args = append(cmd.Args, 56 "/Fo", pathout, 57 "/T", profile, 58 "/E", entryPoint, 59 pathin, 60 ) 61 62 output, err := cmd.CombinedOutput() 63 if err != nil { 64 info := "" 65 if runtime.GOOS != "windows" { 66 info = "If the fxc tool cannot be found, set WINEPATH to the Windows path for the Windows SDK.\n" 67 } 68 return "", fmt.Errorf("%s\n%sfailed to run %v: %w", output, info, cmd.Args, err) 69 } 70 71 compiled, err := ioutil.ReadFile(result) 72 if err != nil { 73 return "", fmt.Errorf("unable to read output %q: %w", pathout, err) 74 } 75 76 return string(compiled), nil 77 } 78 79 // DXC is hlsl compiler that targets ShaderModel 6.0 and newer. 80 type DXC struct { 81 Bin string 82 WorkDir WorkDir 83 } 84 85 func NewDXC() *DXC { return &DXC{Bin: "dxc"} } 86 87 // Compile compiles the input shader. 88 func (dxc *DXC) Compile(path, variant string, input []byte, entryPoint string, profile string) (string, error) { 89 base := dxc.WorkDir.Path(filepath.Base(path), variant, profile) 90 pathin := base + ".in" 91 pathout := base + ".out" 92 result := pathout 93 94 if err := dxc.WorkDir.WriteFile(pathin, input); err != nil { 95 return "", fmt.Errorf("unable to write shader to disk: %w", err) 96 } 97 98 cmd := exec.Command(dxc.Bin) 99 100 cmd.Args = append(cmd.Args, 101 "-Fo", pathout, 102 "-T", profile, 103 "-E", entryPoint, 104 "-Qstrip_reflect", 105 pathin, 106 ) 107 108 output, err := cmd.CombinedOutput() 109 if err != nil { 110 return "", fmt.Errorf("%s\nfailed to run %v: %w", output, cmd.Args, err) 111 } 112 113 compiled, err := ioutil.ReadFile(result) 114 if err != nil { 115 return "", fmt.Errorf("unable to read output %q: %w", pathout, err) 116 } 117 118 return string(compiled), nil 119 } 120 121 // winepath uses the winepath tool to convert a paths to Windows format. 122 // The returned path can be used as arguments for Windows command line tools. 123 func winepath(paths ...*string) error { 124 winepath := exec.Command("winepath", "--windows") 125 for _, path := range paths { 126 winepath.Args = append(winepath.Args, *path) 127 } 128 // Use a pipe instead of Output, because winepath may have left wineserver 129 // running for several seconds as a grandchild. 130 out, err := winepath.StdoutPipe() 131 if err != nil { 132 return fmt.Errorf("unable to start winepath: %w", err) 133 } 134 if err := winepath.Start(); err != nil { 135 return fmt.Errorf("unable to start winepath: %w", err) 136 } 137 var buf bytes.Buffer 138 if _, err := io.Copy(&buf, out); err != nil { 139 return fmt.Errorf("unable to run winepath: %w", err) 140 } 141 winPaths := strings.Split(strings.TrimSpace(buf.String()), "\n") 142 for i, path := range paths { 143 *path = winPaths[i] 144 } 145 return nil 146 }