package graph import ( "fmt" "github.com/vkngwrapper/core/v2/core1_0" "zworld/engine/object" "zworld/engine/renderapi" "zworld/engine/renderapi/command" "zworld/engine/renderapi/sync" "zworld/engine/renderapi/vulkan" ) type NodePass interface { Name() string Record(command.Recorder, renderapi.Args, object.Component) Destroy() } type Node interface { After(nd Node, mask core1_0.PipelineStageFlags) Before(nd Node, mask core1_0.PipelineStageFlags, signal []sync.Semaphore) Requires() []Node Dependants() []Node Name() string Draw(command.Worker, renderapi.Args, object.Component) Detach(Node) Destroy() } type node struct { name string app vulkan.App pass NodePass after map[string]edge before map[string]edge requires []Node dependants []Node } type edge struct { node Node mask core1_0.PipelineStageFlags signal []sync.Semaphore } func newNode(app vulkan.App, name string, pass NodePass) *node { return &node{ app: app, name: name, pass: pass, after: make(map[string]edge, 4), before: make(map[string]edge, 4), requires: make([]Node, 0, 4), dependants: make([]Node, 0, 4), } } func (n *node) Requires() []Node { return n.requires } func (n *node) Dependants() []Node { return n.dependants } func (n *node) After(nd Node, mask core1_0.PipelineStageFlags) { if _, exists := n.after[nd.Name()]; exists { return } signal := sync.NewSemaphoreArray(n.app.Device(), fmt.Sprintf("%s->%s", nd.Name(), n.name), 3) n.after[nd.Name()] = edge{ node: nd, mask: mask, signal: signal, } nd.Before(n, mask, signal) n.refresh() } func (n *node) Before(nd Node, mask core1_0.PipelineStageFlags, signal []sync.Semaphore) { if _, exists := n.before[nd.Name()]; exists { return } n.before[nd.Name()] = edge{ node: nd, mask: mask, signal: signal, } nd.After(n, mask) n.refresh() } func (n *node) refresh() { // recompute signals n.dependants = make([]Node, 0, len(n.after)) for _, edge := range n.before { n.dependants = append(n.dependants, edge.node) } // recompute waits n.requires = make([]Node, 0, len(n.after)) for _, edge := range n.after { if edge.signal == nil { // skip nil signals continue } n.requires = append(n.requires, edge.node) } } func (n *node) Detach(nd Node) { if _, exists := n.before[nd.Name()]; exists { delete(n.before, nd.Name()) nd.Detach(n) } if edge, exists := n.after[nd.Name()]; exists { delete(n.after, nd.Name()) nd.Detach(n) // free semaphores for _, signal := range edge.signal { signal.Destroy() } } n.refresh() } func (n *node) Name() string { return n.name } func (n *node) Destroy() { for _, edge := range n.before { before := edge.node before.Detach(n) for _, s := range edge.signal { s.Destroy() } } for _, edge := range n.after { after := edge.node after.Detach(n) } if n.pass != nil { n.pass.Destroy() n.pass = nil } n.before = nil n.after = nil } func (n *node) waits(index int) []command.Wait { waits := make([]command.Wait, 0, len(n.after)) for _, after := range n.after { if after.signal == nil { // skip nil signals continue } waits = append(waits, command.Wait{ Semaphore: after.signal[index], Mask: after.mask, }) } return waits } func (n *node) signals(index int) []sync.Semaphore { signals := make([]sync.Semaphore, 0, len(n.before)) for _, edge := range n.before { signals = append(signals, edge.signal[index]) } return signals } func (n *node) Draw(worker command.Worker, args renderapi.Args, scene object.Component) { if n.pass == nil { return } cmds := command.NewRecorder() n.pass.Record(cmds, args, scene) worker.Queue(cmds.Apply) worker.Submit(command.SubmitInfo{ Marker: n.pass.Name(), Wait: n.waits(args.Frame), Signal: n.signals(args.Frame), }) }