Skip to content

Commit

Permalink
tree based minSize cache
Browse files Browse the repository at this point in the history
  • Loading branch information
toaster committed Sep 2, 2019
1 parent 8feebf3 commit 7461574
Show file tree
Hide file tree
Showing 3 changed files with 461 additions and 26 deletions.
119 changes: 94 additions & 25 deletions internal/driver/glfw/canvas.go
Expand Up @@ -35,11 +35,31 @@ type glCanvas struct {
scale, detectedScale float32
painter gl.Painter

dirty bool
dirtyMutex *sync.Mutex
minSizes map[fyne.CanvasObject]fyne.Size
refreshQueue chan fyne.CanvasObject
context driver.WithContext
dirty bool
dirtyMutex *sync.Mutex
refreshQueue chan fyne.CanvasObject
contentTree, menuTree, overlayTree *renderCacheTree
context driver.WithContext
}

type renderCacheTree struct {
sync.RWMutex
root *renderCacheNode
}

type renderCacheNode struct {
// structural data
firstChild *renderCacheNode
nextSibling *renderCacheNode
obj fyne.CanvasObject
parent *renderCacheNode
// cache data
minSize fyne.Size
// painterData is some data from the painter associated with the drawed node
// it may for instance point to a GL texture
// it should free all associated resources when released
// i.e. it should not simply be a texture reference integer
painterData interface{}
}

func (c *glCanvas) Capture() image.Image {
Expand All @@ -60,7 +80,7 @@ func (c *glCanvas) Content() fyne.CanvasObject {
func (c *glCanvas) SetContent(content fyne.CanvasObject) {
c.Lock()
c.content = content
c.minSizes = map[fyne.CanvasObject]fyne.Size{}
c.contentTree = &renderCacheTree{root: &renderCacheNode{obj: c.content}}
c.Unlock()

newSize := c.size.Union(c.canvasSize(c.content.MinSize()))
Expand All @@ -79,6 +99,7 @@ func (c *glCanvas) Overlay() fyne.CanvasObject {
func (c *glCanvas) SetOverlay(overlay fyne.CanvasObject) {
c.Lock()
c.overlay = overlay
c.overlayTree = &renderCacheTree{root: &renderCacheNode{obj: c.overlay}}
c.Unlock()

if overlay != nil {
Expand Down Expand Up @@ -212,7 +233,8 @@ func (c *glCanvas) ensureMinSize() bool {
}
var objToLayout fyne.CanvasObject
windowNeedsMinSizeUpdate := false
ensureMinSize := func(obj fyne.CanvasObject, parent fyne.CanvasObject) {
ensureMinSize := func(node *renderCacheNode) {
obj := node.obj
canvasMutex.Lock()
canvases[obj] = c
canvasMutex.Unlock()
Expand All @@ -221,11 +243,11 @@ func (c *glCanvas) ensureMinSize() bool {
return
}
minSize := obj.MinSize()
minSizeChanged := c.minSizes[obj] != minSize
minSizeChanged := node.minSize != minSize
if minSizeChanged {
c.minSizes[obj] = minSize
if parent != nil {
objToLayout = parent
node.minSize = minSize
if node.parent != nil {
objToLayout = node.parent.obj
} else {
windowNeedsMinSizeUpdate = true
size := obj.Size()
Expand Down Expand Up @@ -264,7 +286,8 @@ func (c *glCanvas) paint(size fyne.Size) {
c.setDirty(false)
c.painter.Clear()

paint := func(obj fyne.CanvasObject, pos fyne.Position, _ fyne.Position, _ fyne.Size) bool {
paint := func(node *renderCacheNode, pos fyne.Position) {
obj := node.obj
// TODO should this be somehow not scroll container specific?
if _, ok := obj.(*widget.ScrollContainer); ok {
c.painter.StartClipping(
Expand All @@ -273,10 +296,9 @@ func (c *glCanvas) paint(size fyne.Size) {
)
}
c.painter.Paint(obj, pos, size)
return false
}
afterPaint := func(obj, _ fyne.CanvasObject) {
if _, ok := obj.(*widget.ScrollContainer); ok {
afterPaint := func(node *renderCacheNode) {
if _, ok := node.obj.(*widget.ScrollContainer); ok {
c.painter.StopClipping()
}
}
Expand All @@ -285,24 +307,70 @@ func (c *glCanvas) paint(size fyne.Size) {
}

func (c *glCanvas) walkTrees(
beforeChildren func(fyne.CanvasObject, fyne.Position, fyne.Position, fyne.Size) bool,
afterChildren func(fyne.CanvasObject, fyne.CanvasObject),
beforeChildren func(*renderCacheNode, fyne.Position),
afterChildren func(*renderCacheNode),
) {
c.walkTree(c.content, beforeChildren, afterChildren)
c.walkTree(c.contentTree, beforeChildren, afterChildren)
if c.menu != nil {
c.walkTree(c.menu, beforeChildren, afterChildren)
c.walkTree(c.menuTree, beforeChildren, afterChildren)
}
if c.overlay != nil {
c.walkTree(c.overlay, beforeChildren, afterChildren)
c.walkTree(c.overlayTree, beforeChildren, afterChildren)
}
}

func (c *glCanvas) walkTree(
obj fyne.CanvasObject,
beforeChildren func(fyne.CanvasObject, fyne.Position, fyne.Position, fyne.Size) bool,
afterChildren func(fyne.CanvasObject, fyne.CanvasObject),
tree *renderCacheTree,
beforeChildren func(*renderCacheNode, fyne.Position),
afterChildren func(*renderCacheNode),
) {
driver.WalkVisibleObjectTree(obj, beforeChildren, afterChildren)
tree.Lock()
defer tree.Unlock()
var node, parent, prev *renderCacheNode
node = tree.root

bc := func(obj fyne.CanvasObject, pos fyne.Position, _ fyne.Position, _ fyne.Size) bool {
if node != nil && node.obj != obj {
if parent.firstChild == node {
parent.firstChild = nil
}
node = nil
}
if node == nil {
node = &renderCacheNode{parent: parent, obj: obj}
if parent.firstChild == nil {
parent.firstChild = node
} else {
prev.nextSibling = node
}
}
if prev != nil && prev.parent != parent {
prev = nil
}

if beforeChildren != nil {
beforeChildren(node, pos)
}

parent = node
node = parent.firstChild
return false
}
ac := func(obj fyne.CanvasObject, _ fyne.CanvasObject) {
node = parent
parent = node.parent
if prev != nil && prev.parent != parent {
prev.nextSibling = nil
}

if afterChildren != nil {
afterChildren(node)
}

prev = node
node = node.nextSibling
}
driver.WalkVisibleObjectTree(tree.root.obj, bc, ac)
}

func (c *glCanvas) setDirty(dirty bool) {
Expand Down Expand Up @@ -349,6 +417,7 @@ func (c *glCanvas) buildMenuBar(m *fyne.MainMenu) {
func (c *glCanvas) setMenuBar(b *widget.Toolbar) {
c.Lock()
c.menu = b
c.menuTree = &renderCacheTree{root: &renderCacheNode{obj: c.menu}}
c.Unlock()
}

Expand Down Expand Up @@ -398,7 +467,7 @@ func (c *glCanvas) contentPos() fyne.Position {
func newCanvas() *glCanvas {
c := &glCanvas{scale: 1.0}
c.content = &canvas.Rectangle{FillColor: theme.BackgroundColor()}
c.minSizes = map[fyne.CanvasObject]fyne.Size{}
c.contentTree = &renderCacheTree{root: &renderCacheNode{obj: c.content}}
c.padded = true

c.focusMgr = app.NewFocusManager(c)
Expand Down

0 comments on commit 7461574

Please sign in to comment.