231 lines
5.4 KiB
Go
231 lines
5.4 KiB
Go
package runtime
|
|
|
|
import (
|
|
"cmp"
|
|
"slices"
|
|
"unsafe"
|
|
|
|
"aego/core/rewind"
|
|
"aego/scene"
|
|
"aego/script/coroutine"
|
|
"aego/script/signal"
|
|
"aego/script/vm"
|
|
)
|
|
|
|
var _ rewind.Keyframer = (*Runtime)(nil)
|
|
|
|
type typeKey struct {
|
|
vals []vm.Value
|
|
free []int
|
|
live int
|
|
}
|
|
|
|
type runtimeKey struct {
|
|
types []*scriptType
|
|
vals []typeKey
|
|
inst []instance
|
|
instFree []vm.InstanceID
|
|
nodes []scene.NodeHandle
|
|
counts []int32
|
|
ids []vm.InstanceID
|
|
update []vm.InstanceID
|
|
always []vm.InstanceID
|
|
whenPaused []vm.InstanceID
|
|
fixed []vm.InstanceID
|
|
ready []vm.InstanceID
|
|
spawn []scene.NodeHandle
|
|
evq []queued
|
|
evargs []vm.Value
|
|
machine vm.State
|
|
co coroutine.State
|
|
sig signal.State
|
|
elapsed float64
|
|
dirty bool
|
|
size int
|
|
}
|
|
|
|
func (k *runtimeKey) Size() int {
|
|
return k.size
|
|
}
|
|
|
|
func fitTypes(s []typeKey, n int) []typeKey {
|
|
if cap(s) < n {
|
|
s = append(s[:cap(s)], make([]typeKey, n-cap(s))...)
|
|
}
|
|
return s[:n]
|
|
}
|
|
|
|
func (r *Runtime) Capture(reuse rewind.Key) rewind.Key {
|
|
k, _ := reuse.(*runtimeKey)
|
|
if k == nil {
|
|
k = &runtimeKey{}
|
|
}
|
|
vsize := int(unsafe.Sizeof(vm.Value{}))
|
|
k.size = 0
|
|
k.types = append(k.types[:0], r.types...)
|
|
k.vals = fitTypes(k.vals, len(r.types))
|
|
for i, t := range r.types {
|
|
tk := &k.vals[i]
|
|
tk.vals = append(tk.vals[:0], t.vals...)
|
|
tk.free = append(tk.free[:0], t.free...)
|
|
tk.live = t.live
|
|
k.size += len(tk.vals)*vsize + 8*len(tk.free)
|
|
}
|
|
k.inst = append(k.inst[:0], r.inst...)
|
|
k.instFree = append(k.instFree[:0], r.instFree...)
|
|
k.nodes, k.counts, k.ids = k.nodes[:0], k.counts[:0], k.ids[:0]
|
|
for h, list := range r.byNode {
|
|
k.nodes = append(k.nodes, h)
|
|
k.counts = append(k.counts, int32(len(list)))
|
|
k.ids = append(k.ids, list...)
|
|
}
|
|
k.update = append(k.update[:0], r.update...)
|
|
k.always = append(k.always[:0], r.always...)
|
|
k.whenPaused = append(k.whenPaused[:0], r.whenPaused...)
|
|
k.fixed = append(k.fixed[:0], r.fixed...)
|
|
k.ready = append(k.ready[:0], r.ready...)
|
|
k.spawn = append(k.spawn[:0], r.spawn...)
|
|
k.evq = append(k.evq[:0], r.evq...)
|
|
k.evargs = append(k.evargs[:0], r.evargs...)
|
|
k.elapsed, k.dirty = r.elapsed, r.dirty
|
|
if r.m != nil {
|
|
r.m.Capture(&k.machine)
|
|
}
|
|
if r.co != nil {
|
|
r.co.Capture(&k.co)
|
|
}
|
|
if r.sig != nil {
|
|
r.sig.Capture(&k.sig)
|
|
}
|
|
k.size += int(unsafe.Sizeof(*k)) + len(k.inst)*int(unsafe.Sizeof(instance{})) +
|
|
4*(len(k.instFree)+len(k.ids)+len(k.counts)+len(k.update)+len(k.always)+len(k.whenPaused)+len(k.fixed)+len(k.ready)) +
|
|
(len(k.nodes)+len(k.spawn))*int(unsafe.Sizeof(scene.NodeHandle{})) +
|
|
len(k.evq)*int(unsafe.Sizeof(queued{})) + len(k.evargs)*vsize +
|
|
k.machine.Size() + k.co.Size() + k.sig.Size()
|
|
return k
|
|
}
|
|
|
|
func (r *Runtime) Restore(key rewind.Key) {
|
|
k := key.(*runtimeKey)
|
|
if !slices.Equal(k.types, r.types) {
|
|
r.log.Error("script state cannot be restored after the scripts were reloaded")
|
|
return
|
|
}
|
|
for i, t := range r.types {
|
|
tk := &k.vals[i]
|
|
t.vals = append(t.vals[:0], tk.vals...)
|
|
t.free = append(t.free[:0], tk.free...)
|
|
t.live = tk.live
|
|
}
|
|
r.inst = append(r.inst[:0], k.inst...)
|
|
r.instFree = append(r.instFree[:0], k.instFree...)
|
|
clear(r.byNode)
|
|
buf := slices.Clone(k.ids)
|
|
off := 0
|
|
for i, h := range k.nodes {
|
|
n := int(k.counts[i])
|
|
r.byNode[h] = buf[off : off+n : off+n]
|
|
off += n
|
|
}
|
|
r.update = append(r.update[:0], k.update...)
|
|
r.always = append(r.always[:0], k.always...)
|
|
r.whenPaused = append(r.whenPaused[:0], k.whenPaused...)
|
|
r.fixed = append(r.fixed[:0], k.fixed...)
|
|
r.ready = append(r.ready[:0], k.ready...)
|
|
r.readyRun = r.readyRun[:0]
|
|
r.spawn = append(r.spawn[:0], k.spawn...)
|
|
r.evq = append(r.evq[:0], k.evq...)
|
|
r.evargs = append(r.evargs[:0], k.evargs...)
|
|
r.elapsed, r.dirty = k.elapsed, k.dirty
|
|
r.argTop = 0
|
|
if r.m != nil {
|
|
r.m.Restore(&k.machine)
|
|
}
|
|
if r.co != nil {
|
|
r.co.Restore(&k.co)
|
|
}
|
|
if r.sig != nil {
|
|
r.sig.Restore(&k.sig)
|
|
}
|
|
}
|
|
|
|
func hashInstances(h *rewind.Hasher, list []vm.InstanceID) {
|
|
h.Int(len(list))
|
|
for _, id := range list {
|
|
h.U32(uint32(id))
|
|
}
|
|
}
|
|
|
|
func hashNodes(h *rewind.Hasher, list []scene.NodeHandle) {
|
|
h.Int(len(list))
|
|
for _, n := range list {
|
|
h.U32(n.Index)
|
|
h.U32(n.Gen)
|
|
}
|
|
}
|
|
|
|
func (r *Runtime) Hash(h *rewind.Hasher) {
|
|
h.String("script")
|
|
h.Int(len(r.inst))
|
|
for i := range r.inst {
|
|
in := &r.inst[i]
|
|
h.U64(uint64(in.state))
|
|
h.Int(in.base)
|
|
h.U32(in.node.Index)
|
|
h.U32(in.node.Gen)
|
|
if in.state == dead || in.base < 0 {
|
|
continue
|
|
}
|
|
h.String(in.typ.name)
|
|
vm.HashValues(h, in.typ.vals[in.base:in.base+in.typ.slots])
|
|
}
|
|
hashInstances(h, r.instFree)
|
|
for _, t := range r.types {
|
|
h.String(t.name)
|
|
h.Int(t.live)
|
|
h.Int(len(t.free))
|
|
for _, f := range t.free {
|
|
h.Int(f)
|
|
}
|
|
}
|
|
nodes := make([]scene.NodeHandle, 0, len(r.byNode))
|
|
for n := range r.byNode {
|
|
nodes = append(nodes, n)
|
|
}
|
|
slices.SortFunc(nodes, func(a, b scene.NodeHandle) int {
|
|
if c := cmp.Compare(a.Index, b.Index); c != 0 {
|
|
return c
|
|
}
|
|
return cmp.Compare(a.Gen, b.Gen)
|
|
})
|
|
for _, n := range nodes {
|
|
h.U32(n.Index)
|
|
h.U32(n.Gen)
|
|
hashInstances(h, r.byNode[n])
|
|
}
|
|
for _, l := range [...][]vm.InstanceID{r.update, r.always, r.whenPaused, r.fixed, r.ready} {
|
|
hashInstances(h, l)
|
|
}
|
|
hashNodes(h, r.spawn)
|
|
h.Int(len(r.evq))
|
|
for _, q := range r.evq {
|
|
h.U32(q.node.Index)
|
|
h.U32(q.node.Gen)
|
|
h.U32(uint32(q.ev))
|
|
h.U32(uint32(q.off))
|
|
h.U32(uint32(q.n))
|
|
}
|
|
vm.HashValues(h, r.evargs)
|
|
h.F64(r.elapsed)
|
|
h.Bool(r.dirty)
|
|
if r.m != nil {
|
|
r.m.Hash(h)
|
|
}
|
|
if r.co != nil {
|
|
r.co.Hash(h)
|
|
}
|
|
if r.sig != nil {
|
|
r.sig.Hash(h)
|
|
}
|
|
}
|