ui: let OpsReader keep track of references

Instead of exposing the entire reference slice, return the relevant
references from Next.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2019-06-01 20:48:36 +02:00
parent 5966aab77e
commit 0d2cffe196
11 changed files with 166 additions and 104 deletions
+5 -5
View File
@@ -667,7 +667,7 @@ func (d *drawOps) newPathOp() *pathOp {
func (d *drawOps) collectOps(r *ui.OpsReader, state drawState) int { func (d *drawOps) collectOps(r *ui.OpsReader, state drawState) int {
loop: loop:
for { for {
data, ok := r.Decode() data, refs, ok := r.Decode()
if !ok { if !ok {
break break
} }
@@ -678,7 +678,7 @@ loop:
state.t = state.t.Mul(op.Transform) state.t = state.t.Mul(op.Transform)
case ops.TypeClip: case ops.TypeClip:
var op gdraw.OpClip var op gdraw.OpClip
op.Decode(data, r.Refs) op.Decode(data, refs)
if op.Path == nil { if op.Path == nil {
state.clip = f32.Rectangle{} state.clip = f32.Rectangle{}
continue continue
@@ -702,17 +702,17 @@ loop:
} }
case ops.TypeColor: case ops.TypeColor:
var op gdraw.OpColor var op gdraw.OpColor
op.Decode(data, r.Refs) op.Decode(data, refs)
state.img = nil state.img = nil
state.color = op.Col state.color = op.Col
case ops.TypeImage: case ops.TypeImage:
var op gdraw.OpImage var op gdraw.OpImage
op.Decode(data, r.Refs) op.Decode(data, refs)
state.img = op.Img state.img = op.Img
state.imgRect = op.Rect state.imgRect = op.Rect
case ops.TypeDraw: case ops.TypeDraw:
var op gdraw.OpDraw var op gdraw.OpDraw
op.Decode(data, r.Refs) op.Decode(data, refs)
off := state.t.Transform(f32.Point{}) off := state.t.Transform(f32.Point{})
clip := state.clip.Intersect(op.Rect.Add(off)) clip := state.clip.Intersect(op.Rect.Add(off))
if clip.Empty() { if clip.Empty() {
+1 -1
View File
@@ -156,7 +156,7 @@ func collectRedraws(r *ui.OpsReader) (time.Time, bool) {
var t time.Time var t time.Time
redraw := false redraw := false
for { for {
data, ok := r.Decode() data, _, ok := r.Decode()
if !ok { if !ok {
break break
} }
+12 -15
View File
@@ -31,13 +31,11 @@ func (i OpImage) Add(o *ui.Ops) {
data := make([]byte, ops.TypeImageLen) data := make([]byte, ops.TypeImageLen)
data[0] = byte(ops.TypeImage) data[0] = byte(ops.TypeImage)
bo := binary.LittleEndian bo := binary.LittleEndian
ref := o.Ref(i.Img) bo.PutUint32(data[1:], uint32(i.Rect.Min.X))
bo.PutUint32(data[1:], uint32(ref)) bo.PutUint32(data[5:], uint32(i.Rect.Min.Y))
bo.PutUint32(data[5:], uint32(i.Rect.Min.X)) bo.PutUint32(data[9:], uint32(i.Rect.Max.X))
bo.PutUint32(data[9:], uint32(i.Rect.Min.Y)) bo.PutUint32(data[13:], uint32(i.Rect.Max.Y))
bo.PutUint32(data[13:], uint32(i.Rect.Max.X)) o.Write(data, []interface{}{i.Img})
bo.PutUint32(data[17:], uint32(i.Rect.Max.Y))
o.Write(data)
} }
func (i *OpImage) Decode(data []byte, refs []interface{}) { 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 { if ops.OpType(data[0]) != ops.TypeImage {
panic("invalid op") panic("invalid op")
} }
ref := int(bo.Uint32(data[1:]))
sr := image.Rectangle{ sr := image.Rectangle{
Min: image.Point{ Min: image.Point{
X: int(bo.Uint32(data[5:])), X: int(bo.Uint32(data[1:])),
Y: int(bo.Uint32(data[9:])), Y: int(bo.Uint32(data[5:])),
}, },
Max: image.Point{ Max: image.Point{
X: int(bo.Uint32(data[13:])), X: int(bo.Uint32(data[9:])),
Y: int(bo.Uint32(data[17:])), Y: int(bo.Uint32(data[13:])),
}, },
} }
*i = OpImage{ *i = OpImage{
Img: refs[ref].(image.Image), Img: refs[0].(image.Image),
Rect: sr, Rect: sr,
} }
} }
@@ -69,7 +66,7 @@ func (c OpColor) Add(o *ui.Ops) {
data[2] = c.Col.G data[2] = c.Col.G
data[3] = c.Col.B data[3] = c.Col.B
data[4] = c.Col.A data[4] = c.Col.A
o.Write(data) o.Write(data, nil)
} }
func (c *OpColor) Decode(data []byte, refs []interface{}) { 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[5:], math.Float32bits(d.Rect.Min.Y))
bo.PutUint32(data[9:], math.Float32bits(d.Rect.Max.X)) bo.PutUint32(data[9:], math.Float32bits(d.Rect.Max.X))
bo.PutUint32(data[13:], math.Float32bits(d.Rect.Max.Y)) 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{}) { func (d *OpDraw) Decode(data []byte, refs []interface{}) {
+2 -8
View File
@@ -3,7 +3,6 @@
package draw package draw
import ( import (
"encoding/binary"
"math" "math"
"gioui.org/ui" "gioui.org/ui"
@@ -37,20 +36,15 @@ func (p *Path) Data() interface{} {
func (c OpClip) Add(o *ui.Ops) { func (c OpClip) Add(o *ui.Ops) {
data := make([]byte, ops.TypeClipLen) data := make([]byte, ops.TypeClipLen)
data[0] = byte(ops.TypeClip) data[0] = byte(ops.TypeClip)
bo := binary.LittleEndian o.Write(data, []interface{}{c.Path})
ref := o.Ref(c.Path)
bo.PutUint32(data[1:], uint32(ref))
o.Write(data)
} }
func (c *OpClip) Decode(d []byte, refs []interface{}) { func (c *OpClip) Decode(d []byte, refs []interface{}) {
bo := binary.LittleEndian
if ops.OpType(d[0]) != ops.TypeClip { if ops.OpType(d[0]) != ops.TypeClip {
panic("invalid op") panic("invalid op")
} }
ref := int(bo.Uint32(d[1:]))
*c = OpClip{ *c = OpClip{
Path: refs[ref].(*Path), Path: refs[0].(*Path),
} }
} }
+25 -7
View File
@@ -2,8 +2,11 @@ package ops
type OpType byte type OpType byte
// Start at a high number for easier debugging.
const FirstOpIndex = 200
const ( const (
TypeBlockDef OpType = iota TypeBlockDef OpType = iota + FirstOpIndex
TypeBlock TypeBlock
TypeTransform TypeTransform
TypeLayer TypeLayer
@@ -20,18 +23,33 @@ const (
) )
const ( const (
TypeBlockDefLen = 1 + 4 TypeBlockDefLen = 1 + 4 + 4
TypeBlockLen = 1 + 4 TypeBlockLen = 1 + 4 + 4
TypeTransformLen = 1 + 4*2 TypeTransformLen = 1 + 4*2
TypeLayerLen = 1 TypeLayerLen = 1
TypeRedrawLen = 1 + 8 TypeRedrawLen = 1 + 8
TypeClipLen = 1 + 4 TypeClipLen = 1
TypeImageLen = 1 + 4 + 4*4 TypeImageLen = 1 + 4*4
TypeDrawLen = 1 + 4*4 TypeDrawLen = 1 + 4*4
TypeColorLen = 1 + 4 TypeColorLen = 1 + 4
TypePointerHandlerLen = 1 + 4 + 4 + 1 TypePointerHandlerLen = 1 + 1
TypeKeyHandlerLen = 1 + 4 + 1 TypeKeyHandlerLen = 1 + 1
TypeHideInputLen = 1 TypeHideInputLen = 1
TypePushLen = 1 TypePushLen = 1
TypePopLen = 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
) )
+3 -9
View File
@@ -3,8 +3,6 @@
package key package key
import ( import (
"encoding/binary"
"gioui.org/ui" "gioui.org/ui"
"gioui.org/ui/internal/ops" "gioui.org/ui/internal/ops"
) )
@@ -73,30 +71,26 @@ const (
func (h OpHandler) Add(o *ui.Ops) { func (h OpHandler) Add(o *ui.Ops) {
data := make([]byte, ops.TypeKeyHandlerLen) data := make([]byte, ops.TypeKeyHandlerLen)
data[0] = byte(ops.TypeKeyHandler) data[0] = byte(ops.TypeKeyHandler)
bo := binary.LittleEndian
if h.Focus { if h.Focus {
data[1] = 1 data[1] = 1
} }
bo.PutUint32(data[2:], uint32(o.Ref(h.Key))) o.Write(data, []interface{}{h.Key})
o.Write(data)
} }
func (h *OpHandler) Decode(d []byte, refs []interface{}) { func (h *OpHandler) Decode(d []byte, refs []interface{}) {
bo := binary.LittleEndian
if ops.OpType(d[0]) != ops.TypeKeyHandler { if ops.OpType(d[0]) != ops.TypeKeyHandler {
panic("invalid op") panic("invalid op")
} }
key := int(bo.Uint32(d[2:]))
*h = OpHandler{ *h = OpHandler{
Focus: d[1] != 0, Focus: d[1] != 0,
Key: refs[key].(Key), Key: refs[0].(Key),
} }
} }
func (h OpHideInput) Add(o *ui.Ops) { func (h OpHideInput) Add(o *ui.Ops) {
data := make([]byte, ops.TypeHideInputLen) data := make([]byte, ops.TypeHideInputLen)
data[0] = byte(ops.TypeHideInput) data[0] = byte(ops.TypeHideInput)
o.Write(data) o.Write(data, nil)
} }
func (Edit) ImplementsEvent() {} func (Edit) ImplementsEvent() {}
+2 -2
View File
@@ -80,14 +80,14 @@ func resolveFocus(r *ui.OpsReader, focus Key) (Key, listenerPriority, bool) {
var hide bool var hide bool
loop: loop:
for { for {
data, ok := r.Decode() data, refs, ok := r.Decode()
if !ok { if !ok {
break break
} }
switch ops.OpType(data[0]) { switch ops.OpType(data[0]) {
case ops.TypeKeyHandler: case ops.TypeKeyHandler:
var op OpHandler var op OpHandler
op.Decode(data, r.Refs) op.Decode(data, refs)
var newPri listenerPriority var newPri listenerPriority
switch { switch {
case op.Focus: case op.Focus:
+108 -42
View File
@@ -9,7 +9,7 @@ import (
// Ops holds a list of serialized Ops. // Ops holds a list of serialized Ops.
type Ops struct { type Ops struct {
// Stack of block start indices. // Stack of block start indices.
stack []int stack []pc
// Serialized ops. // Serialized ops.
data []byte data []byte
// Op references. // Op references.
@@ -17,17 +17,22 @@ type Ops struct {
} }
type OpsReader struct { type OpsReader struct {
pc int pc pc
stack []block stack []block
Refs []interface{} refs []interface{}
data []byte data []byte
pseudoOp [1]byte pseudoOp [1]byte
} }
type block struct { type block struct {
retPC int retPC pc
endPC int endPC pc
}
type pc struct {
data int
refs int
} }
var typeLengths = [...]int{ var typeLengths = [...]int{
@@ -47,16 +52,51 @@ var typeLengths = [...]int{
ops.TypePopLen, 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 { type OpBlock struct {
idx int pc pc
}
type opBlockDef struct {
endpc pc
} }
// Begin a block of ops. // Begin a block of ops.
func (o *Ops) Begin() { func (o *Ops) Begin() {
o.stack = append(o.stack, o.Size()) o.stack = append(o.stack, o.pc())
data := make([]byte, ops.TypeBlockDefLen) // Make room for a block definition. Filled out in End.
data[0] = byte(ops.TypeBlockDef) o.data = append(o.data, make([]byte, ops.TypeBlockDefLen)...)
o.Write(data) }
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 // End the most recent block and return
@@ -64,9 +104,13 @@ func (o *Ops) Begin() {
func (o *Ops) End() OpBlock { func (o *Ops) End() OpBlock {
start := o.stack[len(o.stack)-1] start := o.stack[len(o.stack)-1]
o.stack = 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 := 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} return OpBlock{start}
} }
@@ -77,41 +121,51 @@ func (o *Ops) Reset() {
o.data = o.data[:0] o.data = o.data[:0]
} }
func (o *Ops) Ref(r interface{}) int { func (o *Ops) Write(op []byte, refs []interface{}) {
o.refs = append(o.refs, r)
return len(o.refs) - 1
}
func (o *Ops) Write(op []byte) {
o.data = append(o.data, op...) o.data = append(o.data, op...)
o.refs = append(o.refs, refs...)
} }
// Size returns the length of the serialized Op data. func (o *Ops) pc() pc {
func (o *Ops) Size() int { return pc{data: len(o.data), refs: len(o.refs)}
return len(o.data) }
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) { func (b OpBlock) Add(o *Ops) {
data := make([]byte, ops.TypeBlockLen) data := make([]byte, ops.TypeBlockLen)
data[0] = byte(ops.TypeBlock) data[0] = byte(ops.TypeBlock)
bo := binary.LittleEndian bo := binary.LittleEndian
bo.PutUint32(data[1:], uint32(b.idx)) bo.PutUint32(data[1:], uint32(b.pc.data))
o.Write(data) bo.PutUint32(data[5:], uint32(b.pc.refs))
o.Write(data, nil)
} }
// Reset start reading from the op list. // Reset start reading from the op list.
func (r *OpsReader) Reset(ops *Ops) { func (r *OpsReader) Reset(ops *Ops) {
r.Refs = ops.refs r.refs = ops.refs
r.data = ops.data r.data = ops.data
r.stack = r.stack[:0] r.stack = r.stack[:0]
r.pc = 0 r.pc = pc{}
} }
func (r *OpsReader) Decode() ([]byte, bool) { func (r *OpsReader) Decode() ([]byte, []interface{}, bool) {
bo := binary.LittleEndian
for { for {
if r.pc == len(r.data) { if r.pc.data == len(r.data) {
return nil, false return nil, nil, false
} }
if len(r.stack) > 0 { if len(r.stack) > 0 {
b := r.stack[len(r.stack)-1] b := r.stack[len(r.stack)-1]
@@ -119,28 +173,40 @@ func (r *OpsReader) Decode() ([]byte, bool) {
r.pc = b.retPC r.pc = b.retPC
r.stack = r.stack[:len(r.stack)-1] r.stack = r.stack[:len(r.stack)-1]
r.pseudoOp[0] = byte(ops.TypePop) r.pseudoOp[0] = byte(ops.TypePop)
return r.pseudoOp[:], true return r.pseudoOp[:], nil, true
} }
} }
t := ops.OpType(r.data[r.pc]) t := ops.OpType(r.data[r.pc.data])
n := typeLengths[t] n := typeLengths[t-ops.FirstOpIndex]
data := r.data[r.pc : r.pc+n] 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 { switch t {
case ops.TypeBlock: case ops.TypeBlock:
blockIdx := int(bo.Uint32(data[1:])) var op OpBlock
if ops.OpType(r.data[blockIdx]) != ops.TypeBlockDef { op.decode(data)
if ops.OpType(r.data[op.pc.data]) != ops.TypeBlockDef {
panic("invalid block reference") panic("invalid block reference")
} }
blockLen := int(bo.Uint32(r.data[blockIdx+1:])) var opDef opBlockDef
r.stack = append(r.stack, block{r.pc + n, blockIdx + blockLen}) opDef.decode(r.data[op.pc.data : op.pc.data+ops.TypeBlockDefLen])
r.pc = blockIdx + 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) r.pseudoOp[0] = byte(ops.TypePush)
return r.pseudoOp[:], true return r.pseudoOp[:], nil, true
case ops.TypeBlockDef: case ops.TypeBlockDef:
r.pc += int(bo.Uint32(data[1:])) var op opBlockDef
op.decode(data)
r.pc = op.endpc
continue continue
} }
r.pc += n r.pc.data += n
return data, true r.pc.refs += nrefs
return data, refs, true
} }
} }
+3 -10
View File
@@ -3,7 +3,6 @@
package pointer package pointer
import ( import (
"encoding/binary"
"time" "time"
"gioui.org/ui" "gioui.org/ui"
@@ -72,26 +71,20 @@ const (
func (h OpHandler) Add(o *ui.Ops) { func (h OpHandler) Add(o *ui.Ops) {
data := make([]byte, ops.TypePointerHandlerLen) data := make([]byte, ops.TypePointerHandlerLen)
data[0] = byte(ops.TypePointerHandler) data[0] = byte(ops.TypePointerHandler)
bo := binary.LittleEndian
if h.Grab { if h.Grab {
data[1] = 1 data[1] = 1
} }
bo.PutUint32(data[2:], uint32(o.Ref(h.Key))) o.Write(data, []interface{}{h.Key, h.Area})
bo.PutUint32(data[6:], uint32(o.Ref(h.Area)))
o.Write(data)
} }
func (h *OpHandler) Decode(d []byte, refs []interface{}) { func (h *OpHandler) Decode(d []byte, refs []interface{}) {
bo := binary.LittleEndian
if ops.OpType(d[0]) != ops.TypePointerHandler { if ops.OpType(d[0]) != ops.TypePointerHandler {
panic("invalid op") panic("invalid op")
} }
key := int(bo.Uint32(d[2:]))
area := int(bo.Uint32(d[6:]))
*h = OpHandler{ *h = OpHandler{
Grab: d[1] != 0, Grab: d[1] != 0,
Key: refs[key].(Key), Key: refs[0].(Key),
Area: refs[area].(Area), Area: refs[1].(Area),
} }
} }
+2 -2
View File
@@ -39,7 +39,7 @@ type handler struct {
func (q *Queue) collectHandlers(r *ui.OpsReader, t ui.Transform, layer int) { func (q *Queue) collectHandlers(r *ui.OpsReader, t ui.Transform, layer int) {
for { for {
data, ok := r.Decode() data, refs, ok := r.Decode()
if !ok { if !ok {
return return
} }
@@ -57,7 +57,7 @@ func (q *Queue) collectHandlers(r *ui.OpsReader, t ui.Transform, layer int) {
t = t.Mul(op.Transform) t = t.Mul(op.Transform)
case ops.TypePointerHandler: case ops.TypePointerHandler:
var op OpHandler var op OpHandler
op.Decode(data, r.Refs) op.Decode(data, refs)
q.hitTree = append(q.hitTree, hitNode{level: layer, key: op.Key}) q.hitTree = append(q.hitTree, hitNode{level: layer, key: op.Key})
h, ok := q.handlers[op.Key] h, ok := q.handlers[op.Key]
if !ok { if !ok {
+3 -3
View File
@@ -77,7 +77,7 @@ func (r OpRedraw) Add(o *Ops) {
bo.PutUint64(data[1:], uint64(nanos)) bo.PutUint64(data[1:], uint64(nanos))
} }
} }
o.Write(data) o.Write(data, nil)
} }
func (r *OpRedraw) Decode(d []byte) { func (r *OpRedraw) Decode(d []byte) {
@@ -110,7 +110,7 @@ func (t OpTransform) Add(o *Ops) {
bo := binary.LittleEndian bo := binary.LittleEndian
bo.PutUint32(data[1:], math.Float32bits(t.Transform.offset.X)) bo.PutUint32(data[1:], math.Float32bits(t.Transform.offset.X))
bo.PutUint32(data[5:], math.Float32bits(t.Transform.offset.Y)) bo.PutUint32(data[5:], math.Float32bits(t.Transform.offset.Y))
o.Write(data) o.Write(data, nil)
} }
func (t *OpTransform) Decode(d []byte) { func (t *OpTransform) Decode(d []byte) {
@@ -129,7 +129,7 @@ func (t *OpTransform) Decode(d []byte) {
func (l OpLayer) Add(o *Ops) { func (l OpLayer) Add(o *Ops) {
data := make([]byte, ops.TypeLayerLen) data := make([]byte, ops.TypeLayerLen)
data[0] = byte(ops.TypeLayer) data[0] = byte(ops.TypeLayer)
o.Write(data) o.Write(data, nil)
} }
func (l *OpLayer) Decode(d []byte) { func (l *OpLayer) Decode(d []byte) {