Files
gio/gpu/internal/convertshaders/hlsl.go
T
Elias Naur 8adcf25049 gpu/internal/convertshaders: batch calls to winepath
Wine tools can be slow to run, so it makes sense to batch their use.
Fortunately, winepath supports resolving multiple paths in one
execution.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-04-12 12:44:06 +02:00

134 lines
3.2 KiB
Go

// SPDX-License-Identifier: Unlicense OR MIT
package main
import (
"fmt"
"io/ioutil"
"os/exec"
"path/filepath"
"runtime"
"strings"
)
// FXC is hlsl compiler that targets ShaderModel 5.x and lower.
type FXC struct {
Bin string
WorkDir WorkDir
}
func NewFXC() *FXC { return &FXC{Bin: "fxc.exe"} }
// Compile compiles the input shader.
func (fxc *FXC) Compile(path, variant string, input []byte, entryPoint string, profileVersion string) (string, error) {
base := fxc.WorkDir.Path(filepath.Base(path), variant, profileVersion)
pathin := base + ".in"
pathout := base + ".out"
result := pathout
if err := fxc.WorkDir.WriteFile(pathin, input); err != nil {
return "", fmt.Errorf("unable to write shader to disk: %w", err)
}
cmd := exec.Command(fxc.Bin)
if runtime.GOOS != "windows" {
cmd = exec.Command("wine", fxc.Bin)
if err := winepath(&pathin, &pathout); err != nil {
return "", err
}
}
var profile string
switch filepath.Ext(path) {
case ".frag":
profile = "ps_" + profileVersion
case ".vert":
profile = "vs_" + profileVersion
default:
return "", fmt.Errorf("unrecognized shader type %s", path)
}
cmd.Args = append(cmd.Args,
"/Fo", pathout,
"/T", profile,
"/E", entryPoint,
pathin,
)
output, err := cmd.CombinedOutput()
if err != nil {
info := ""
if runtime.GOOS != "windows" {
info = "If the fxc tool cannot be found, set WINEPATH to the Windows path for the Windows SDK.\n"
}
return "", fmt.Errorf("%s\n%sfailed to run %v: %w", output, info, cmd.Args, err)
}
compiled, err := ioutil.ReadFile(result)
if err != nil {
return "", fmt.Errorf("unable to read output %q: %w", pathout, err)
}
return string(compiled), nil
}
// DXC is hlsl compiler that targets ShaderModel 6.0 and newer.
type DXC struct {
Bin string
WorkDir WorkDir
}
func NewDXC() *DXC { return &DXC{Bin: "dxc"} }
// Compile compiles the input shader.
func (dxc *DXC) Compile(path, variant string, input []byte, entryPoint string, profile string) (string, error) {
base := dxc.WorkDir.Path(filepath.Base(path), variant, profile)
pathin := base + ".in"
pathout := base + ".out"
result := pathout
if err := dxc.WorkDir.WriteFile(pathin, input); err != nil {
return "", fmt.Errorf("unable to write shader to disk: %w", err)
}
cmd := exec.Command(dxc.Bin)
cmd.Args = append(cmd.Args,
"-Fo", pathout,
"-T", profile,
"-E", entryPoint,
"-Qstrip_reflect",
pathin,
)
output, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("%s\nfailed to run %v: %w", output, cmd.Args, err)
}
compiled, err := ioutil.ReadFile(result)
if err != nil {
return "", fmt.Errorf("unable to read output %q: %w", pathout, err)
}
return string(compiled), nil
}
// winepath uses the winepath tool to convert a paths to Windows format.
// The returned path can be used as arguments for Windows command line tools.
func winepath(paths ...*string) error {
winepath := exec.Command("winepath", "--windows")
for _, path := range paths {
winepath.Args = append(winepath.Args, *path)
}
out, err := winepath.Output()
if err != nil {
return fmt.Errorf("unable to run `winepath --windows`: %w", err)
}
winPaths := strings.Split(strings.TrimSpace(string(out)), "\n")
for i, path := range paths {
*path = winPaths[i]
}
return nil
}