Avoid heap allocating in GetTextRuns+Perf notes+pprof by default

This commit is contained in:
bloeys
2022-07-08 08:44:57 +04:00
parent d8289139d3
commit cb2ead0907
4 changed files with 49 additions and 38 deletions

1
.gitignore vendored
View File

@ -14,3 +14,4 @@
# Dependency directories (remove the comment below to include it)
# vendor/
*.png
*.cpu

View File

@ -31,6 +31,7 @@ type GlyphRend struct {
GlyphMesh *meshes.Mesh
InstancedBuf buffers.Buffer
GlyphMat *materials.Material
TextRunsBuf []TextRun
GlyphCount uint32
//NOTE: Because of the sad realities (bugs?) of CGO, passing an array in a struct
@ -60,7 +61,8 @@ func (gr *GlyphRend) DrawTextOpenGL01(text string, screenPos *gglm.Vec3, color *
//Color is RGBA in the range [0,1].
func (gr *GlyphRend) DrawTextOpenGLAbs(text string, screenPos *gglm.Vec3, color *gglm.Vec4) {
runs := gr.GetTextRuns(text)
runs := gr.TextRunsBuf[:]
gr.GetTextRuns(text, &runs)
if runs == nil {
return
}
@ -180,19 +182,20 @@ type TextRun struct {
IsLtr bool
}
func (gr *GlyphRend) GetTextRuns(t string) []TextRun {
func (gr *GlyphRend) GetTextRuns(t string, textRunsBuf *[]TextRun) {
//PERF: Might be better to pass a []TextRun buffer to avoid allocating on the heap
rs := []rune(t)
if len(rs) == 0 {
return nil
if len(t) == 0 {
return
}
runs := make([]TextRun, 0, 10)
rs := []rune(t)
runs := textRunsBuf
currRunScript := RuneInfos[rs[0]].ScriptTable
//TODO: We need to detect neutral characters through BiDi category, not being in common
//TODO: Diacritics go into things like 'Category_Mn' and don't necessairly follow the parent script (e.g. Arabic diacritics are NOT in unicode.Arabic).
//They should be part of the same run but right now we split them into their own run.
runStartIndex := 0
for i := 1; i < len(rs); i++ {
@ -216,9 +219,9 @@ func (gr *GlyphRend) GetTextRuns(t string) []TextRun {
//If we have a run without trailing neutrals or had a run of just neutrals (e.g. starting sentence with spaces)
//then the full run is added, otherwise we slice the run to put neturals in a separate run
if trailingCommonsCount == 0 || len(newRunRunes) == trailingCommonsCount {
runs = append(runs, TextRun{Runes: newRunRunes})
*runs = append(*runs, TextRun{Runes: newRunRunes})
} else {
runs = append(runs,
*runs = append(*runs,
TextRun{Runes: newRunRunes[:len(newRunRunes)-trailingCommonsCount]}, TextRun{Runes: newRunRunes[len(newRunRunes)-trailingCommonsCount:]})
}
@ -227,12 +230,12 @@ func (gr *GlyphRend) GetTextRuns(t string) []TextRun {
currRunScript = ri.ScriptTable
}
runs = append(runs, TextRun{Runes: rs[runStartIndex:]})
*runs = append(*runs, TextRun{Runes: rs[runStartIndex:]})
//Detect directionality of each run
for i := 0; i < len(runs); i++ {
for i := 0; i < len(*runs); i++ {
run := &runs[i]
run := &(*runs)[i]
bidiCat := BidiCategory_L
for _, r := range run.Runes {
if !unicode.Is(unicode.Common, r) {
@ -242,12 +245,12 @@ func (gr *GlyphRend) GetTextRuns(t string) []TextRun {
}
run.IsLtr = !(bidiCat == BidiCategory_R || bidiCat == BidiCategory_AL || bidiCat == BidiCategory_RLE || bidiCat == BidiCategory_RLO || bidiCat == BidiCategory_RLI || bidiCat == BidiCategory_RLM)
}
return runs
}
func (gr *GlyphRend) glyphFromRunes(curr, prev, next rune) FontAtlasGlyph {
//PERF: Map access times are absolute garbage to the point that ~85%+ of the runtime of this func
//is spent reading from maps :). Using nSet or fMap or similar would be a lot better.
type PosCtx int
const (
PosCtx_start PosCtx = iota
@ -438,6 +441,7 @@ func NewGlyphRend(fontFile string, fontOptions *truetype.Options, screenWidth, s
gr := &GlyphRend{
GlyphCount: 0,
GlyphVBO: make([]float32, floatsPerGlyph*MaxGlyphsPerBatch),
TextRunsBuf: make([]TextRun, 0, 20),
SpacesPerTab: 4,
}

View File

@ -1,5 +1,5 @@
[Window][Debug##Default]
Pos=814,53
Size=396,114
Size=399,134
Collapsed=0

46
main.go
View File

@ -2,7 +2,10 @@ package main
import (
"fmt"
"math"
"math/rand"
"os"
"runtime/pprof"
"github.com/bloeys/gglm/gglm"
"github.com/bloeys/nmage/engine"
@ -10,6 +13,7 @@ import (
"github.com/bloeys/nmage/materials"
"github.com/bloeys/nmage/meshes"
"github.com/bloeys/nmage/renderer/rend3dgl"
"github.com/bloeys/nmage/timing"
nmageimgui "github.com/bloeys/nmage/ui/imgui"
"github.com/bloeys/nterm/glyphs"
"github.com/golang/freetype/truetype"
@ -74,12 +78,11 @@ func main() {
//Don't flash white
p.win.SDLWin.GLSwap()
// var pf, _ = os.Create("pprof.cpu")
// defer pf.Close()
// pprof.StartCPUProfile(pf)
var pf, _ = os.Create("pprof.cpu")
defer pf.Close()
pprof.StartCPUProfile(pf)
engine.Run(p, p.win, p.imguiInfo)
// pprof.StopCPUProfile()
pprof.StopCPUProfile()
}
func (p *program) Init() {
@ -164,7 +167,8 @@ func (p *program) Update() {
imgui.InputText("", &textToShow)
if imgui.Button("Print Runs") {
runs := p.GlyphRend.GetTextRuns(textToShow)
runs := make([]glyphs.TextRun, 0, 20)
p.GlyphRend.GetTextRuns(textToShow, &runs)
for _, run := range runs {
fmt.Printf("%s; runes: %#x\n\n", string(run.Runes), run.Runes)
}
@ -179,9 +183,12 @@ func (p *program) Update() {
p.GlyphRend.GlyphMat.SetUnifInt32("drawBounds", 0)
}
}
imgui.Checkbox("Draw many", &drawManyLines)
}
var isDrawingBounds = false
var drawManyLines = false
var textToShow = " Hello there يا friend. اسمي عمر wow!"
var xOff float32 = 0
@ -215,23 +222,22 @@ func (p *program) Render() {
b = 0
}
textColor := gglm.NewVec4(r, g, b, 1)
str := textToShow
// str := "مرحبا بك my friend"
// str := "my, friend"
// str := " hello there يا friend. أسمي عمر wow"
// str := " ijojo\n\n Hello there, friend|. pq?\n ABCDEFG\tHIJKLMNOPQRSTUVWXYZ\nمرحبا بك"
// str := " ijojo\n\n Hello there, friend|. pq?\n ABCDEFG\tHIJKLMNOPQRSTUVWXYZ"
charCount := len([]rune(str))
fps := int(timing.GetAvgFPS())
textColor := gglm.NewVec4(r, g, b, 1)
if drawManyLines {
const charsPerFrame = 500_000
for i := 0; i < charsPerFrame/charCount; i++ {
p.GlyphRend.DrawTextOpenGLAbs(str, gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*5+yOff, 0), textColor)
// strLen := len(str)
// const charsPerFrame = 100_000
// for i := 0; i < charsPerFrame/strLen; i++ {
// p.GlyphRend.DrawTextOpenGLAbs(str, gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*8+yOff, 0), textColor)
// }
// fps := int(timing.GetAvgFPS())
// p.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.MaxGlyphsPerBatch), " chars/f: ", charsPerFrame, " chars/s: ", fps*charsPerFrame))
}
p.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.MaxGlyphsPerBatch), " chars/f: ", charsPerFrame, " chars/s: ", fps*charsPerFrame))
} else {
charsPerFrame := float64(charCount)
p.GlyphRend.DrawTextOpenGLAbs(str, gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*5+yOff, 0), textColor)
p.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.MaxGlyphsPerBatch), " chars/f: ", charsPerFrame, " chars/s: ", fps*int(charsPerFrame)))
}
}