Changes to minimize memory allocations and to ensure OpenGL objects cleanup (#148)

This commit is contained in:
nikitar020 2019-01-14 20:50:03 +00:00 committed by Liam Galvin
parent 4242442980
commit 43072eb024
6 changed files with 88 additions and 20 deletions

View File

@ -24,6 +24,7 @@ type Font struct {
texture uint32 // Holds the glyph texture id.
color color
ttf *truetype.Font
ttfFace font.Face
scale float32
linePadding float32
lineHeight float32
@ -51,10 +52,26 @@ func LoadFont(reader io.Reader, scale float32, windowWidth int, windowHeight int
//set screen resolution
resUniform := gl.GetUniformLocation(program, gl.Str("resolution\x00"))
gl.Uniform2f(resUniform, float32(windowWidth), float32(windowHeight))
gl.UseProgram(0)
return LoadTrueTypeFont(program, reader, scale)
}
func (f *Font) Free() {
for _, chr := range f.characters {
gl.DeleteTextures(1, &chr.textureID)
}
gl.DeleteBuffers(1, &f.vbo)
gl.DeleteVertexArrays(1, &f.vao)
gl.DeleteProgram(f.program)
f.vbo = 0
f.vao = 0
f.program = 0
}
//SetColor allows you to set the text color to be used when you draw the text
func (f *Font) SetColor(red float32, green float32, blue float32, alpha float32) {
f.color.r = red
@ -223,14 +240,7 @@ func (f *Font) GetRune(r rune) (*character, error) {
char := new(character)
//create new face to measure glyph diamensions
ttfFace := truetype.NewFace(f.ttf, &truetype.Options{
Size: float64(f.scale),
DPI: DPI,
Hinting: font.HintingFull,
})
gBnd, gAdv, ok := ttfFace.GlyphBounds(r)
gBnd, gAdv, ok := f.ttfFace.GlyphBounds(r)
if ok != true {
return nil, fmt.Errorf("ttf face glyphBounds error")
}

View File

@ -6,6 +6,7 @@ import (
"github.com/go-gl/gl/all-core/gl"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
)
type character struct {
@ -17,14 +18,14 @@ type character struct {
bearingV int //glyph bearing vertical
}
//LoadTrueTypeFont builds a set of textures based on a ttf files gylphs
//LoadTrueTypeFont builds a set of textures based on a ttf files glyphs
func LoadTrueTypeFont(program uint32, r io.Reader, scale float32) (*Font, error) {
data, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
//make Font stuct type
//make Font struct type
f := new(Font)
f.scale = scale
f.characters = map[rune]*character{}
@ -62,5 +63,12 @@ func LoadTrueTypeFont(program uint32, r io.Reader, scale float32) (*Font, error)
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
gl.BindVertexArray(0)
//create new face to measure glyph dimensions
f.ttfFace = truetype.NewFace(f.ttf, &truetype.Options {
Size: float64(f.scale),
DPI: DPI,
Hinting: font.HintingFull,
})
return f, nil
}

View File

@ -14,6 +14,26 @@ func NewFontMap(defaultFont *glfont.Font, defaultBoldFont *glfont.Font) *FontMap
}
}
func (fm *FontMap) Free() {
if fm.defaultFont != nil {
fm.defaultFont.Free()
fm.defaultFont = nil
}
if fm.defaultBoldFont != nil {
fm.defaultBoldFont.Free()
fm.defaultBoldFont = nil
}
}
func (fm *FontMap) AssignFonts(defaultFont *glfont.Font, defaultBoldFont *glfont.Font) {
fm.defaultFont.Free()
fm.defaultBoldFont.Free()
fm.defaultFont = defaultFont
fm.defaultBoldFont = defaultBoldFont
}
func (fm *FontMap) UpdateResolution(w int, h int) {
fm.defaultFont.UpdateResolution(w, h)
fm.defaultBoldFont.UpdateResolution(w, h)

View File

@ -40,8 +40,7 @@ func (gui *GUI) loadFonts() error {
if gui.fontMap == nil {
gui.fontMap = NewFontMap(defaultFont, boldFont)
} else {
gui.fontMap.defaultFont = defaultFont
gui.fontMap.defaultBoldFont = boldFont
gui.fontMap.AssignFonts(defaultFont, boldFont)
}
// add special non-ascii fonts here

View File

@ -16,6 +16,7 @@ import (
"github.com/liamg/aminal/terminal"
"github.com/liamg/aminal/version"
"go.uber.org/zap"
"unsafe"
)
type GUI struct {
@ -413,7 +414,7 @@ func (gui *GUI) redraw(defaultCell buffer.Cell) {
if y < len(lines) {
bufStr := ""
var builder strings.Builder
bold := false
dim := false
col := 0
@ -423,14 +424,14 @@ func (gui *GUI) redraw(defaultCell buffer.Cell) {
for x := 0; x < colCount; x++ {
if x < len(cells) {
cell := cells[x]
if bufStr != "" && (cell.Attr().Dim != dim || cell.Attr().Bold != bold || colour != cell.Fg()) {
if builder.Len() > 0 && (cell.Attr().Dim != dim || cell.Attr().Bold != bold || colour != cell.Fg()) {
var alpha float32 = 1.0
if dim {
alpha = 0.5
}
gui.renderer.DrawCellText(bufStr, uint(col), uint(y), alpha, colour, bold)
gui.renderer.DrawCellText(builder.String(), uint(col), uint(y), alpha, colour, bold)
col = x
bufStr = ""
builder.Reset()
}
dim = cell.Attr().Dim
colour = cell.Fg()
@ -439,15 +440,15 @@ func (gui *GUI) redraw(defaultCell buffer.Cell) {
if r == 0 {
r = ' '
}
bufStr += string(r)
builder.WriteRune(r)
}
}
if bufStr != "" {
if builder.Len() > 0 {
var alpha float32 = 1.0
if dim {
alpha = 0.5
}
gui.renderer.DrawCellText(bufStr, uint(col), uint(y), alpha, colour, bold)
gui.renderer.DrawCellText(builder.String(), uint(col), uint(y), alpha, colour, bold)
}
}
@ -526,6 +527,10 @@ func (gui *GUI) createWindowWithOpenGLVersion(major int, minor int) (*glfw.Windo
return window, nil
}
func (gui *GUI) onDebugMessage(source uint32, gltype uint32, id uint32, severity uint32, length int32, message string, userParam unsafe.Pointer) {
gui.logger.Infof("GL debug message: %s", message)
}
// initOpenGL initializes OpenGL and returns an intiialized program.
func (gui *GUI) createProgram() (uint32, error) {
if err := gl.Init(); err != nil {
@ -533,6 +538,12 @@ func (gui *GUI) createProgram() (uint32, error) {
}
gui.logger.Infof("OpenGL version %s", gl.GoStr(gl.GetString(gl.VERSION)))
if gui.config.DebugMode {
// This allows to catch some OpenGL errors
gl.DebugMessageCallback(gui.onDebugMessage, nil)
gl.Enable(gl.DEBUG_OUTPUT)
}
gui.logger.Debugf("Compiling shaders...")
vertexShader, err := compileShader(vertexShaderSource, gl.VERTEX_SHADER)
@ -550,6 +561,9 @@ func (gui *GUI) createProgram() (uint32, error) {
gl.AttachShader(prog, fragmentShader)
gl.LinkProgram(prog)
gl.DeleteShader(vertexShader)
gl.DeleteShader(fragmentShader)
return prog, nil
}

View File

@ -155,7 +155,6 @@ func (rect *rectangle) setColour(colour [3]float32) {
}
func (rect *rectangle) Free() {
gl.UseProgram(rect.prog)
gl.DeleteVertexArrays(1, &rect.vao)
gl.DeleteBuffers(1, &rect.vbo)
gl.DeleteBuffers(1, &rect.cv)
@ -183,6 +182,24 @@ func NewOpenGLRenderer(config *config.Config, fontMap *FontMap, areaX int, areaY
return r
}
// This method ensures that all OpenGL resources are deleted correctly
func (r *OpenGLRenderer) Free() {
for _, rect := range r.rectangles {
rect.Free()
}
r.rectangles = map[[2]uint]*rectangle{}
for _, tex := range r.textureMap {
gl.DeleteTextures(1, &tex)
}
r.textureMap = map[*image.RGBA]uint32{}
r.fontMap.Free()
gl.DeleteProgram(r.program)
r.program = 0
}
func (r *OpenGLRenderer) GetTermSize() (uint, uint) {
return r.termCols, r.termRows
}