Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First stage in text speed improvements #5190

Merged
merged 2 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions internal/cache/text.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cache

import (
"image/color"
"sync"
"time"

Expand All @@ -19,10 +20,16 @@ type fontMetric struct {
}

type fontSizeEntry struct {
text string
size float32
style fyne.TextStyle
custom string
Text string
Size float32
Style fyne.TextStyle
Source string
}

type FontCacheEntry struct {
fontSizeEntry

Color color.Color
}

// GetFontMetrics looks up a calculated size and baseline required for the specified text parameters.
Expand Down
37 changes: 37 additions & 0 deletions internal/cache/texture_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,17 @@ func DeleteTexture(obj fyne.CanvasObject) {
textures.Delete(obj)
}

// GetTextTexture gets cached texture for a text run.
func GetTextTexture(ent FontCacheEntry) (TextureType, bool) {
return load(ent)
}

// GetTexture gets cached texture.
func GetTexture(obj fyne.CanvasObject) (TextureType, bool) {
return load(obj)
}

func load(obj any) (TextureType, bool) {
t, ok := textures.Load(obj)
if t == nil || !ok {
return NoTexture, false
Expand All @@ -31,6 +40,17 @@ func GetTexture(obj fyne.CanvasObject) (TextureType, bool) {
func RangeExpiredTexturesFor(canvas fyne.Canvas, f func(fyne.CanvasObject)) {
now := timeNow()
textures.Range(func(key, value any) bool {
if _, ok := key.(FontCacheEntry); ok {
tinfo := value.(*textureInfo)

// just free text directly when that string/style combo is done
if tinfo.isExpired(now) && tinfo.canvas == canvas {
textures.Delete(key)
tinfo.textFree()
}

return true
}
obj, tinfo := key.(fyne.CanvasObject), value.(*textureInfo)
if tinfo.isExpired(now) && tinfo.canvas == canvas {
f(obj)
Expand All @@ -40,11 +60,16 @@ func RangeExpiredTexturesFor(canvas fyne.Canvas, f func(fyne.CanvasObject)) {
}

// RangeTexturesFor range over the textures for the specified canvas.
// It will not return the texture for a `canvas.Text` as their render lifecycle is handled separately.
//
// Note: If this is used to free textures, then it should be called inside a current
// gl context to ensure textures are deleted from gl.
func RangeTexturesFor(canvas fyne.Canvas, f func(fyne.CanvasObject)) {
textures.Range(func(key, value any) bool {
if _, ok := key.(FontCacheEntry); ok {
return true // do nothing, text cache lives outside the scope of an object
}

obj, tinfo := key.(fyne.CanvasObject), value.(*textureInfo)
if tinfo.canvas == canvas {
f(obj)
Expand All @@ -53,9 +78,21 @@ func RangeTexturesFor(canvas fyne.Canvas, f func(fyne.CanvasObject)) {
})
}

// SetTextTexture sets cached texture for a text run.
func SetTextTexture(ent FontCacheEntry, texture TextureType, canvas fyne.Canvas, free func()) {
store(ent, texture, canvas, free)
}

// SetTexture sets cached texture.
func SetTexture(obj fyne.CanvasObject, texture TextureType, canvas fyne.Canvas) {
store(obj, texture, canvas, nil)
}

func store(obj any, texture TextureType, canvas fyne.Canvas, free func()) {
texInfo := &textureInfo{texture: texture}
if free != nil {
texInfo.textFree = free
}
texInfo.canvas = canvas
texInfo.setAlive()
textures.Store(obj, texInfo)
Expand Down
4 changes: 3 additions & 1 deletion internal/cache/texture_desktop.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ var NoTexture = TextureType(0)

type textureInfo struct {
textureCacheBase
texture TextureType

texture TextureType
textFree func()
}

// IsValid will return true if the passed texture is potentially a texture
Expand Down
4 changes: 3 additions & 1 deletion internal/cache/texture_gomobile.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ var NoTexture = gl.Texture{0}

type textureInfo struct {
textureCacheBase
texture TextureType

texture TextureType
textFree func()
}

// IsValid will return true if the passed texture is potentially a texture
Expand Down
6 changes: 4 additions & 2 deletions internal/cache/texture_goxjs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

package cache

import gl "github.com/fyne-io/gl-js"
import "github.com/fyne-io/gl-js"

// TextureType represents an uploaded GL texture
type TextureType = gl.Texture
Expand All @@ -11,7 +11,9 @@ var NoTexture = gl.NoTexture

type textureInfo struct {
textureCacheBase
texture TextureType

texture TextureType
textFree func()
}

// IsValid will return true if the passed texture is potentially a texture
Expand Down
24 changes: 24 additions & 0 deletions internal/painter/gl/texture.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,30 @@ func (p *painter) freeTexture(obj fyne.CanvasObject) {
}

func (p *painter) getTexture(object fyne.CanvasObject, creator func(canvasObject fyne.CanvasObject) Texture) (Texture, error) {
if t, ok := object.(*canvas.Text); ok {
custom := ""
if t.FontSource != nil {
custom = t.FontSource.Name()
}
ent := cache.FontCacheEntry{Color: t.Color}
ent.Text = t.Text
ent.Size = t.TextSize
ent.Style = t.TextStyle
ent.Source = custom

texture, ok := cache.GetTextTexture(ent)

if !ok {
tex := creator(object)
texture = cache.TextureType(tex)
cache.SetTextTexture(ent, texture, p.canvas, func() {
p.ctx.DeleteTexture(tex)
})
}

return Texture(texture), nil
}

texture, ok := cache.GetTexture(object)

if !ok {
Expand Down