diff --git a/ui/app/internal/gpu/gpu.go b/ui/app/internal/gpu/gpu.go index 9f9f6bd0..9f2a854c 100644 --- a/ui/app/internal/gpu/gpu.go +++ b/ui/app/internal/gpu/gpu.go @@ -667,7 +667,7 @@ func (d *drawOps) newPathOp() *pathOp { func (d *drawOps) collectOps(r *ui.OpsReader, state drawState) int { loop: for { - data, ok := r.Decode() + data, refs, ok := r.Decode() if !ok { break } @@ -678,7 +678,7 @@ loop: state.t = state.t.Mul(op.Transform) case ops.TypeClip: var op gdraw.OpClip - op.Decode(data, r.Refs) + op.Decode(data, refs) if op.Path == nil { state.clip = f32.Rectangle{} continue @@ -702,17 +702,17 @@ loop: } case ops.TypeColor: var op gdraw.OpColor - op.Decode(data, r.Refs) + op.Decode(data, refs) state.img = nil state.color = op.Col case ops.TypeImage: var op gdraw.OpImage - op.Decode(data, r.Refs) + op.Decode(data, refs) state.img = op.Img state.imgRect = op.Rect case ops.TypeDraw: var op gdraw.OpDraw - op.Decode(data, r.Refs) + op.Decode(data, refs) off := state.t.Transform(f32.Point{}) clip := state.clip.Intersect(op.Rect.Add(off)) if clip.Empty() { diff --git a/ui/app/window.go b/ui/app/window.go index 60365c52..74c0f152 100644 --- a/ui/app/window.go +++ b/ui/app/window.go @@ -156,7 +156,7 @@ func collectRedraws(r *ui.OpsReader) (time.Time, bool) { var t time.Time redraw := false for { - data, ok := r.Decode() + data, _, ok := r.Decode() if !ok { break } diff --git a/ui/draw/draw.go b/ui/draw/draw.go index e3e3dc09..92690d21 100644 --- a/ui/draw/draw.go +++ b/ui/draw/draw.go @@ -31,13 +31,11 @@ func (i OpImage) Add(o *ui.Ops) { data := make([]byte, ops.TypeImageLen) data[0] = byte(ops.TypeImage) bo := binary.LittleEndian - ref := o.Ref(i.Img) - bo.PutUint32(data[1:], uint32(ref)) - bo.PutUint32(data[5:], uint32(i.Rect.Min.X)) - bo.PutUint32(data[9:], uint32(i.Rect.Min.Y)) - bo.PutUint32(data[13:], uint32(i.Rect.Max.X)) - bo.PutUint32(data[17:], uint32(i.Rect.Max.Y)) - o.Write(data) + bo.PutUint32(data[1:], uint32(i.Rect.Min.X)) + bo.PutUint32(data[5:], uint32(i.Rect.Min.Y)) + bo.PutUint32(data[9:], uint32(i.Rect.Max.X)) + bo.PutUint32(data[13:], uint32(i.Rect.Max.Y)) + o.Write(data, []interface{}{i.Img}) } func (i *OpImage) Decode(data []byte, refs []interface{}) { @@ -45,19 +43,18 @@ func (i *OpImage) Decode(data []byte, refs []interface{}) { if ops.OpType(data[0]) != ops.TypeImage { panic("invalid op") } - ref := int(bo.Uint32(data[1:])) sr := image.Rectangle{ Min: image.Point{ - X: int(bo.Uint32(data[5:])), - Y: int(bo.Uint32(data[9:])), + X: int(bo.Uint32(data[1:])), + Y: int(bo.Uint32(data[5:])), }, Max: image.Point{ - X: int(bo.Uint32(data[13:])), - Y: int(bo.Uint32(data[17:])), + X: int(bo.Uint32(data[9:])), + Y: int(bo.Uint32(data[13:])), }, } *i = OpImage{ - Img: refs[ref].(image.Image), + Img: refs[0].(image.Image), Rect: sr, } } @@ -69,7 +66,7 @@ func (c OpColor) Add(o *ui.Ops) { data[2] = c.Col.G data[3] = c.Col.B data[4] = c.Col.A - o.Write(data) + o.Write(data, nil) } func (c *OpColor) Decode(data []byte, refs []interface{}) { @@ -94,7 +91,7 @@ func (d OpDraw) Add(o *ui.Ops) { bo.PutUint32(data[5:], math.Float32bits(d.Rect.Min.Y)) bo.PutUint32(data[9:], math.Float32bits(d.Rect.Max.X)) bo.PutUint32(data[13:], math.Float32bits(d.Rect.Max.Y)) - o.Write(data) + o.Write(data, nil) } func (d *OpDraw) Decode(data []byte, refs []interface{}) { diff --git a/ui/draw/path.go b/ui/draw/path.go index 9a0758a8..dbbb7812 100644 --- a/ui/draw/path.go +++ b/ui/draw/path.go @@ -3,7 +3,6 @@ package draw import ( - "encoding/binary" "math" "gioui.org/ui" @@ -37,20 +36,15 @@ func (p *Path) Data() interface{} { func (c OpClip) Add(o *ui.Ops) { data := make([]byte, ops.TypeClipLen) data[0] = byte(ops.TypeClip) - bo := binary.LittleEndian - ref := o.Ref(c.Path) - bo.PutUint32(data[1:], uint32(ref)) - o.Write(data) + o.Write(data, []interface{}{c.Path}) } func (c *OpClip) Decode(d []byte, refs []interface{}) { - bo := binary.LittleEndian if ops.OpType(d[0]) != ops.TypeClip { panic("invalid op") } - ref := int(bo.Uint32(d[1:])) *c = OpClip{ - Path: refs[ref].(*Path), + Path: refs[0].(*Path), } } diff --git a/ui/internal/ops/ops.go b/ui/internal/ops/ops.go index 481f81aa..b0fa805c 100644 --- a/ui/internal/ops/ops.go +++ b/ui/internal/ops/ops.go @@ -2,8 +2,11 @@ package ops type OpType byte +// Start at a high number for easier debugging. +const FirstOpIndex = 200 + const ( - TypeBlockDef OpType = iota + TypeBlockDef OpType = iota + FirstOpIndex TypeBlock TypeTransform TypeLayer @@ -20,18 +23,33 @@ const ( ) const ( - TypeBlockDefLen = 1 + 4 - TypeBlockLen = 1 + 4 + TypeBlockDefLen = 1 + 4 + 4 + TypeBlockLen = 1 + 4 + 4 TypeTransformLen = 1 + 4*2 TypeLayerLen = 1 TypeRedrawLen = 1 + 8 - TypeClipLen = 1 + 4 - TypeImageLen = 1 + 4 + 4*4 + TypeClipLen = 1 + TypeImageLen = 1 + 4*4 TypeDrawLen = 1 + 4*4 TypeColorLen = 1 + 4 - TypePointerHandlerLen = 1 + 4 + 4 + 1 - TypeKeyHandlerLen = 1 + 4 + 1 + TypePointerHandlerLen = 1 + 1 + TypeKeyHandlerLen = 1 + 1 TypeHideInputLen = 1 TypePushLen = 1 TypePopLen = 1 + + TypeBlockDefRefs = 0 + TypeBlockRefs = 0 + TypeTransformRefs = 0 + TypeLayerRefs = 0 + TypeRedrawRefs = 0 + TypeClipRefs = 1 + TypeImageRefs = 1 + TypeDrawRefs = 0 + TypeColorRefs = 0 + TypePointerHandlerRefs = 2 + TypeKeyHandlerRefs = 1 + TypeHideInputRefs = 0 + TypePushRefs = 0 + TypePopRefs = 0 ) diff --git a/ui/key/key.go b/ui/key/key.go index e8e0c393..128525a5 100644 --- a/ui/key/key.go +++ b/ui/key/key.go @@ -3,8 +3,6 @@ package key import ( - "encoding/binary" - "gioui.org/ui" "gioui.org/ui/internal/ops" ) @@ -73,30 +71,26 @@ const ( func (h OpHandler) Add(o *ui.Ops) { data := make([]byte, ops.TypeKeyHandlerLen) data[0] = byte(ops.TypeKeyHandler) - bo := binary.LittleEndian if h.Focus { data[1] = 1 } - bo.PutUint32(data[2:], uint32(o.Ref(h.Key))) - o.Write(data) + o.Write(data, []interface{}{h.Key}) } func (h *OpHandler) Decode(d []byte, refs []interface{}) { - bo := binary.LittleEndian if ops.OpType(d[0]) != ops.TypeKeyHandler { panic("invalid op") } - key := int(bo.Uint32(d[2:])) *h = OpHandler{ Focus: d[1] != 0, - Key: refs[key].(Key), + Key: refs[0].(Key), } } func (h OpHideInput) Add(o *ui.Ops) { data := make([]byte, ops.TypeHideInputLen) data[0] = byte(ops.TypeHideInput) - o.Write(data) + o.Write(data, nil) } func (Edit) ImplementsEvent() {} diff --git a/ui/key/queue.go b/ui/key/queue.go index 2727b3eb..cd0582f0 100644 --- a/ui/key/queue.go +++ b/ui/key/queue.go @@ -80,14 +80,14 @@ func resolveFocus(r *ui.OpsReader, focus Key) (Key, listenerPriority, bool) { var hide bool loop: for { - data, ok := r.Decode() + data, refs, ok := r.Decode() if !ok { break } switch ops.OpType(data[0]) { case ops.TypeKeyHandler: var op OpHandler - op.Decode(data, r.Refs) + op.Decode(data, refs) var newPri listenerPriority switch { case op.Focus: diff --git a/ui/ops.go b/ui/ops.go index 3c2ec380..1de22759 100644 --- a/ui/ops.go +++ b/ui/ops.go @@ -9,7 +9,7 @@ import ( // Ops holds a list of serialized Ops. type Ops struct { // Stack of block start indices. - stack []int + stack []pc // Serialized ops. data []byte // Op references. @@ -17,17 +17,22 @@ type Ops struct { } type OpsReader struct { - pc int + pc pc stack []block - Refs []interface{} + refs []interface{} data []byte pseudoOp [1]byte } type block struct { - retPC int - endPC int + retPC pc + endPC pc +} + +type pc struct { + data int + refs int } var typeLengths = [...]int{ @@ -47,16 +52,51 @@ var typeLengths = [...]int{ ops.TypePopLen, } +var refLengths = [...]int{ + ops.TypeBlockDefRefs, + ops.TypeBlockRefs, + ops.TypeTransformRefs, + ops.TypeLayerRefs, + ops.TypeRedrawRefs, + ops.TypeClipRefs, + ops.TypeImageRefs, + ops.TypeDrawRefs, + ops.TypeColorRefs, + ops.TypePointerHandlerRefs, + ops.TypeKeyHandlerRefs, + ops.TypeHideInputRefs, + ops.TypePushRefs, + ops.TypePopRefs, +} + type OpBlock struct { - idx int + pc pc +} + +type opBlockDef struct { + endpc pc } // Begin a block of ops. func (o *Ops) Begin() { - o.stack = append(o.stack, o.Size()) - data := make([]byte, ops.TypeBlockDefLen) - data[0] = byte(ops.TypeBlockDef) - o.Write(data) + o.stack = append(o.stack, o.pc()) + // Make room for a block definition. Filled out in End. + o.data = append(o.data, make([]byte, ops.TypeBlockDefLen)...) +} + +func (op *opBlockDef) decode(data []byte) { + if ops.OpType(data[0]) != ops.TypeBlockDef { + panic("invalid op") + } + bo := binary.LittleEndian + dataIdx := int(bo.Uint32(data[1:])) + refsIdx := int(bo.Uint32(data[5:])) + *op = opBlockDef{ + endpc: pc{ + data: dataIdx, + refs: refsIdx, + }, + } } // End the most recent block and return @@ -64,9 +104,13 @@ func (o *Ops) Begin() { func (o *Ops) End() OpBlock { start := o.stack[len(o.stack)-1] o.stack = o.stack[:len(o.stack)-1] - blockLen := o.Size() - start + pc := o.pc() + // Write the block header reserved in Begin. + data := o.data[start.data : start.data+ops.TypeBlockDefLen] + data[0] = byte(ops.TypeBlockDef) bo := binary.LittleEndian - bo.PutUint32(o.data[start+1:], uint32(blockLen)) + bo.PutUint32(data[1:], uint32(pc.data)) + bo.PutUint32(data[5:], uint32(pc.refs)) return OpBlock{start} } @@ -77,41 +121,51 @@ func (o *Ops) Reset() { o.data = o.data[:0] } -func (o *Ops) Ref(r interface{}) int { - o.refs = append(o.refs, r) - return len(o.refs) - 1 -} - -func (o *Ops) Write(op []byte) { +func (o *Ops) Write(op []byte, refs []interface{}) { o.data = append(o.data, op...) + o.refs = append(o.refs, refs...) } -// Size returns the length of the serialized Op data. -func (o *Ops) Size() int { - return len(o.data) +func (o *Ops) pc() pc { + return pc{data: len(o.data), refs: len(o.refs)} +} + +func (b *OpBlock) decode(data []byte) { + if ops.OpType(data[0]) != ops.TypeBlock { + panic("invalid op") + } + bo := binary.LittleEndian + dataIdx := int(bo.Uint32(data[1:])) + refsIdx := int(bo.Uint32(data[5:])) + *b = OpBlock{ + pc: pc{ + data: dataIdx, + refs: refsIdx, + }, + } } func (b OpBlock) Add(o *Ops) { data := make([]byte, ops.TypeBlockLen) data[0] = byte(ops.TypeBlock) bo := binary.LittleEndian - bo.PutUint32(data[1:], uint32(b.idx)) - o.Write(data) + bo.PutUint32(data[1:], uint32(b.pc.data)) + bo.PutUint32(data[5:], uint32(b.pc.refs)) + o.Write(data, nil) } // Reset start reading from the op list. func (r *OpsReader) Reset(ops *Ops) { - r.Refs = ops.refs + r.refs = ops.refs r.data = ops.data r.stack = r.stack[:0] - r.pc = 0 + r.pc = pc{} } -func (r *OpsReader) Decode() ([]byte, bool) { - bo := binary.LittleEndian +func (r *OpsReader) Decode() ([]byte, []interface{}, bool) { for { - if r.pc == len(r.data) { - return nil, false + if r.pc.data == len(r.data) { + return nil, nil, false } if len(r.stack) > 0 { b := r.stack[len(r.stack)-1] @@ -119,28 +173,40 @@ func (r *OpsReader) Decode() ([]byte, bool) { r.pc = b.retPC r.stack = r.stack[:len(r.stack)-1] r.pseudoOp[0] = byte(ops.TypePop) - return r.pseudoOp[:], true + return r.pseudoOp[:], nil, true } } - t := ops.OpType(r.data[r.pc]) - n := typeLengths[t] - data := r.data[r.pc : r.pc+n] + t := ops.OpType(r.data[r.pc.data]) + n := typeLengths[t-ops.FirstOpIndex] + nrefs := refLengths[t-ops.FirstOpIndex] + data := r.data[r.pc.data : r.pc.data+n] + refs := r.refs[r.pc.refs : r.pc.refs+nrefs] switch t { case ops.TypeBlock: - blockIdx := int(bo.Uint32(data[1:])) - if ops.OpType(r.data[blockIdx]) != ops.TypeBlockDef { + var op OpBlock + op.decode(data) + if ops.OpType(r.data[op.pc.data]) != ops.TypeBlockDef { panic("invalid block reference") } - blockLen := int(bo.Uint32(r.data[blockIdx+1:])) - r.stack = append(r.stack, block{r.pc + n, blockIdx + blockLen}) - r.pc = blockIdx + ops.TypeBlockDefLen + var opDef opBlockDef + opDef.decode(r.data[op.pc.data : op.pc.data+ops.TypeBlockDefLen]) + retPC := r.pc + retPC.data += n + retPC.refs += nrefs + r.stack = append(r.stack, block{retPC: retPC, endPC: opDef.endpc}) + r.pc = op.pc + r.pc.data += ops.TypeBlockDefLen + r.pc.refs += ops.TypeBlockDefRefs r.pseudoOp[0] = byte(ops.TypePush) - return r.pseudoOp[:], true + return r.pseudoOp[:], nil, true case ops.TypeBlockDef: - r.pc += int(bo.Uint32(data[1:])) + var op opBlockDef + op.decode(data) + r.pc = op.endpc continue } - r.pc += n - return data, true + r.pc.data += n + r.pc.refs += nrefs + return data, refs, true } } diff --git a/ui/pointer/pointer.go b/ui/pointer/pointer.go index 013824ac..9ff9062a 100644 --- a/ui/pointer/pointer.go +++ b/ui/pointer/pointer.go @@ -3,7 +3,6 @@ package pointer import ( - "encoding/binary" "time" "gioui.org/ui" @@ -72,26 +71,20 @@ const ( func (h OpHandler) Add(o *ui.Ops) { data := make([]byte, ops.TypePointerHandlerLen) data[0] = byte(ops.TypePointerHandler) - bo := binary.LittleEndian if h.Grab { data[1] = 1 } - bo.PutUint32(data[2:], uint32(o.Ref(h.Key))) - bo.PutUint32(data[6:], uint32(o.Ref(h.Area))) - o.Write(data) + o.Write(data, []interface{}{h.Key, h.Area}) } func (h *OpHandler) Decode(d []byte, refs []interface{}) { - bo := binary.LittleEndian if ops.OpType(d[0]) != ops.TypePointerHandler { panic("invalid op") } - key := int(bo.Uint32(d[2:])) - area := int(bo.Uint32(d[6:])) *h = OpHandler{ Grab: d[1] != 0, - Key: refs[key].(Key), - Area: refs[area].(Area), + Key: refs[0].(Key), + Area: refs[1].(Area), } } diff --git a/ui/pointer/queue.go b/ui/pointer/queue.go index ef22218a..2299bf27 100644 --- a/ui/pointer/queue.go +++ b/ui/pointer/queue.go @@ -39,7 +39,7 @@ type handler struct { func (q *Queue) collectHandlers(r *ui.OpsReader, t ui.Transform, layer int) { for { - data, ok := r.Decode() + data, refs, ok := r.Decode() if !ok { return } @@ -57,7 +57,7 @@ func (q *Queue) collectHandlers(r *ui.OpsReader, t ui.Transform, layer int) { t = t.Mul(op.Transform) case ops.TypePointerHandler: var op OpHandler - op.Decode(data, r.Refs) + op.Decode(data, refs) q.hitTree = append(q.hitTree, hitNode{level: layer, key: op.Key}) h, ok := q.handlers[op.Key] if !ok { diff --git a/ui/ui.go b/ui/ui.go index 7a0592aa..c31ec3ca 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -77,7 +77,7 @@ func (r OpRedraw) Add(o *Ops) { bo.PutUint64(data[1:], uint64(nanos)) } } - o.Write(data) + o.Write(data, nil) } func (r *OpRedraw) Decode(d []byte) { @@ -110,7 +110,7 @@ func (t OpTransform) Add(o *Ops) { bo := binary.LittleEndian bo.PutUint32(data[1:], math.Float32bits(t.Transform.offset.X)) bo.PutUint32(data[5:], math.Float32bits(t.Transform.offset.Y)) - o.Write(data) + o.Write(data, nil) } func (t *OpTransform) Decode(d []byte) { @@ -129,7 +129,7 @@ func (t *OpTransform) Decode(d []byte) { func (l OpLayer) Add(o *Ops) { data := make([]byte, ops.TypeLayerLen) data[0] = byte(ops.TypeLayer) - o.Write(data) + o.Write(data, nil) } func (l *OpLayer) Decode(d []byte) {