Skip to content

Commit

Permalink
Store renderers using async.Map
Browse files Browse the repository at this point in the history
  • Loading branch information
Jacalz committed Jan 9, 2025
1 parent 53db8dd commit dac93f7
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 46 deletions.
19 changes: 6 additions & 13 deletions internal/cache/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,18 @@ func CleanCanvas(canvas fyne.Canvas) {
}
canvasesLock.Unlock()

renderersLock.Lock()
for _, dobj := range deletingObjs {
wid, ok := dobj.(fyne.Widget)
if !ok {
continue
}
rinfo, ok := renderers[wid]
rinfo, ok := renderers.LoadAndDelete(wid)
if !ok {
continue
}
rinfo.renderer.Destroy()
overrides.Delete(wid)
delete(renderers, wid)
}
renderersLock.Unlock()
}

// CleanCanvases runs cache clean tasks for canvases that are being refreshed. This is called on paint events.
Expand Down Expand Up @@ -129,21 +126,18 @@ func CleanCanvases(refreshingCanvases []fyne.Canvas) {
}
canvasesLock.Unlock()

renderersLock.Lock()
for _, dobj := range deletingObjs {
wid, ok := dobj.(fyne.Widget)
if !ok {
continue
}
rinfo, ok := renderers[wid]
rinfo, ok := renderers.LoadAndDelete(wid)
if !ok || !rinfo.isExpired(now) {
continue
}
rinfo.renderer.Destroy()
overrides.Delete(wid)
delete(renderers, wid)
}
renderersLock.Unlock()
lastClean = timeNow()
}

Expand All @@ -167,15 +161,14 @@ func destroyExpiredCanvases(now time.Time) {
// destroyExpiredRenderers deletes the renderer from the cache and calls
// renderer.Destroy()
func destroyExpiredRenderers(now time.Time) {
renderersLock.Lock()
for wid, rinfo := range renderers {
renderers.Range(func(wid fyne.Widget, rinfo *rendererInfo) bool {
if rinfo.isExpired(now) {
rinfo.renderer.Destroy()
overrides.Delete(wid)
delete(renderers, wid)
renderers.Delete(wid)
}
}
renderersLock.Unlock()
return true
})
}

// matchesACanvas returns true if the canvas represented by the canvasInfo object matches one of
Expand Down
26 changes: 13 additions & 13 deletions internal/cache/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ func TestCacheClean(t *testing.T) {
lastClean = tm.createTime(10, 20)
Clean(false)
assert.Equal(t, svgs.Len(), 40)
assert.Len(t, renderers, 40)
assert.Equal(t, 40, renderers.Len())
assert.Len(t, canvases, 40)
assert.Zero(t, destroyedRenderersCnt)
assert.Equal(t, tm.now, lastClean)

tm.setTime(10, 30)
Clean(true)
assert.Equal(t, svgs.Len(), 40)
assert.Len(t, renderers, 40)
assert.Equal(t, 40, renderers.Len())
assert.Len(t, canvases, 40)
assert.Zero(t, destroyedRenderersCnt)
assert.Equal(t, tm.now, lastClean)
Expand Down Expand Up @@ -72,7 +72,7 @@ func TestCacheClean(t *testing.T) {
assert.Equal(t, tm.now, lastClean)

assert.Equal(t, svgs.Len(), 40)
assert.Len(t, renderers, 40)
assert.Equal(t, 40, renderers.Len())
assert.Len(t, canvases, 40)
assert.Zero(t, destroyedRenderersCnt)
})
Expand All @@ -82,14 +82,14 @@ func TestCacheClean(t *testing.T) {
tm.setTime(11, 12)
Clean(false)
assert.Equal(t, svgs.Len(), 20)
assert.Len(t, renderers, 40)
assert.Equal(t, 40, renderers.Len())
assert.Len(t, canvases, 40)
assert.Zero(t, destroyedRenderersCnt)

tm.setTime(11, 42)
Clean(false)
assert.Equal(t, svgs.Len(), 0)
assert.Len(t, renderers, 40)
assert.Equal(t, 40, renderers.Len())
assert.Len(t, canvases, 40)
assert.Zero(t, destroyedRenderersCnt)
})
Expand All @@ -99,14 +99,14 @@ func TestCacheClean(t *testing.T) {
tm.setTime(11, 11)
Clean(true)
assert.Equal(t, svgs.Len(), 0)
assert.Len(t, renderers, 20)
assert.Equal(t, 20, renderers.Len())
assert.Len(t, canvases, 20)
assert.Equal(t, 20, destroyedRenderersCnt)

tm.setTime(11, 22)
Clean(true)
assert.Equal(t, svgs.Len(), 0)
assert.Len(t, renderers, 0)
assert.Equal(t, 0, renderers.Len())
assert.Len(t, canvases, 0)
assert.Equal(t, 40, destroyedRenderersCnt)
})
Expand All @@ -125,13 +125,13 @@ func TestCacheClean(t *testing.T) {
Clean(true)
assert.True(t, skippedCleanWithCanvasRefresh)
assert.Less(t, lastClean.UnixNano(), tm.now.UnixNano())
assert.Len(t, renderers, 1)
assert.Equal(t, 1, renderers.Len())

tm.setTime(14, 21)
Clean(false)
assert.False(t, skippedCleanWithCanvasRefresh)
assert.Equal(t, tm.now, lastClean)
assert.Len(t, renderers, 0)
assert.Equal(t, 0, renderers.Len())
})
}

Expand All @@ -158,19 +158,19 @@ func TestCleanCanvas(t *testing.T) {
SetCanvasForObject(dwidget, dcanvas2, nil)
}

assert.Len(t, renderers, 42)
assert.Equal(t, 42, renderers.Len())
assert.Len(t, canvases, 42)

CleanCanvas(dcanvas1)
assert.Len(t, renderers, 22)
assert.Equal(t, 22, renderers.Len())
assert.Len(t, canvases, 22)
assert.Equal(t, 20, destroyedRenderersCnt)
for _, cinfo := range canvases {
assert.Equal(t, dcanvas2, cinfo.canvas)
}

CleanCanvas(dcanvas2)
assert.Len(t, renderers, 0)
assert.Equal(t, 0, renderers.Len())
assert.Len(t, canvases, 0)
assert.Equal(t, 42, destroyedRenderersCnt)
}
Expand Down Expand Up @@ -271,6 +271,6 @@ func testClearAll() {
})
textTextures.Clear()
objectTextures.Clear()
renderers = map[fyne.Widget]*rendererInfo{}
renderers.Clear()
timeNow = time.Now
}
26 changes: 6 additions & 20 deletions internal/cache/widget.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package cache

import (
"sync"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/internal/async"
)

var renderersLock sync.RWMutex
var renderers = map[fyne.Widget]*rendererInfo{}
var renderers async.Map[fyne.Widget, *rendererInfo]

type isBaseWidget interface {
ExtendBaseWidget(fyne.Widget)
Expand All @@ -26,14 +24,10 @@ func Renderer(wid fyne.Widget) fyne.WidgetRenderer {
}
}

renderersLock.RLock()
rinfo, ok := renderers[wid]
renderersLock.RUnlock()
rinfo, ok := renderers.Load(wid)
if !ok {
rinfo = &rendererInfo{renderer: wid.CreateRenderer()}
renderersLock.Lock()
renderers[wid] = rinfo
renderersLock.Unlock()
renderers.Store(wid, rinfo)
}

if rinfo == nil {
Expand All @@ -48,28 +42,20 @@ func Renderer(wid fyne.Widget) fyne.WidgetRenderer {
// DestroyRenderer frees a render implementation for a widget.
// This is typically for internal use only.
func DestroyRenderer(wid fyne.Widget) {
renderersLock.RLock()
rinfo, ok := renderers[wid]
renderersLock.RUnlock()
rinfo, ok := renderers.LoadAndDelete(wid)
if !ok {
return
}
if rinfo != nil {
rinfo.renderer.Destroy()
}
overrides.Delete(wid)

renderersLock.Lock()
delete(renderers, wid)
renderersLock.Unlock()
}

// IsRendered returns true of the widget currently has a renderer.
// One will be created the first time a widget is shown but may be removed after it is hidden.
func IsRendered(wid fyne.Widget) bool {
renderersLock.RLock()
_, found := renderers[wid]
renderersLock.RUnlock()
_, found := renderers.Load(wid)
return found
}

Expand Down

0 comments on commit dac93f7

Please sign in to comment.