ci-platforms / windows amd64 smoke (push) Skipped
ci / release (push) Successful in 8m58s
ci-platforms / darwin amd64 smoke (push) Skipped
ci-platforms / darwin arm64 smoke (push) Skipped
ci / checks (push) Successful in 4m46s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 13m28s
118 lines
3.6 KiB
Go
118 lines
3.6 KiB
Go
//go:build js
|
|
|
|
package webgl2
|
|
|
|
import (
|
|
"slices"
|
|
"strings"
|
|
"syscall/js"
|
|
|
|
"aego/core/errs"
|
|
"aego/core/log"
|
|
"aego/gpu"
|
|
)
|
|
|
|
const glCompletionStatusKHR = 0x91B1
|
|
|
|
type compilation struct {
|
|
d *Device
|
|
desc gpu.ShaderDesc
|
|
program js.Value
|
|
stages []js.Value
|
|
generation uint64
|
|
done bool
|
|
result gpu.ShaderHandle
|
|
err error
|
|
}
|
|
|
|
// BeginShaderCompile submits and links stages without status queries; Poll uses KHR_parallel_shader_compile completion queries when available.
|
|
// Otherwise Poll synchronously falls back at load/reload; no background thread or universal asynchrony is implied.
|
|
func (d *Device) BeginShaderCompile(desc gpu.ShaderDesc) (gpu.ShaderCompilation, error) {
|
|
if d.lost || len(desc.Sources) == 0 {
|
|
return nil, errs.New(errs.GPUShaderLink, "cannot start shader compilation without a live context and stages", log.F("label", desc.Label))
|
|
}
|
|
job := &compilation{d: d, desc: gpu.ShaderDesc{Label: desc.Label, Sources: slices.Clone(desc.Sources)}, generation: d.contextGen}
|
|
job.program = d.call(d.f.CreateProgram)
|
|
if !job.program.Truthy() {
|
|
return nil, d.shaderCreationError(errs.GPUShaderLink, "createProgram", desc.Label)
|
|
}
|
|
for _, src := range desc.Sources {
|
|
stage := d.call(d.f.CreateShader, stageType(src.Stage))
|
|
if !stage.Truthy() {
|
|
job.Cancel()
|
|
return nil, d.shaderCreationError(errs.GPUShaderCompile, "createShader", desc.Label)
|
|
}
|
|
job.stages = append(job.stages, stage)
|
|
d.call(d.f.ShaderSource, stage, src.Source)
|
|
d.call(d.f.CompileShader, stage)
|
|
d.call(d.f.AttachShader, job.program, stage)
|
|
}
|
|
d.call(d.f.LinkProgram, job.program)
|
|
d.compiles[job] = struct{}{}
|
|
return job, nil
|
|
}
|
|
|
|
func (j *compilation) cleanup() {
|
|
d := j.d
|
|
if !d.lost && j.generation == d.contextGen {
|
|
for _, stage := range j.stages {
|
|
d.call(d.f.DetachShader, j.program, stage)
|
|
d.call(d.f.DeleteShader, stage)
|
|
}
|
|
}
|
|
j.stages = nil
|
|
delete(d.compiles, j)
|
|
}
|
|
|
|
func (j *compilation) Cancel() {
|
|
if j.done {
|
|
return
|
|
}
|
|
j.cleanup()
|
|
if !j.d.lost && j.generation == j.d.contextGen && j.program.Truthy() {
|
|
j.d.call(j.d.f.DeleteProgram, j.program)
|
|
}
|
|
j.program = js.Null()
|
|
j.done = true
|
|
j.err = errs.New(errs.GPUShaderLink, "shader candidate cancelled", log.F("label", j.desc.Label))
|
|
}
|
|
|
|
func (j *compilation) Poll() (gpu.ShaderHandle, bool, error) {
|
|
if j.done {
|
|
return j.result, true, j.err
|
|
}
|
|
d := j.d
|
|
if d.lost || j.generation != d.contextGen {
|
|
j.Cancel()
|
|
return gpu.ShaderHandle{}, true, j.err
|
|
}
|
|
if d.parallel.Truthy() && !d.call(d.f.GetProgramParameter, j.program, glCompletionStatusKHR).Truthy() {
|
|
return gpu.ShaderHandle{}, false, nil
|
|
}
|
|
linked := d.call(d.f.GetProgramParameter, j.program, glLinkStatus).Truthy()
|
|
if !linked {
|
|
for i, stage := range j.stages {
|
|
if !d.call(d.f.GetShaderParameter, stage, glCompileStatus).Truthy() {
|
|
info := strings.TrimSpace(d.call(d.f.GetShaderInfoLog, stage).String())
|
|
j.err = errs.New(errs.GPUShaderCompile, "shader compile failed", log.F("label", j.desc.Label), log.F("stage", j.desc.Sources[i].Stage.String()), log.F("log", info))
|
|
break
|
|
}
|
|
}
|
|
if j.err == nil {
|
|
j.err = errs.New(errs.GPUShaderLink, "shader link failed", log.F("label", j.desc.Label), log.F("log", strings.TrimSpace(d.call(d.f.GetProgramInfoLog, j.program).String())))
|
|
}
|
|
j.cleanup()
|
|
d.call(d.f.DeleteProgram, j.program)
|
|
j.program = js.Null()
|
|
j.done = true
|
|
return gpu.ShaderHandle{}, true, j.err
|
|
}
|
|
j.cleanup()
|
|
id := d.obj.put(j.program)
|
|
d.bindProgramSlots(j.program, id, j.desc.Label)
|
|
h, s := d.shaders.Alloc()
|
|
*s = glShader{program: id, desc: j.desc}
|
|
j.result, j.done = gpu.ShaderHandle(h), true
|
|
return j.result, true, nil
|
|
}
|