all: switch to external shaders in the gioui.org/shaders module

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-08-02 17:46:40 +02:00
parent 18b4442393
commit 6aee543234
50 changed files with 112 additions and 11502 deletions
@@ -1,50 +0,0 @@
// SPDX-License-Identifier: Unlicense OR MIT
package main
import (
"bytes"
"fmt"
"io/ioutil"
"os/exec"
"path/filepath"
)
// GLSLValidator is OpenGL reference compiler.
type GLSLValidator struct {
Bin string
WorkDir WorkDir
}
func NewGLSLValidator() *GLSLValidator { return &GLSLValidator{Bin: "glslangValidator"} }
// Convert converts a glsl shader to spirv.
func (glsl *GLSLValidator) Convert(path, variant string, hlsl bool, input []byte) ([]byte, error) {
base := glsl.WorkDir.Path(filepath.Base(path), variant)
pathout := base + ".out"
cmd := exec.Command(glsl.Bin,
"--stdin",
"-I"+filepath.Dir(path),
"-V", // OpenGL ES 3.1.
"-w", // Suppress warnings.
"-S", filepath.Ext(path)[1:],
"-o", pathout,
)
if hlsl {
cmd.Args = append(cmd.Args, "-DHLSL")
}
cmd.Stdin = bytes.NewBuffer(input)
out, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("%s\nfailed to run %v: %w", out, cmd.Args, err)
}
compiled, err := ioutil.ReadFile(pathout)
if err != nil {
return nil, fmt.Errorf("unable to read output %q: %w", pathout, err)
}
return compiled, nil
}
-146
View File
@@ -1,146 +0,0 @@
// SPDX-License-Identifier: Unlicense OR MIT
package main
import (
"bytes"
"fmt"
"io"
"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
case ".comp":
profile = "cs_" + 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)
}
// Use a pipe instead of Output, because winepath may have left wineserver
// running for several seconds as a grandchild.
out, err := winepath.StdoutPipe()
if err != nil {
return fmt.Errorf("unable to start winepath: %w", err)
}
if err := winepath.Start(); err != nil {
return fmt.Errorf("unable to start winepath: %w", err)
}
var buf bytes.Buffer
if _, err := io.Copy(&buf, out); err != nil {
return fmt.Errorf("unable to run winepath: %w", err)
}
winPaths := strings.Split(strings.TrimSpace(buf.String()), "\n")
for i, path := range paths {
*path = winPaths[i]
}
return nil
}
-418
View File
@@ -1,418 +0,0 @@
// SPDX-License-Identifier: Unlicense OR MIT
package main
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"sort"
"strconv"
"strings"
"sync"
"text/template"
"gioui.org/gpu/internal/driver"
)
func main() {
packageName := flag.String("package", "", "specify Go package name")
workdir := flag.String("work", "", "temporary working directory (default TEMP)")
shadersDir := flag.String("dir", "shaders", "shaders directory")
directCompute := flag.Bool("directcompute", false, "enable compiling DirectCompute shaders")
flag.Parse()
var work WorkDir
cleanup := func() {}
if *workdir == "" {
tempdir, err := ioutil.TempDir("", "shader-convert")
if err != nil {
fmt.Fprintf(os.Stderr, "failed to create tempdir: %v\n", err)
os.Exit(1)
}
cleanup = func() { os.RemoveAll(tempdir) }
defer cleanup()
work = WorkDir(tempdir)
} else {
if abs, err := filepath.Abs(*workdir); err == nil {
*workdir = abs
}
work = WorkDir(*workdir)
}
var out bytes.Buffer
conv := NewConverter(work, *packageName, *shadersDir, *directCompute)
if err := conv.Run(&out); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
cleanup()
os.Exit(1)
}
if err := ioutil.WriteFile("shaders.go", out.Bytes(), 0644); err != nil {
fmt.Fprintf(os.Stderr, "failed to create shaders: %v\n", err)
cleanup()
os.Exit(1)
}
cmd := exec.Command("gofmt", "-s", "-w", "shaders.go")
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "formatting shaders.go failed: %v\n", err)
cleanup()
os.Exit(1)
}
}
type Converter struct {
workDir WorkDir
shadersDir string
directCompute bool
packageName string
glslvalidator *GLSLValidator
spirv *SPIRVCross
fxc *FXC
}
func NewConverter(workDir WorkDir, packageName, shadersDir string, directCompute bool) *Converter {
if abs, err := filepath.Abs(shadersDir); err == nil {
shadersDir = abs
}
conv := &Converter{}
conv.workDir = workDir
conv.shadersDir = shadersDir
conv.directCompute = directCompute
conv.packageName = packageName
conv.glslvalidator = NewGLSLValidator()
conv.spirv = NewSPIRVCross()
conv.fxc = NewFXC()
verifyBinaryPath(&conv.glslvalidator.Bin)
verifyBinaryPath(&conv.spirv.Bin)
// We cannot check fxc since it may depend on wine.
conv.glslvalidator.WorkDir = workDir.Dir("glslvalidator")
conv.fxc.WorkDir = workDir.Dir("fxc")
conv.spirv.WorkDir = workDir.Dir("spirv")
return conv
}
func verifyBinaryPath(bin *string) {
new, err := exec.LookPath(*bin)
if err != nil {
fmt.Fprintf(os.Stderr, "unable to find %q: %v\n", *bin, err)
} else {
*bin = new
}
}
func (conv *Converter) Run(out io.Writer) error {
shaders, err := filepath.Glob(filepath.Join(conv.shadersDir, "*"))
if len(shaders) == 0 || err != nil {
return fmt.Errorf("failed to list shaders in %q: %w", conv.shadersDir, err)
}
sort.Strings(shaders)
var workers Workers
type ShaderResult struct {
Path string
Shaders []driver.ShaderSources
Error error
}
shaderResults := make([]ShaderResult, len(shaders))
for i, shaderPath := range shaders {
i, shaderPath := i, shaderPath
switch filepath.Ext(shaderPath) {
case ".vert", ".frag":
workers.Go(func() {
shaders, err := conv.Shader(shaderPath)
shaderResults[i] = ShaderResult{
Path: shaderPath,
Shaders: shaders,
Error: err,
}
})
case ".comp":
workers.Go(func() {
shaders, err := conv.ComputeShader(shaderPath)
shaderResults[i] = ShaderResult{
Path: shaderPath,
Shaders: shaders,
Error: err,
}
})
default:
continue
}
}
workers.Wait()
var allErrors string
for _, r := range shaderResults {
if r.Error != nil {
if len(allErrors) > 0 {
allErrors += "\n\n"
}
allErrors += "--- " + r.Path + " --- \n\n" + r.Error.Error() + "\n"
}
}
if len(allErrors) > 0 {
return errors.New(allErrors)
}
fmt.Fprintf(out, "// Code generated by build.go. DO NOT EDIT.\n\n")
fmt.Fprintf(out, "package %s\n\n", conv.packageName)
fmt.Fprintf(out, "import %q\n\n", "gioui.org/gpu/internal/driver")
fmt.Fprintf(out, "var (\n")
for _, r := range shaderResults {
if len(r.Shaders) == 0 {
continue
}
name := filepath.Base(r.Path)
name = strings.ReplaceAll(name, ".", "_")
fmt.Fprintf(out, "\tshader_%s = ", name)
multiVariant := len(r.Shaders) > 1
if multiVariant {
fmt.Fprintf(out, "[...]driver.ShaderSources{\n")
}
for _, src := range r.Shaders {
fmt.Fprintf(out, "driver.ShaderSources{\n")
fmt.Fprintf(out, "Name: %#v,\n", src.Name)
if len(src.Inputs) > 0 {
fmt.Fprintf(out, "Inputs: %#v,\n", src.Inputs)
}
if u := src.Uniforms; len(u.Blocks) > 0 {
fmt.Fprintf(out, "Uniforms: driver.UniformsReflection{\n")
fmt.Fprintf(out, "Blocks: %#v,\n", u.Blocks)
fmt.Fprintf(out, "Locations: %#v,\n", u.Locations)
fmt.Fprintf(out, "Size: %d,\n", u.Size)
fmt.Fprintf(out, "},\n")
}
if len(src.Textures) > 0 {
fmt.Fprintf(out, "Textures: %#v,\n", src.Textures)
}
if len(src.GLSL100ES) > 0 {
fmt.Fprintf(out, "GLSL100ES: `%s`,\n", src.GLSL100ES)
}
if len(src.GLSL300ES) > 0 {
fmt.Fprintf(out, "GLSL300ES: `%s`,\n", src.GLSL300ES)
}
if len(src.GLSL310ES) > 0 {
fmt.Fprintf(out, "GLSL310ES: `%s`,\n", src.GLSL310ES)
}
if len(src.GLSL130) > 0 {
fmt.Fprintf(out, "GLSL130: `%s`,\n", src.GLSL130)
}
if len(src.GLSL150) > 0 {
fmt.Fprintf(out, "GLSL150: `%s`,\n", src.GLSL150)
}
if len(src.HLSL) > 0 {
fmt.Fprintf(out, "HLSL: %q,\n", src.HLSL)
}
if len(src.Hash) > 0 {
fmt.Fprintf(out, "Hash: %q,\n", src.Hash)
}
fmt.Fprintf(out, "}")
if multiVariant {
fmt.Fprintf(out, ",")
}
fmt.Fprintf(out, "\n")
}
if multiVariant {
fmt.Fprintf(out, "}\n")
}
}
fmt.Fprintf(out, ")\n")
return nil
}
func (conv *Converter) Shader(shaderPath string) ([]driver.ShaderSources, error) {
type Variant struct {
FetchColorExpr string
Header string
}
variantArgs := [...]Variant{
{
FetchColorExpr: `_color.color`,
Header: `layout(binding=0) uniform Color { vec4 color; } _color;`,
},
{
FetchColorExpr: `mix(_gradient.color1, _gradient.color2, clamp(vUV.x, 0.0, 1.0))`,
Header: `layout(binding=0) uniform Gradient { vec4 color1; vec4 color2; } _gradient;`,
},
{
FetchColorExpr: `texture(tex, vUV)`,
Header: `layout(binding=0) uniform sampler2D tex;`,
},
}
shaderTemplate, err := template.ParseFiles(shaderPath)
if err != nil {
return nil, fmt.Errorf("failed to parse template %q: %w", shaderPath, err)
}
var variants []driver.ShaderSources
for i, variantArg := range variantArgs {
variantName := strconv.Itoa(i)
var buf bytes.Buffer
err := shaderTemplate.Execute(&buf, variantArg)
if err != nil {
return nil, fmt.Errorf("failed to execute template %q with %#v: %w", shaderPath, variantArg, err)
}
var sources driver.ShaderSources
sources.Name = filepath.Base(shaderPath)
// Ignore error; some shaders are not meant to run in GLSL 1.00.
sources.GLSL100ES, _, _ = conv.ShaderVariant(shaderPath, variantName, buf.Bytes(), "es", "100")
var metadata Metadata
sources.GLSL300ES, metadata, err = conv.ShaderVariant(shaderPath, variantName, buf.Bytes(), "es", "300")
if err != nil {
return nil, fmt.Errorf("failed to convert GLSL300ES:\n%w", err)
}
sources.GLSL130, _, err = conv.ShaderVariant(shaderPath, variantName, buf.Bytes(), "glsl", "130")
if err != nil {
return nil, fmt.Errorf("failed to convert GLSL130:\n%w", err)
}
hlsl, _, err := conv.ShaderVariant(shaderPath, variantName, buf.Bytes(), "hlsl", "40")
if err != nil {
return nil, fmt.Errorf("failed to convert HLSL:\n%w", err)
}
sources.HLSL, err = conv.fxc.Compile(shaderPath, variantName, []byte(hlsl), "main", "4_0_level_9_1")
if err != nil {
// Attempt shader model 4.0. Only the gpu/headless
// test shaders use features not supported by level
// 9.1.
sources.HLSL, err = conv.fxc.Compile(shaderPath, variantName, []byte(hlsl), "main", "4_0")
if err != nil {
return nil, fmt.Errorf("failed to compile HLSL: %w", err)
}
}
sources.GLSL150, _, err = conv.ShaderVariant(shaderPath, variantName, buf.Bytes(), "glsl", "150")
if err != nil {
return nil, fmt.Errorf("failed to convert GLSL150:\n%w", err)
}
sources.Uniforms = metadata.Uniforms
sources.Inputs = metadata.Inputs
sources.Textures = metadata.Textures
variants = append(variants, sources)
}
// If the shader don't use the variant arguments, output only a single version.
if variants[0].GLSL100ES == variants[1].GLSL100ES {
variants = variants[:1]
}
return variants, nil
}
func (conv *Converter) ShaderVariant(shaderPath, variant string, src []byte, lang, profile string) (string, Metadata, error) {
spirv, err := conv.glslvalidator.Convert(shaderPath, variant, lang == "hlsl", src)
if err != nil {
return "", Metadata{}, fmt.Errorf("failed to generate SPIR-V for %q: %w", shaderPath, err)
}
dst, err := conv.spirv.Convert(shaderPath, variant, spirv, lang, profile)
if err != nil {
return "", Metadata{}, fmt.Errorf("failed to convert shader %q: %w", shaderPath, err)
}
meta, err := conv.spirv.Metadata(shaderPath, variant, spirv)
if err != nil {
return "", Metadata{}, fmt.Errorf("failed to extract metadata for shader %q: %w", shaderPath, err)
}
return dst, meta, nil
}
func (conv *Converter) ComputeShader(shaderPath string) ([]driver.ShaderSources, error) {
shader, err := ioutil.ReadFile(shaderPath)
if err != nil {
return nil, fmt.Errorf("failed to load shader %q: %w", shaderPath, err)
}
spirv, err := conv.glslvalidator.Convert(shaderPath, "", false, shader)
if err != nil {
return nil, fmt.Errorf("failed to convert compute shader %q: %w", shaderPath, err)
}
var sources driver.ShaderSources
sources.Name = filepath.Base(shaderPath)
sum := sha256.Sum256(shader)
sources.Hash = hex.EncodeToString(sum[:])
sources.GLSL310ES, err = conv.spirv.Convert(shaderPath, "", spirv, "es", "310")
if err != nil {
return nil, fmt.Errorf("failed to convert es compute shader %q: %w", shaderPath, err)
}
sources.GLSL310ES = unixLineEnding(sources.GLSL310ES)
hlslSource, err := conv.spirv.Convert(shaderPath, "", spirv, "hlsl", "50")
if err != nil {
return nil, fmt.Errorf("failed to convert hlsl compute shader %q: %w", shaderPath, err)
}
dxil, err := conv.fxc.Compile(shaderPath, "0", []byte(hlslSource), "main", "5_0")
if err != nil {
return nil, fmt.Errorf("failed to compile hlsl compute shader %q: %w", shaderPath, err)
}
if conv.directCompute {
sources.HLSL = dxil
}
return []driver.ShaderSources{sources}, nil
}
// Workers implements wait group with synchronous logging.
type Workers struct {
running sync.WaitGroup
}
func (lg *Workers) Go(fn func()) {
lg.running.Add(1)
go func() {
defer lg.running.Done()
fn()
}()
}
func (lg *Workers) Wait() {
lg.running.Wait()
}
func unixLineEnding(s string) string {
return strings.ReplaceAll(s, "\r\n", "\n")
}
-212
View File
@@ -1,212 +0,0 @@
// SPDX-License-Identifier: Unlicense OR MIT
package main
import (
"encoding/json"
"fmt"
"os/exec"
"path/filepath"
"sort"
"strings"
"gioui.org/gpu/internal/driver"
)
// Metadata contains reflection data about a shader.
type Metadata struct {
Uniforms driver.UniformsReflection
Inputs []driver.InputLocation
Textures []driver.TextureBinding
}
// SPIRVCross cross-compiles spirv shaders to es, hlsl and others.
type SPIRVCross struct {
Bin string
WorkDir WorkDir
}
func NewSPIRVCross() *SPIRVCross { return &SPIRVCross{Bin: "spirv-cross"} }
// Convert converts compute shader from spirv format to a target format.
func (spirv *SPIRVCross) Convert(path, variant string, shader []byte, target, version string) (string, error) {
base := spirv.WorkDir.Path(filepath.Base(path), variant)
if err := spirv.WorkDir.WriteFile(base, shader); err != nil {
return "", fmt.Errorf("unable to write shader to disk: %w", err)
}
var cmd *exec.Cmd
switch target {
case "glsl":
cmd = exec.Command(spirv.Bin,
"--no-es",
"--version", version,
)
case "es":
cmd = exec.Command(spirv.Bin,
"--es",
"--version", version,
)
case "hlsl":
cmd = exec.Command(spirv.Bin,
"--hlsl",
"--shader-model", version,
)
default:
return "", fmt.Errorf("unknown target %q", target)
}
cmd.Args = append(cmd.Args, "--no-420pack-extension", base)
out, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("%s\nfailed to run %v: %w", out, cmd.Args, err)
}
s := string(out)
if target != "hlsl" {
// Strip Windows \r in line endings.
s = unixLineEnding(s)
}
return s, nil
}
// Metadata extracts metadata for a SPIR-V shader.
func (spirv *SPIRVCross) Metadata(path, variant string, shader []byte) (Metadata, error) {
base := spirv.WorkDir.Path(filepath.Base(path), variant)
if err := spirv.WorkDir.WriteFile(base, shader); err != nil {
return Metadata{}, fmt.Errorf("unable to write shader to disk: %w", err)
}
cmd := exec.Command(spirv.Bin,
base,
"--reflect",
)
out, err := cmd.Output()
if err != nil {
return Metadata{}, fmt.Errorf("failed to run %v: %w", cmd.Args, err)
}
meta, err := parseMetadata(out)
if err != nil {
return Metadata{}, fmt.Errorf("%s\nfailed to parse metadata: %w", out, err)
}
return meta, nil
}
func parseMetadata(data []byte) (Metadata, error) {
var reflect struct {
Types map[string]struct {
Name string `json:"name"`
Members []struct {
Name string `json:"name"`
Type string `json:"type"`
Offset int `json:"offset"`
} `json:"members"`
} `json:"types"`
Inputs []struct {
Name string `json:"name"`
Type string `json:"type"`
Location int `json:"location"`
} `json:"inputs"`
Textures []struct {
Name string `json:"name"`
Type string `json:"type"`
Set int `json:"set"`
Binding int `json:"binding"`
} `json:"textures"`
UBOs []struct {
Name string `json:"name"`
Type string `json:"type"`
BlockSize int `json:"block_size"`
Set int `json:"set"`
Binding int `json:"binding"`
} `json:"ubos"`
}
if err := json.Unmarshal(data, &reflect); err != nil {
return Metadata{}, fmt.Errorf("failed to parse reflection data: %w", err)
}
var m Metadata
for _, input := range reflect.Inputs {
dataType, dataSize, err := parseDataType(input.Type)
if err != nil {
return Metadata{}, fmt.Errorf("parseReflection: %v", err)
}
m.Inputs = append(m.Inputs, driver.InputLocation{
Name: input.Name,
Location: input.Location,
Semantic: "TEXCOORD",
SemanticIndex: input.Location,
Type: dataType,
Size: dataSize,
})
}
sort.Slice(m.Inputs, func(i, j int) bool {
return m.Inputs[i].Location < m.Inputs[j].Location
})
blockOffset := 0
for _, block := range reflect.UBOs {
m.Uniforms.Blocks = append(m.Uniforms.Blocks, driver.UniformBlock{
Name: block.Name,
Binding: block.Binding,
})
t := reflect.Types[block.Type]
// By convention uniform block variables are named by prepending an underscore
// and converting to lowercase.
blockVar := "_" + strings.ToLower(block.Name)
for _, member := range t.Members {
dataType, size, err := parseDataType(member.Type)
if err != nil {
return Metadata{}, fmt.Errorf("failed to parse reflection data: %v", err)
}
m.Uniforms.Locations = append(m.Uniforms.Locations, driver.UniformLocation{
Name: fmt.Sprintf("%s.%s", blockVar, member.Name),
Type: dataType,
Size: size,
Offset: blockOffset + member.Offset,
})
}
blockOffset += block.BlockSize
}
m.Uniforms.Size = blockOffset
for _, texture := range reflect.Textures {
m.Textures = append(m.Textures, driver.TextureBinding{
Name: texture.Name,
Binding: texture.Binding,
})
}
//return m, fmt.Errorf("not yet!: %+v", reflect)
return m, nil
}
func parseDataType(t string) (driver.DataType, int, error) {
switch t {
case "float":
return driver.DataTypeFloat, 1, nil
case "vec2":
return driver.DataTypeFloat, 2, nil
case "vec3":
return driver.DataTypeFloat, 3, nil
case "vec4":
return driver.DataTypeFloat, 4, nil
case "int":
return driver.DataTypeInt, 1, nil
case "int2":
return driver.DataTypeInt, 2, nil
case "int3":
return driver.DataTypeInt, 3, nil
case "int4":
return driver.DataTypeInt, 4, nil
default:
return 0, 0, fmt.Errorf("unsupported input data type: %s", t)
}
}
-35
View File
@@ -1,35 +0,0 @@
// SPDX-License-Identifier: Unlicense OR MIT
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
type WorkDir string
func (wd WorkDir) Dir(path string) WorkDir {
dirname := filepath.Join(string(wd), path)
if err := os.Mkdir(dirname, 0755); err != nil {
if !os.IsExist(err) {
fmt.Fprintf(os.Stderr, "failed to create %q: %v\n", dirname, err)
}
}
return WorkDir(dirname)
}
func (wd WorkDir) Path(path ...string) (fullpath string) {
return filepath.Join(string(wd), strings.Join(path, "."))
}
func (wd WorkDir) WriteFile(path string, data []byte) error {
err := ioutil.WriteFile(path, data, 0644)
if err != nil {
return fmt.Errorf("unable to create %v: %w", path, err)
}
return nil
}
+9 -8
View File
@@ -14,6 +14,7 @@ import (
"gioui.org/gpu/internal/driver"
"gioui.org/internal/d3d11"
"gioui.org/shader"
)
type Backend struct {
@@ -287,7 +288,7 @@ func (b *Backend) NewFramebuffer(tex driver.Texture) (driver.Framebuffer, error)
return fbo, nil
}
func (b *Backend) NewInputLayout(vertexShader driver.ShaderSources, layout []driver.InputDesc) (driver.InputLayout, error) {
func (b *Backend) NewInputLayout(vertexShader shader.Sources, layout []shader.InputDesc) (driver.InputLayout, error) {
if len(vertexShader.Inputs) != len(layout) {
return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vertexShader.Inputs))
}
@@ -300,7 +301,7 @@ func (b *Backend) NewInputLayout(vertexShader driver.ShaderSources, layout []dri
}
var format uint32
switch l.Type {
case driver.DataTypeFloat:
case shader.DataTypeFloat:
switch l.Size {
case 1:
format = d3d11.DXGI_FORMAT_R32_FLOAT
@@ -313,7 +314,7 @@ func (b *Backend) NewInputLayout(vertexShader driver.ShaderSources, layout []dri
default:
panic("unsupported data size")
}
case driver.DataTypeShort:
case shader.DataTypeShort:
switch l.Size {
case 1:
format = d3d11.DXGI_FORMAT_R16_SINT
@@ -332,7 +333,7 @@ func (b *Backend) NewInputLayout(vertexShader driver.ShaderSources, layout []dri
AlignedByteOffset: uint32(l.Offset),
}
}
l, err := b.dev.CreateInputLayout(descs, []byte(vertexShader.HLSL))
l, err := b.dev.CreateInputLayout(descs, []byte(vertexShader.DXBC))
if err != nil {
return nil, err
}
@@ -380,16 +381,16 @@ func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (dri
return &Buffer{backend: b, buf: buf, bind: bind, immutable: true}, nil
}
func (b *Backend) NewComputeProgram(shader driver.ShaderSources) (driver.Program, error) {
func (b *Backend) NewComputeProgram(shader shader.Sources) (driver.Program, error) {
panic("not implemented")
}
func (b *Backend) NewProgram(vertexShader, fragmentShader driver.ShaderSources) (driver.Program, error) {
vs, err := b.dev.CreateVertexShader([]byte(vertexShader.HLSL))
func (b *Backend) NewProgram(vertexShader, fragmentShader shader.Sources) (driver.Program, error) {
vs, err := b.dev.CreateVertexShader([]byte(vertexShader.DXBC))
if err != nil {
return nil, err
}
ps, err := b.dev.CreatePixelShader([]byte(fragmentShader.HLSL))
ps, err := b.dev.CreatePixelShader([]byte(fragmentShader.DXBC))
if err != nil {
return nil, err
}
+5 -68
View File
@@ -6,6 +6,8 @@ import (
"errors"
"image"
"time"
"gioui.org/shader"
)
// Device represents the abstraction of underlying GPU
@@ -23,9 +25,9 @@ type Device interface {
NewFramebuffer(tex Texture) (Framebuffer, error)
NewImmutableBuffer(typ BufferBinding, data []byte) (Buffer, error)
NewBuffer(typ BufferBinding, size int) (Buffer, error)
NewComputeProgram(shader ShaderSources) (Program, error)
NewProgram(vertexShader, fragmentShader ShaderSources) (Program, error)
NewInputLayout(vertexShader ShaderSources, layout []InputDesc) (InputLayout, error)
NewComputeProgram(shader shader.Sources) (Program, error)
NewProgram(vertexShader, fragmentShader shader.Sources) (Program, error)
NewInputLayout(vertexShader shader.Sources, layout []shader.InputDesc) (InputLayout, error)
Clear(r, g, b, a float32)
Viewport(x, y, width, height int)
@@ -49,63 +51,6 @@ type Device interface {
Release()
}
type ShaderSources struct {
Name string
GLSL100ES string
GLSL300ES string
GLSL310ES string
GLSL130 string
GLSL150 string
HLSL string
Uniforms UniformsReflection
Inputs []InputLocation
Textures []TextureBinding
Hash string
}
type UniformsReflection struct {
Blocks []UniformBlock
Locations []UniformLocation
Size int
}
type TextureBinding struct {
Name string
Binding int
}
type UniformBlock struct {
Name string
Binding int
}
type UniformLocation struct {
Name string
Type DataType
Size int
Offset int
}
type InputLocation struct {
// For GLSL.
Name string
Location int
// For HLSL.
Semantic string
SemanticIndex int
Type DataType
Size int
}
// InputDesc describes a vertex attribute as laid out in a Buffer.
type InputDesc struct {
Type DataType
Size int
Offset int
}
// InputLayout is the driver specific representation of the mapping
// between Buffers and shader attributes.
type InputLayout interface {
@@ -123,8 +68,6 @@ type TextureFormat uint8
type BufferBinding uint8
type DataType uint8
type Features uint
type Caps struct {
@@ -167,12 +110,6 @@ type Texture interface {
Release()
}
const (
DataTypeFloat DataType = iota
DataTypeInt
DataTypeShort
)
const (
BufferBindingIndices BufferBinding = 1 << iota
BufferBindingVertices
+15 -14
View File
@@ -12,6 +12,7 @@ import (
"gioui.org/gpu/internal/driver"
"gioui.org/internal/gl"
"gioui.org/shader"
)
// Backend implements driver.Device.
@@ -139,13 +140,13 @@ type uniformsTracker struct {
type uniformLocation struct {
uniform gl.Uniform
offset int
typ driver.DataType
typ shader.DataType
size int
}
type gpuInputLayout struct {
inputs []driver.InputLocation
layout []driver.InputDesc
inputs []shader.InputLocation
layout []shader.InputDesc
}
// textureTriple holds the type settings for
@@ -846,7 +847,7 @@ func (b *Backend) Clear(colR, colG, colB, colA float32) {
b.funcs.Clear(gl.COLOR_BUFFER_BIT)
}
func (b *Backend) NewInputLayout(vs driver.ShaderSources, layout []driver.InputDesc) (driver.InputLayout, error) {
func (b *Backend) NewInputLayout(vs shader.Sources, layout []shader.InputDesc) (driver.InputLayout, error) {
if len(vs.Inputs) != len(layout) {
return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vs.Inputs))
}
@@ -861,7 +862,7 @@ func (b *Backend) NewInputLayout(vs driver.ShaderSources, layout []driver.InputD
}, nil
}
func (b *Backend) NewComputeProgram(src driver.ShaderSources) (driver.Program, error) {
func (b *Backend) NewComputeProgram(src shader.Sources) (driver.Program, error) {
p, err := gl.CreateComputeProgram(b.funcs, src.GLSL310ES)
if err != nil {
return nil, fmt.Errorf("%s: %v", src.Name, err)
@@ -873,7 +874,7 @@ func (b *Backend) NewComputeProgram(src driver.ShaderSources) (driver.Program, e
return gpuProg, nil
}
func (b *Backend) NewProgram(vertShader, fragShader driver.ShaderSources) (driver.Program, error) {
func (b *Backend) NewProgram(vertShader, fragShader shader.Sources) (driver.Program, error) {
attr := make([]string, len(vertShader.Inputs))
for _, inp := range vertShader.Inputs {
attr[inp.Location] = inp.Name
@@ -937,7 +938,7 @@ func (b *Backend) NewProgram(vertShader, fragShader driver.ShaderSources) (drive
return gpuProg, nil
}
func lookupUniform(funcs *gl.Functions, p gl.Program, loc driver.UniformLocation) uniformLocation {
func lookupUniform(funcs *gl.Functions, p gl.Program, loc shader.UniformLocation) uniformLocation {
u := funcs.GetUniformLocation(p, loc.Name)
if !u.Valid() {
panic(fmt.Errorf("uniform %q not found", loc.Name))
@@ -985,7 +986,7 @@ func (p *gpuProgram) Release() {
p.backend.glstate.deleteProgram(p.backend.funcs, p.obj)
}
func (u *uniformsTracker) setup(funcs *gl.Functions, p gl.Program, uniformSize int, uniforms []driver.UniformLocation) {
func (u *uniformsTracker) setup(funcs *gl.Functions, p gl.Program, uniformSize int, uniforms []shader.UniformLocation) {
u.locs = make([]uniformLocation, len(uniforms))
for i, uniform := range uniforms {
u.locs[i] = lookupUniform(funcs, p, uniform)
@@ -1016,19 +1017,19 @@ func (p *uniformsTracker) update(funcs *gl.Functions) {
for _, u := range p.locs {
data := data[u.offset:]
switch {
case u.typ == driver.DataTypeFloat && u.size == 1:
case u.typ == shader.DataTypeFloat && u.size == 1:
data := data[:4]
v := *(*[1]float32)(unsafe.Pointer(&data[0]))
funcs.Uniform1f(u.uniform, v[0])
case u.typ == driver.DataTypeFloat && u.size == 2:
case u.typ == shader.DataTypeFloat && u.size == 2:
data := data[:8]
v := *(*[2]float32)(unsafe.Pointer(&data[0]))
funcs.Uniform2f(u.uniform, v[0], v[1])
case u.typ == driver.DataTypeFloat && u.size == 3:
case u.typ == shader.DataTypeFloat && u.size == 3:
data := data[:12]
v := *(*[3]float32)(unsafe.Pointer(&data[0]))
funcs.Uniform3f(u.uniform, v[0], v[1], v[2])
case u.typ == driver.DataTypeFloat && u.size == 4:
case u.typ == shader.DataTypeFloat && u.size == 4:
data := data[:16]
v := *(*[4]float32)(unsafe.Pointer(&data[0]))
funcs.Uniform4f(u.uniform, v[0], v[1], v[2], v[3])
@@ -1108,9 +1109,9 @@ func (b *Backend) setupVertexArrays() {
l := layout.layout[i]
var gltyp gl.Enum
switch l.Type {
case driver.DataTypeFloat:
case shader.DataTypeFloat:
gltyp = gl.FLOAT
case driver.DataTypeShort:
case shader.DataTypeShort:
gltyp = gl.SHORT
default:
panic("unsupported data type")