all: serialize ops

Pros:
- Much less per-frame garbage
- Allow future preprocessing of ops while building it
- Much fewer interface calls and pointer chasing
- Allow future serialization of ops for remote rendering

Cons:
- Slightly clumsier API

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2019-04-24 13:33:01 +02:00
parent 7b6e1ce35a
commit 252e058766
21 changed files with 809 additions and 385 deletions
+29 -23
View File
@@ -15,20 +15,20 @@ type Flex struct {
CrossAxisAlignment CrossAxisAlignment
MainAxisSize MainAxisSize
cs Constraints
ops *ui.Ops
cs Constraints
children []flexChild
taken int
maxCross int
maxBaseline int
ccache [10]flexChild
opCache [10]ui.Op
ccache [10]flexChild
}
type flexChild struct {
op ui.Op
dims Dimens
block ui.OpBlock
dims Dimens
}
type MainAxisSize uint8
@@ -60,7 +60,8 @@ const (
Stretch
)
func (f *Flex) Init(cs Constraints) *Flex {
func (f *Flex) Init(ops *ui.Ops, cs Constraints) *Flex {
f.ops = ops
f.cs = cs
if f.children == nil {
f.children = f.ccache[:0]
@@ -78,7 +79,10 @@ func (f *Flex) Rigid(w Widget) *Flex {
mainMax -= f.taken
}
cs := axisConstraints(f.Axis, Constraint{Max: mainMax}, f.crossConstraintChild(f.cs))
op, dims := w.Layout(cs)
f.ops.Begin()
ui.OpLayer{}.Add(f.ops)
dims := w.Layout(f.ops, cs)
block := f.ops.End()
f.taken += axisMain(f.Axis, dims.Size)
if c := axisCross(f.Axis, dims.Size); c > f.maxCross {
f.maxCross = c
@@ -86,7 +90,7 @@ func (f *Flex) Rigid(w Widget) *Flex {
if b := dims.Baseline; b > f.maxBaseline {
f.maxBaseline = b
}
f.children = append(f.children, flexChild{op, dims})
f.children = append(f.children, flexChild{block, dims})
return f
}
@@ -101,7 +105,10 @@ func (f *Flex) Flexible(idx int, flex float32, mode FlexMode, w Widget) *Flex {
submainc.Min = submainc.Max
}
cs := axisConstraints(f.Axis, submainc, f.crossConstraintChild(f.cs))
op, dims := w.Layout(cs)
f.ops.Begin()
ui.OpLayer{}.Add(f.ops)
dims := w.Layout(f.ops, cs)
block := f.ops.End()
f.taken += axisMain(f.Axis, dims.Size)
if c := axisCross(f.Axis, dims.Size); c > f.maxCross {
f.maxCross = c
@@ -109,15 +116,16 @@ func (f *Flex) Flexible(idx int, flex float32, mode FlexMode, w Widget) *Flex {
if b := dims.Baseline; b > f.maxBaseline {
f.maxBaseline = b
}
f.children = append(f.children, flexChild{op, dims})
if idx < 0 {
idx += len(f.children)
idx += len(f.children) + 1
}
f.children[idx], f.children[len(f.children)-1] = f.children[len(f.children)-1], f.children[idx]
f.children = append(f.children, flexChild{})
copy(f.children[idx+1:], f.children[idx:])
f.children[idx] = flexChild{block, dims}
return f
}
func (f *Flex) Layout() (ui.Op, Dimens) {
func (f *Flex) Layout() Dimens {
mainc := axisMainConstraint(f.Axis, f.cs)
crossSize := axisCrossConstraint(f.Axis, f.cs).Constrain(f.maxCross)
var space int
@@ -140,13 +148,7 @@ func (f *Flex) Layout() (ui.Op, Dimens) {
case SpaceAround:
mainSize += space / (len(f.children) * 2)
}
var ops ui.Ops
if len(f.children) > len(f.opCache) {
ops = make([]ui.Op, len(f.children))
} else {
ops = f.opCache[:len(f.children)]
}
for i, child := range f.children {
for _, child := range f.children {
dims := child.dims
b := dims.Baseline
var cross int
@@ -160,8 +162,12 @@ func (f *Flex) Layout() (ui.Op, Dimens) {
cross = f.maxBaseline - b
}
}
off := ui.Offset(toPointF(axisPoint(f.Axis, mainSize, cross)))
ops[i] = ui.OpLayer{Op: ui.OpTransform{Transform: off, Op: child.op}}
f.ops.Begin()
ui.OpTransform{
Transform: ui.Offset(toPointF(axisPoint(f.Axis, mainSize, cross))),
}.Add(f.ops)
child.block.Add(f.ops)
f.ops.End().Add(f.ops)
mainSize += axisMain(f.Axis, dims.Size)
switch f.MainAxisAlignment {
case SpaceEvenly:
@@ -187,7 +193,7 @@ func (f *Flex) Layout() (ui.Op, Dimens) {
if baseline == 0 {
baseline = sz.Y
}
return ops, Dimens{Size: sz, Baseline: baseline}
return Dimens{Size: sz, Baseline: baseline}
}
func axisPoint(a Axis, main, cross int) image.Point {
+36 -24
View File
@@ -12,8 +12,8 @@ import (
)
type scrollChild struct {
op ui.Op
size image.Point
size image.Point
block ui.OpBlock
}
type List struct {
@@ -24,12 +24,14 @@ type List struct {
// The distance scrolled since last call to Init.
Distance int
area gesture.Rect
scroll gesture.Scroll
scrollDir int
offset int
first int
ops *ui.Ops
cs Constraints
len int
@@ -38,7 +40,6 @@ type List struct {
elem func(w Widget)
size image.Point
ops ui.Ops
}
type Interface interface {
@@ -46,19 +47,20 @@ type Interface interface {
At(i int) Widget
}
func (l *List) Init(cs Constraints, len int) (int, bool) {
func (l *List) Init(ops *ui.Ops, cs Constraints, len int) (int, bool) {
l.maxSize = 0
l.children = l.children[:0]
l.ops = ops
l.cs = cs
l.len = len
l.elem = nil
if l.first > len {
l.first = len
}
l.ops = l.ops[:0]
if len == 0 {
return 0, false
}
l.scroll.Op(ops, &l.area)
return l.Index()
}
@@ -82,9 +84,9 @@ func (l *List) Index() (int, bool) {
return i, ok
}
func (l *List) Layout() (ui.Op, Dimens) {
ops := append(ui.Ops{l.scroll.Op(&gesture.Rect{l.size})}, l.ops...)
return ops, Dimens{Size: l.size}
func (l *List) Layout() Dimens {
l.area.Size = l.size
return Dimens{Size: l.size}
}
func (l *List) next() (int, bool) {
@@ -116,21 +118,28 @@ func (l *List) Elem(w Widget) {
}
func (l *List) backward(w Widget) {
subcs := axisConstraints(l.Axis, Constraint{Max: ui.Inf}, l.crossConstraintChild(l.cs))
l.first--
op, dims := w.Layout(subcs)
mainSize := axisMain(l.Axis, dims.Size)
child := l.add(w)
mainSize := axisMain(l.Axis, child.size)
l.offset += mainSize
l.maxSize += mainSize
l.children = append([]scrollChild{{op, dims.Size}}, l.children...)
l.children = append([]scrollChild{child}, l.children...)
}
func (l *List) forward(w Widget) {
subcs := axisConstraints(l.Axis, Constraint{Max: ui.Inf}, l.crossConstraintChild(l.cs))
op, dims := w.Layout(subcs)
mainSize := axisMain(l.Axis, dims.Size)
child := l.add(w)
mainSize := axisMain(l.Axis, child.size)
l.maxSize += mainSize
l.children = append(l.children, scrollChild{op, dims.Size})
l.children = append(l.children, child)
}
func (l *List) add(w Widget) scrollChild {
subcs := axisConstraints(l.Axis, Constraint{Max: ui.Inf}, l.crossConstraintChild(l.cs))
l.ops.Begin()
ui.OpLayer{}.Add(l.ops)
dims := w.Layout(l.ops, subcs)
block := l.ops.End()
return scrollChild{dims.Size, block}
}
func (l *List) draw() {
@@ -176,14 +185,17 @@ func (l *List) draw() {
if min < 0 {
min = 0
}
op := draw.ClipRect(
image.Rectangle{
Min: axisPoint(l.Axis, min, -ui.Inf),
Max: axisPoint(l.Axis, max, ui.Inf),
},
ui.OpTransform{Transform: ui.Offset(toPointF(axisPoint(l.Axis, pos, cross))), Op: child.op},
)
l.ops = append(l.ops, ui.OpLayer{Op: op})
r := image.Rectangle{
Min: axisPoint(l.Axis, min, -ui.Inf),
Max: axisPoint(l.Axis, max, ui.Inf),
}
l.ops.Begin()
draw.OpClip{Path: draw.RectPath(r)}.Add(l.ops)
ui.OpTransform{
Transform: ui.Offset(toPointF(axisPoint(l.Axis, pos, cross))),
}.Add(l.ops)
child.block.Add(l.ops)
l.ops.End().Add(l.ops)
pos += axisMain(l.Axis, sz)
}
atStart := l.first == 0 && l.offset <= 0
+25 -19
View File
@@ -10,7 +10,7 @@ import (
)
type Widget interface {
Layout(cs Constraints) (ui.Op, Dimens)
Layout(ops *ui.Ops, cs Constraints) Dimens
}
type Constraints struct {
@@ -29,7 +29,7 @@ type Dimens struct {
type Axis uint8
type F func(cs Constraints) (ui.Op, Dimens)
type F func(ops *ui.Ops, cs Constraints) Dimens
const (
Horizontal Axis = iota
@@ -73,8 +73,8 @@ func ExactConstraints(size image.Point) Constraints {
}
}
func (f F) Layout(cs Constraints) (ui.Op, Dimens) {
return f(cs)
func (f F) Layout(ops *ui.Ops, cs Constraints) Dimens {
return f(ops, cs)
}
type Margins struct {
@@ -82,7 +82,7 @@ type Margins struct {
}
func Margin(c *ui.Config, m Margins, w Widget) Widget {
return F(func(cs Constraints) (ui.Op, Dimens) {
return F(func(ops *ui.Ops, cs Constraints) Dimens {
mcs := cs
t, r, b, l := int(c.Pixels(m.Top)+0.5), int(c.Pixels(m.Right)+0.5), int(c.Pixels(m.Bottom)+0.5), int(c.Pixels(m.Left)+0.5)
if mcs.Width.Max != ui.Inf {
@@ -105,10 +105,11 @@ func Margin(c *ui.Config, m Margins, w Widget) Widget {
mcs.Height.Max = mcs.Height.Min
}
}
op, dims := w.Layout(mcs)
op = ui.OpTransform{Transform: ui.Offset(toPointF(image.Point{X: l, Y: t})), Op: op}
return op, Dimens{
ops.Begin()
ui.OpTransform{Transform: ui.Offset(toPointF(image.Point{X: l, Y: t}))}.Add(ops)
dims := w.Layout(ops, mcs)
ops.End().Add(ops)
return Dimens{
Size: cs.Constrain(dims.Size.Add(image.Point{X: r + l, Y: t + b})),
Baseline: dims.Baseline + t,
}
@@ -124,7 +125,7 @@ func isInf(v ui.Value) bool {
}
func Capped(c *ui.Config, maxWidth, maxHeight ui.Value, wt Widget) Widget {
return F(func(cs Constraints) (ui.Op, Dimens) {
return F(func(ops *ui.Ops, cs Constraints) Dimens {
if !isInf(maxWidth) {
mw := int(c.Pixels(maxWidth) + .5)
if mw < cs.Width.Min {
@@ -143,12 +144,12 @@ func Capped(c *ui.Config, maxWidth, maxHeight ui.Value, wt Widget) Widget {
cs.Height.Max = mh
}
}
return wt.Layout(cs)
return wt.Layout(ops, cs)
})
}
func Sized(c *ui.Config, width, height ui.Value, wt Widget) Widget {
return F(func(cs Constraints) (ui.Op, Dimens) {
return F(func(ops *ui.Ops, cs Constraints) Dimens {
if h := int(c.Pixels(height) + 0.5); h != 0 {
if cs.Height.Min < h {
cs.Height.Min = h
@@ -165,25 +166,27 @@ func Sized(c *ui.Config, width, height ui.Value, wt Widget) Widget {
cs.Width.Max = w
}
}
return wt.Layout(cs)
return wt.Layout(ops, cs)
})
}
func Expand(w Widget) Widget {
return F(func(cs Constraints) (ui.Op, Dimens) {
return F(func(ops *ui.Ops, cs Constraints) Dimens {
if cs.Height.Max != ui.Inf {
cs.Height.Min = cs.Height.Max
}
if cs.Width.Max != ui.Inf {
cs.Width.Min = cs.Width.Max
}
return w.Layout(cs)
return w.Layout(ops, cs)
})
}
func Align(alignment Direction, w Widget) Widget {
return F(func(cs Constraints) (ui.Op, Dimens) {
op, dims := w.Layout(cs.Loose())
return F(func(ops *ui.Ops, cs Constraints) Dimens {
ops.Begin()
dims := w.Layout(ops, cs.Loose())
block := ops.End()
sz := dims.Size
if cs.Width.Max != ui.Inf {
sz.X = cs.Width.Max
@@ -204,8 +207,11 @@ func Align(alignment Direction, w Widget) Widget {
case SW, S, SE:
p.Y = sz.Y - dims.Size.Y
}
op = ui.OpTransform{Transform: ui.Offset(toPointF(p)), Op: op}
return op, Dimens{
ops.Begin()
ui.OpTransform{Transform: ui.Offset(toPointF(p))}.Add(ops)
block.Add(ops)
ops.End().Add(ops)
return Dimens{
Size: sz,
Baseline: dims.Baseline,
}
+29 -22
View File
@@ -11,18 +11,18 @@ import (
type Stack struct {
Alignment Direction
ops *ui.Ops
cs Constraints
children []stackChild
maxSZ image.Point
baseline int
ccache [10]stackChild
opCache [10]ui.Op
ccache [10]stackChild
}
type stackChild struct {
op ui.Op
dims Dimens
block ui.OpBlock
dims Dimens
}
type Direction uint8
@@ -38,26 +38,31 @@ const (
W
)
func (s *Stack) Init(cs Constraints) *Stack {
func (s *Stack) Init(ops *ui.Ops, cs Constraints) *Stack {
if s.children == nil {
s.children = s.ccache[:0]
}
s.children = s.children[:0]
s.maxSZ = image.Point{}
s.baseline = 0
s.ops = ops
s.cs = cs
return s
}
func (s *Stack) Rigid(w Widget) *Stack {
op, dims := w.Layout(s.cs)
s.ops.Begin()
ui.OpLayer{}.Add(s.ops)
dims := w.Layout(s.ops, s.cs)
b := s.ops.End()
if w := dims.Size.X; w > s.maxSZ.X {
s.maxSZ.X = w
}
if h := dims.Size.Y; h > s.maxSZ.Y {
s.maxSZ.Y = h
}
s.add(op, dims)
s.addjustBaseline(dims)
s.children = append(s.children, stackChild{b, dims})
return s
}
@@ -66,16 +71,21 @@ func (s *Stack) Expand(idx int, w Widget) *Stack {
Width: Constraint{Min: s.maxSZ.X, Max: s.maxSZ.X},
Height: Constraint{Min: s.maxSZ.Y, Max: s.maxSZ.Y},
}
s.add(w.Layout(cs))
s.ops.Begin()
ui.OpLayer{}.Add(s.ops)
dims := w.Layout(s.ops, cs)
b := s.ops.End()
s.addjustBaseline(dims)
if idx < 0 {
idx += len(s.children)
idx += len(s.children) + 1
}
s.children[idx], s.children[len(s.children)-1] = s.children[len(s.children)-1], s.children[idx]
s.children = append(s.children, stackChild{})
copy(s.children[idx+1:], s.children[idx:])
s.children[idx] = stackChild{b, dims}
return s
}
func (s *Stack) add(op ui.Op, dims Dimens) {
s.children = append(s.children, stackChild{op, dims})
func (s *Stack) addjustBaseline(dims Dimens) {
if s.baseline == 0 {
if b := dims.Baseline; b != dims.Size.Y {
s.baseline = b
@@ -83,14 +93,8 @@ func (s *Stack) add(op ui.Op, dims Dimens) {
}
}
func (s *Stack) Layout() (ui.Op, Dimens) {
var ops ui.Ops
if len(s.children) > len(s.opCache) {
ops = make([]ui.Op, len(s.children))
} else {
ops = s.opCache[:len(s.children)]
}
for i, ch := range s.children {
func (s *Stack) Layout() Dimens {
for _, ch := range s.children {
sz := ch.dims.Size
var p image.Point
switch s.Alignment {
@@ -105,13 +109,16 @@ func (s *Stack) Layout() (ui.Op, Dimens) {
case SW, S, SE:
p.Y = s.maxSZ.Y - sz.Y
}
ops[i] = ui.OpLayer{Op: ui.OpTransform{Transform: ui.Offset(toPointF(p)), Op: ch.op}}
s.ops.Begin()
ui.OpTransform{Transform: ui.Offset(toPointF(p))}.Add(s.ops)
ch.block.Add(s.ops)
s.ops.End().Add(s.ops)
}
b := s.baseline
if b == 0 {
b = s.maxSZ.Y
}
return ops, Dimens{
return Dimens{
Size: s.maxSZ,
Baseline: b,
}