mirror of
https://github.com/bloeys/nterm.git
synced 2025-12-29 14:38:19 +00:00
Avoid heap allocating in GetTextRuns+Perf notes+pprof by default
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -14,3 +14,4 @@
|
|||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories (remove the comment below to include it)
|
||||||
# vendor/
|
# vendor/
|
||||||
*.png
|
*.png
|
||||||
|
*.cpu
|
||||||
@ -31,6 +31,7 @@ type GlyphRend struct {
|
|||||||
GlyphMesh *meshes.Mesh
|
GlyphMesh *meshes.Mesh
|
||||||
InstancedBuf buffers.Buffer
|
InstancedBuf buffers.Buffer
|
||||||
GlyphMat *materials.Material
|
GlyphMat *materials.Material
|
||||||
|
TextRunsBuf []TextRun
|
||||||
|
|
||||||
GlyphCount uint32
|
GlyphCount uint32
|
||||||
//NOTE: Because of the sad realities (bugs?) of CGO, passing an array in a struct
|
//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].
|
//Color is RGBA in the range [0,1].
|
||||||
func (gr *GlyphRend) DrawTextOpenGLAbs(text string, screenPos *gglm.Vec3, color *gglm.Vec4) {
|
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 {
|
if runs == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -180,19 +182,20 @@ type TextRun struct {
|
|||||||
IsLtr bool
|
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
|
if len(t) == 0 {
|
||||||
rs := []rune(t)
|
return
|
||||||
|
|
||||||
if len(rs) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
runs := make([]TextRun, 0, 10)
|
rs := []rune(t)
|
||||||
|
|
||||||
|
runs := textRunsBuf
|
||||||
currRunScript := RuneInfos[rs[0]].ScriptTable
|
currRunScript := RuneInfos[rs[0]].ScriptTable
|
||||||
|
|
||||||
//TODO: We need to detect neutral characters through BiDi category, not being in common
|
//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
|
runStartIndex := 0
|
||||||
for i := 1; i < len(rs); i++ {
|
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)
|
//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
|
//then the full run is added, otherwise we slice the run to put neturals in a separate run
|
||||||
if trailingCommonsCount == 0 || len(newRunRunes) == trailingCommonsCount {
|
if trailingCommonsCount == 0 || len(newRunRunes) == trailingCommonsCount {
|
||||||
runs = append(runs, TextRun{Runes: newRunRunes})
|
*runs = append(*runs, TextRun{Runes: newRunRunes})
|
||||||
} else {
|
} else {
|
||||||
runs = append(runs,
|
*runs = append(*runs,
|
||||||
TextRun{Runes: newRunRunes[:len(newRunRunes)-trailingCommonsCount]}, TextRun{Runes: newRunRunes[len(newRunRunes)-trailingCommonsCount:]})
|
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
|
currRunScript = ri.ScriptTable
|
||||||
}
|
}
|
||||||
|
|
||||||
runs = append(runs, TextRun{Runes: rs[runStartIndex:]})
|
*runs = append(*runs, TextRun{Runes: rs[runStartIndex:]})
|
||||||
|
|
||||||
//Detect directionality of each run
|
//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
|
bidiCat := BidiCategory_L
|
||||||
for _, r := range run.Runes {
|
for _, r := range run.Runes {
|
||||||
if !unicode.Is(unicode.Common, r) {
|
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)
|
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 {
|
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
|
type PosCtx int
|
||||||
const (
|
const (
|
||||||
PosCtx_start PosCtx = iota
|
PosCtx_start PosCtx = iota
|
||||||
@ -438,6 +441,7 @@ func NewGlyphRend(fontFile string, fontOptions *truetype.Options, screenWidth, s
|
|||||||
gr := &GlyphRend{
|
gr := &GlyphRend{
|
||||||
GlyphCount: 0,
|
GlyphCount: 0,
|
||||||
GlyphVBO: make([]float32, floatsPerGlyph*MaxGlyphsPerBatch),
|
GlyphVBO: make([]float32, floatsPerGlyph*MaxGlyphsPerBatch),
|
||||||
|
TextRunsBuf: make([]TextRun, 0, 20),
|
||||||
SpacesPerTab: 4,
|
SpacesPerTab: 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
[Window][Debug##Default]
|
[Window][Debug##Default]
|
||||||
Pos=814,53
|
Pos=814,53
|
||||||
Size=396,114
|
Size=399,134
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
|
|||||||
48
main.go
48
main.go
@ -2,7 +2,10 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"runtime/pprof"
|
||||||
|
|
||||||
"github.com/bloeys/gglm/gglm"
|
"github.com/bloeys/gglm/gglm"
|
||||||
"github.com/bloeys/nmage/engine"
|
"github.com/bloeys/nmage/engine"
|
||||||
@ -10,6 +13,7 @@ import (
|
|||||||
"github.com/bloeys/nmage/materials"
|
"github.com/bloeys/nmage/materials"
|
||||||
"github.com/bloeys/nmage/meshes"
|
"github.com/bloeys/nmage/meshes"
|
||||||
"github.com/bloeys/nmage/renderer/rend3dgl"
|
"github.com/bloeys/nmage/renderer/rend3dgl"
|
||||||
|
"github.com/bloeys/nmage/timing"
|
||||||
nmageimgui "github.com/bloeys/nmage/ui/imgui"
|
nmageimgui "github.com/bloeys/nmage/ui/imgui"
|
||||||
"github.com/bloeys/nterm/glyphs"
|
"github.com/bloeys/nterm/glyphs"
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
@ -74,12 +78,11 @@ func main() {
|
|||||||
//Don't flash white
|
//Don't flash white
|
||||||
p.win.SDLWin.GLSwap()
|
p.win.SDLWin.GLSwap()
|
||||||
|
|
||||||
// var pf, _ = os.Create("pprof.cpu")
|
var pf, _ = os.Create("pprof.cpu")
|
||||||
// defer pf.Close()
|
defer pf.Close()
|
||||||
|
pprof.StartCPUProfile(pf)
|
||||||
// pprof.StartCPUProfile(pf)
|
|
||||||
engine.Run(p, p.win, p.imguiInfo)
|
engine.Run(p, p.win, p.imguiInfo)
|
||||||
// pprof.StopCPUProfile()
|
pprof.StopCPUProfile()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *program) Init() {
|
func (p *program) Init() {
|
||||||
@ -164,7 +167,8 @@ func (p *program) Update() {
|
|||||||
imgui.InputText("", &textToShow)
|
imgui.InputText("", &textToShow)
|
||||||
|
|
||||||
if imgui.Button("Print Runs") {
|
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 {
|
for _, run := range runs {
|
||||||
fmt.Printf("%s; runes: %#x\n\n", string(run.Runes), run.Runes)
|
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)
|
p.GlyphRend.GlyphMat.SetUnifInt32("drawBounds", 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
imgui.Checkbox("Draw many", &drawManyLines)
|
||||||
}
|
}
|
||||||
|
|
||||||
var isDrawingBounds = false
|
var isDrawingBounds = false
|
||||||
|
var drawManyLines = false
|
||||||
var textToShow = " Hello there يا friend. اسمي عمر wow!"
|
var textToShow = " Hello there يا friend. اسمي عمر wow!"
|
||||||
|
|
||||||
var xOff float32 = 0
|
var xOff float32 = 0
|
||||||
@ -215,23 +222,22 @@ func (p *program) Render() {
|
|||||||
b = 0
|
b = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
textColor := gglm.NewVec4(r, g, b, 1)
|
|
||||||
str := textToShow
|
str := textToShow
|
||||||
// str := "مرحبا بك my friend"
|
charCount := len([]rune(str))
|
||||||
// str := "my, friend"
|
fps := int(timing.GetAvgFPS())
|
||||||
// str := " hello there يا friend. أسمي عمر wow"
|
textColor := gglm.NewVec4(r, g, b, 1)
|
||||||
// str := " ijojo\n\n Hello there, friend|. pq?\n ABCDEFG\tHIJKLMNOPQRSTUVWXYZ\nمرحبا بك"
|
if drawManyLines {
|
||||||
// str := " ijojo\n\n Hello there, friend|. pq?\n ABCDEFG\tHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
|
|
||||||
p.GlyphRend.DrawTextOpenGLAbs(str, gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*5+yOff, 0), textColor)
|
const charsPerFrame = 500_000
|
||||||
|
for i := 0; i < charsPerFrame/charCount; i++ {
|
||||||
// strLen := len(str)
|
p.GlyphRend.DrawTextOpenGLAbs(str, gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*5+yOff, 0), textColor)
|
||||||
// const charsPerFrame = 100_000
|
}
|
||||||
// for i := 0; i < charsPerFrame/strLen; i++ {
|
p.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.MaxGlyphsPerBatch), " chars/f: ", charsPerFrame, " chars/s: ", fps*charsPerFrame))
|
||||||
// p.GlyphRend.DrawTextOpenGLAbs(str, gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*8+yOff, 0), textColor)
|
} else {
|
||||||
// }
|
charsPerFrame := float64(charCount)
|
||||||
// fps := int(timing.GetAvgFPS())
|
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*charsPerFrame))
|
p.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.MaxGlyphsPerBatch), " chars/f: ", charsPerFrame, " chars/s: ", fps*int(charsPerFrame)))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user