zworld/engine/renderapi/shader/util.go

88 lines
1.9 KiB
Go
Raw Permalink Normal View History

2024-01-14 22:56:06 +08:00
package shader
import (
"bytes"
"errors"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"unsafe"
"zworld/assets"
)
var ErrCompileFailed = errors.New("shader compilation error")
// Disgusting hack that reinterprets a byte slice as a slice of uint32
func sliceUint32(data []byte) []uint32 {
type sliceHeader struct {
Data uintptr
Len int
Cap int
}
const m = 0x7fffffff
return (*[m / 4]uint32)(unsafe.Pointer((*sliceHeader)(unsafe.Pointer(&data)).Data))[:len(data)/4]
}
func LoadOrCompile(path string, stage ShaderStage) ([]byte, error) {
spvPath := fmt.Sprintf("%s.spv", path)
source, err := assets.ReadAll(spvPath)
if errors.Is(err, os.ErrNotExist) {
return Compile(path, stage)
}
if err != nil {
return nil, err
}
log.Println("loading shader", path)
return source, nil
}
func Compile(path string, stage ShaderStage) ([]byte, error) {
stageflag := ""
switch stage {
case StageFragment:
stageflag = "-fshader-stage=fragment"
case StageVertex:
stageflag = "-fshader-stage=vertex"
case StageCompute:
stageflag = "-fshader-stage=compute"
}
source, err := assets.ReadAll(path)
if err != nil {
return nil, err
}
// todo: check for glslc
includePath := filepath.Join(assets.Path, "shaders")
bytecode := &bytes.Buffer{}
errors := &bytes.Buffer{}
args := []string{
stageflag,
"-O", // optimize SPIR-V
"-I", includePath, // include path
"-o", "-", // output file: standard out
"-", // input file: standard in
}
cmd := exec.Command("glslc", args...)
cmd.Stdin = bytes.NewBuffer(source)
cmd.Stdout = bytecode
cmd.Stderr = errors
cmd.Dir = assets.Path
if err := cmd.Run(); err != nil {
if errors.Len() > 0 {
return nil, fmt.Errorf("%w in %s:\n%s",
ErrCompileFailed,
path,
errors.String())
}
return nil, fmt.Errorf("%s in %s: %w", ErrCompileFailed, path, err)
}
log.Println("shader compiled successfully:", path)
return bytecode.Bytes(), nil
}