97 lines
3.1 KiB
Go
97 lines
3.1 KiB
Go
//go:build js
|
|
|
|
package webgl2
|
|
|
|
import (
|
|
"slices"
|
|
"strings"
|
|
"syscall/js"
|
|
|
|
"aego/core/errs"
|
|
"aego/core/log"
|
|
"aego/gpu"
|
|
"aego/gpu/internal/glstate"
|
|
)
|
|
|
|
func (d *Device) link(desc gpu.ShaderDesc) (js.Value, *errs.Error) {
|
|
prog := d.call(d.f.CreateProgram)
|
|
stages := make([]js.Value, 0, len(desc.Sources))
|
|
cleanup := func() {
|
|
for _, sh := range stages {
|
|
d.call(d.f.DetachShader, prog, sh)
|
|
d.call(d.f.DeleteShader, sh)
|
|
}
|
|
}
|
|
for _, src := range desc.Sources {
|
|
sh := d.call(d.f.CreateShader, stageType(src.Stage))
|
|
d.call(d.f.ShaderSource, sh, src.Source)
|
|
d.call(d.f.CompileShader, sh)
|
|
if !d.call(d.f.GetShaderParameter, sh, glCompileStatus).Truthy() {
|
|
info := strings.TrimSpace(d.call(d.f.GetShaderInfoLog, sh).String())
|
|
d.call(d.f.DeleteShader, sh)
|
|
cleanup()
|
|
d.call(d.f.DeleteProgram, prog)
|
|
return js.Null(), errs.New(errs.GPUShaderCompile, "shader compile failed",
|
|
log.F("label", desc.Label), log.F("stage", src.Stage.String()), log.F("log", info))
|
|
}
|
|
d.call(d.f.AttachShader, prog, sh)
|
|
stages = append(stages, sh)
|
|
}
|
|
d.call(d.f.LinkProgram, prog)
|
|
ok := d.call(d.f.GetProgramParameter, prog, glLinkStatus).Truthy()
|
|
cleanup()
|
|
if !ok {
|
|
info := strings.TrimSpace(d.call(d.f.GetProgramInfoLog, prog).String())
|
|
d.call(d.f.DeleteProgram, prog)
|
|
return js.Null(), errs.New(errs.GPUShaderLink, "shader link failed", log.F("label", desc.Label), log.F("log", info))
|
|
}
|
|
return prog, nil
|
|
}
|
|
|
|
func (d *Device) CreateShader(desc gpu.ShaderDesc) (gpu.ShaderHandle, error) {
|
|
if len(desc.Sources) == 0 {
|
|
return gpu.ShaderHandle{}, errs.New(errs.GPUShaderLink, "shader has no stages", log.F("label", desc.Label))
|
|
}
|
|
prog, err := d.link(desc)
|
|
if err != nil {
|
|
return gpu.ShaderHandle{}, err
|
|
}
|
|
id := d.obj.put(prog)
|
|
d.bindProgramSlots(prog, id, desc.Label)
|
|
h, s := d.shaders.Alloc()
|
|
*s = glShader{program: id, desc: gpu.ShaderDesc{Sources: slices.Clone(desc.Sources), Label: desc.Label}}
|
|
return gpu.ShaderHandle(h), nil
|
|
}
|
|
|
|
func (d *Device) DestroyShader(h gpu.ShaderHandle) {
|
|
d.destroy(kindShader, h.Index, h.Gen)
|
|
}
|
|
|
|
func (d *Device) bindProgramSlots(prog js.Value, id uint32, label string) {
|
|
blocks := num(d.call(d.f.GetProgramParameter, prog, glActiveUniformBlocks))
|
|
for i := 0; i < blocks; i++ {
|
|
block := d.call(d.f.GetActiveUniformBlockName, prog, i).String()
|
|
slot, ok := slotIndex(block, "ub")
|
|
if !ok || slot >= glstate.MaxUniformBuffers {
|
|
d.log.Warn("uniform block does not follow ubN naming", log.F("shader", label), log.F("block", block))
|
|
continue
|
|
}
|
|
d.call(d.f.UniformBlockBinding, prog, i, slot)
|
|
}
|
|
uniforms := num(d.call(d.f.GetProgramParameter, prog, glActiveUniforms))
|
|
d.cache.UseProgram(id)
|
|
for i := 0; i < uniforms; i++ {
|
|
info := d.call(d.f.GetActiveUniform, prog, i)
|
|
if !info.Truthy() || !isSamplerType(uint32(num(info.Get("type")))) {
|
|
continue
|
|
}
|
|
name := strings.TrimSuffix(info.Get("name").String(), "[0]")
|
|
unit, ok := slotIndex(name, "tex")
|
|
if !ok || unit >= scratchUnit {
|
|
d.log.Warn("sampler uniform does not follow texN naming", log.F("shader", label), log.F("uniform", name))
|
|
continue
|
|
}
|
|
d.call(d.f.Uniform1i, d.call(d.f.GetUniformLocation, prog, name), unit)
|
|
}
|
|
}
|