From 7a3ce510634aa352e46031fe124b31dfc2028516 Mon Sep 17 00:00:00 2001 From: bloeys Date: Fri, 1 Jul 2022 11:27:37 +0400 Subject: [PATCH] Fix bug with atlas texture not being updated on GPU --- glyphs/glyphs.go | 141 ++++++++++++++++++++---------------- main.go | 27 ++++--- res/shaders/glyph.frag.glsl | 4 +- res/shaders/glyph.vert.glsl | 1 - 4 files changed, 98 insertions(+), 75 deletions(-) diff --git a/glyphs/glyphs.go b/glyphs/glyphs.go index 727a598..f21fa58 100755 --- a/glyphs/glyphs.go +++ b/glyphs/glyphs.go @@ -1,6 +1,7 @@ package glyphs import ( + "errors" "os" "unicode" @@ -85,15 +86,6 @@ func (gr *GlyphRend) DrawTextOpenGL(text string, screenPos *gglm.Vec3, color *gg screenHeightF32 := float32(gr.ScreenHeight) screenPos.Set(screenPos.X()*screenWidthF32, screenPos.Y()*screenHeightF32, screenPos.Z()) - //The projection matrix fits the screen size. This is needed so we can size and position characters correctly. - projMtx := gglm.Ortho(0, screenWidthF32, screenHeightF32, 0, 0.1, 10) - viewMtx := gglm.LookAt(gglm.NewVec3(0, 0, -1), gglm.NewVec3(0, 0, 0), gglm.NewVec3(0, 1, 0)) - projViewMtx := projMtx.Clone().Mul(viewMtx) - - gr.GlyphMat.DiffuseTex = gr.AtlasTex.TexID - gr.GlyphMat.SetUnifMat4("projViewMat", &projViewMtx.Mat4) - gr.GlyphMat.Bind() - //Prepass to pre-allocate the buffer rs := []rune(text) const floatsPerGlyph = 18 @@ -152,7 +144,6 @@ func (gr *GlyphRend) Draw() { gr.GlyphMat.Bind() gl.DrawElementsInstanced(gl.TRIANGLES, gr.GlyphMesh.Buf.IndexBufCount, gl.UNSIGNED_INT, gl.PtrOffset(0), gr.GlyphCount) - gr.InstancedBuf.UnBind() gr.GlyphCount = 0 gr.GlyphVBO = []float32{} @@ -161,6 +152,7 @@ func (gr *GlyphRend) Draw() { func (gr *GlyphRend) SetFace(fontOptions *truetype.Options) { face := truetype.NewFace(gr.Atlas.Font, fontOptions) gr.Atlas = NewFontAtlasFromFont(gr.Atlas.Font, face, uint(fontOptions.Size)) + gr.updateFontAtlasTexture("temp-atlas") } func (gr *GlyphRend) SetFontFromFile(fontFile string, fontOptions *truetype.Options) error { @@ -171,33 +163,36 @@ func (gr *GlyphRend) SetFontFromFile(fontFile string, fontOptions *truetype.Opti } gr.Atlas = atlas + gr.updateFontAtlasTexture(fontFile) return nil } -func (gr *GlyphRend) SetScreenSize(screenWidth, screenHeight int32) { - gr.ScreenWidth = screenWidth - gr.ScreenHeight = screenHeight -} +//updateFontAtlasTexture uploads the texture representing the font atlas to the GPU +//and updates the GlyphRend.AtlasTex field. +// +//Any old textures are deleted +func (gr *GlyphRend) updateFontAtlasTexture(fontFile string) error { -func NewGlyphRend(fontFile string, fontOptions *truetype.Options, screenWidth, screenHeight int32) (*GlyphRend, error) { - - atlas, err := NewFontAtlasFromFile(fontFile, fontOptions) - if err != nil { - return nil, err + if gr.AtlasTex != nil { + gl.DeleteTextures(1, &gr.AtlasTex.TexID) } - //Create OpenGL texture - pngFileName := fontFile + "atlas.png" - err = SaveImgToPNG(atlas.Img, pngFileName) + pngFileName := fontFile + "-atlas.png" + err := SaveImgToPNG(gr.Atlas.Img, pngFileName) if err != nil { - return nil, err + return err } + defer os.Remove(pngFileName) atlasTex, err := assets.LoadPNGTexture(pngFileName) if err != nil { - return nil, err + return err } - os.Remove(pngFileName) + gr.AtlasTex = &atlasTex + + //TODO: We want a function to load without caching. For now we clear manually + delete(assets.Textures, atlasTex.TexID) + delete(assets.TexturePaths, pngFileName) gl.BindTexture(gl.TEXTURE_2D, atlasTex.TexID) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) @@ -206,8 +201,43 @@ func NewGlyphRend(fontFile string, fontOptions *truetype.Options, screenWidth, s gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.BindTexture(gl.TEXTURE_2D, 0) + return nil +} + +func (gr *GlyphRend) SetScreenSize(screenWidth, screenHeight int32) { + + gr.ScreenWidth = screenWidth + gr.ScreenHeight = screenHeight + + //The projection matrix fits the screen size. This is needed so we can size and position characters correctly. + projMtx := gglm.Ortho(0, float32(screenWidth), float32(screenHeight), 0, 0.1, 10) + viewMtx := gglm.LookAt(gglm.NewVec3(0, 0, -1), gglm.NewVec3(0, 0, 0), gglm.NewVec3(0, 1, 0)) + projViewMtx := projMtx.Clone().Mul(viewMtx) + + gr.GlyphMat.DiffuseTex = gr.AtlasTex.TexID + gr.GlyphMat.SetUnifMat4("projViewMat", &projViewMtx.Mat4) +} + +func NewGlyphRend(fontFile string, fontOptions *truetype.Options, screenWidth, screenHeight int32) (*GlyphRend, error) { + + gr := &GlyphRend{ + GlyphCount: 0, + GlyphVBO: make([]float32, 0), + } + + atlas, err := NewFontAtlasFromFile(fontFile, fontOptions) + if err != nil { + return nil, err + } + gr.Atlas = atlas + + err = gr.updateFontAtlasTexture(fontFile) + if err != nil { + return nil, err + } + //Create glyph mesh - glyphMesh := &meshes.Mesh{ + gr.GlyphMesh = &meshes.Mesh{ Name: "glypQuad", //VertPos, UV, Color; Instanced attributes are stored separately @@ -216,28 +246,29 @@ func NewGlyphRend(fontFile string, fontOptions *truetype.Options, screenWidth, s ), } - glyphMesh.Buf.SetData([]float32{ + gr.GlyphMesh.Buf.SetData([]float32{ -0.5, -0.5, 0, 0.5, -0.5, 0, -0.5, 0.5, 0, 0.5, 0.5, 0, }) - glyphMesh.Buf.SetIndexBufData([]uint32{ + gr.GlyphMesh.Buf.SetIndexBufData([]uint32{ 0, 1, 2, 1, 3, 2, }) //Setup material - glyphMat := materials.NewMaterial("glyphMat", "./res/shaders/glyph") - glyphMat.SetAttribute(glyphMesh.Buf) + gr.GlyphMat = materials.NewMaterial("glyphMat", "./res/shaders/glyph") + gr.GlyphMat.SetAttribute(gr.GlyphMesh.Buf) + gr.GlyphMat.DiffuseTex = gr.AtlasTex.TexID //Create instanced buf and set its instanced attributes. //Multiple VBOs under one VAO, one VBO for vertex data, and one VBO for instanced data. - instancedBuf := buffers.Buffer{ - VAOID: glyphMesh.Buf.VAOID, + gr.InstancedBuf = buffers.Buffer{ + VAOID: gr.GlyphMesh.Buf.VAOID, } - instancedBuf.SetLayout( + gr.InstancedBuf.SetLayout( buffers.Element{ElementType: buffers.DataTypeVec2}, //UVST0 buffers.Element{ElementType: buffers.DataTypeVec2}, //UVST1 buffers.Element{ElementType: buffers.DataTypeVec2}, //UVST2 @@ -248,67 +279,55 @@ func NewGlyphRend(fontFile string, fontOptions *truetype.Options, screenWidth, s buffers.Element{ElementType: buffers.DataTypeVec3}, //ModelScale ) - gl.GenBuffers(1, &instancedBuf.BufID) - if instancedBuf.BufID == 0 { - panic("Failed to create openGL buffer") + gl.GenBuffers(1, &gr.InstancedBuf.BufID) + if gr.InstancedBuf.BufID == 0 { + return nil, errors.New("failed to create OpenGL VBO buffer") } - instancedBuf.Bind() - gl.BindBuffer(gl.ARRAY_BUFFER, instancedBuf.BufID) - layout := instancedBuf.GetLayout() + gr.InstancedBuf.Bind() + gl.BindBuffer(gl.ARRAY_BUFFER, gr.InstancedBuf.BufID) + layout := gr.InstancedBuf.GetLayout() //4 UV values uvEle := layout[0] gl.EnableVertexAttribArray(1) - gl.VertexAttribPointer(1, uvEle.ElementType.CompCount(), uvEle.ElementType.GLType(), false, instancedBuf.Stride, gl.PtrOffset(uvEle.Offset)) + gl.VertexAttribPointer(1, uvEle.ElementType.CompCount(), uvEle.ElementType.GLType(), false, gr.InstancedBuf.Stride, gl.PtrOffset(uvEle.Offset)) gl.VertexAttribDivisor(1, 1) uvEle = layout[1] gl.EnableVertexAttribArray(2) - gl.VertexAttribPointer(2, uvEle.ElementType.CompCount(), uvEle.ElementType.GLType(), false, instancedBuf.Stride, gl.PtrOffset(uvEle.Offset)) + gl.VertexAttribPointer(2, uvEle.ElementType.CompCount(), uvEle.ElementType.GLType(), false, gr.InstancedBuf.Stride, gl.PtrOffset(uvEle.Offset)) gl.VertexAttribDivisor(2, 1) uvEle = layout[2] gl.EnableVertexAttribArray(3) - gl.VertexAttribPointer(3, uvEle.ElementType.CompCount(), uvEle.ElementType.GLType(), false, instancedBuf.Stride, gl.PtrOffset(uvEle.Offset)) + gl.VertexAttribPointer(3, uvEle.ElementType.CompCount(), uvEle.ElementType.GLType(), false, gr.InstancedBuf.Stride, gl.PtrOffset(uvEle.Offset)) gl.VertexAttribDivisor(3, 1) uvEle = layout[3] gl.EnableVertexAttribArray(4) - gl.VertexAttribPointer(4, uvEle.ElementType.CompCount(), uvEle.ElementType.GLType(), false, instancedBuf.Stride, gl.PtrOffset(uvEle.Offset)) + gl.VertexAttribPointer(4, uvEle.ElementType.CompCount(), uvEle.ElementType.GLType(), false, gr.InstancedBuf.Stride, gl.PtrOffset(uvEle.Offset)) gl.VertexAttribDivisor(4, 1) //Rest of instanced attributes colorEle := layout[4] gl.EnableVertexAttribArray(5) - gl.VertexAttribPointer(5, colorEle.ElementType.CompCount(), colorEle.ElementType.GLType(), false, instancedBuf.Stride, gl.PtrOffset(colorEle.Offset)) + gl.VertexAttribPointer(5, colorEle.ElementType.CompCount(), colorEle.ElementType.GLType(), false, gr.InstancedBuf.Stride, gl.PtrOffset(colorEle.Offset)) gl.VertexAttribDivisor(5, 1) posEle := layout[5] gl.EnableVertexAttribArray(6) - gl.VertexAttribPointer(6, posEle.ElementType.CompCount(), posEle.ElementType.GLType(), false, instancedBuf.Stride, gl.PtrOffset(posEle.Offset)) + gl.VertexAttribPointer(6, posEle.ElementType.CompCount(), posEle.ElementType.GLType(), false, gr.InstancedBuf.Stride, gl.PtrOffset(posEle.Offset)) gl.VertexAttribDivisor(6, 1) scaleEle := layout[6] gl.EnableVertexAttribArray(7) - gl.VertexAttribPointer(7, scaleEle.ElementType.CompCount(), scaleEle.ElementType.GLType(), false, instancedBuf.Stride, gl.PtrOffset(scaleEle.Offset)) + gl.VertexAttribPointer(7, scaleEle.ElementType.CompCount(), scaleEle.ElementType.GLType(), false, gr.InstancedBuf.Stride, gl.PtrOffset(scaleEle.Offset)) gl.VertexAttribDivisor(7, 1) gl.BindBuffer(gl.ARRAY_BUFFER, 0) - instancedBuf.UnBind() + gr.InstancedBuf.UnBind() - return &GlyphRend{ - Atlas: atlas, - AtlasTex: &atlasTex, - - GlyphMesh: glyphMesh, - InstancedBuf: instancedBuf, - GlyphMat: glyphMat, - - GlyphCount: 0, - GlyphVBO: make([]float32, 0), - - ScreenWidth: screenWidth, - ScreenHeight: screenHeight, - }, nil + gr.SetScreenSize(screenWidth, screenHeight) + return gr, nil } diff --git a/main.go b/main.go index 393fcbf..d05fb31 100755 --- a/main.go +++ b/main.go @@ -1,8 +1,6 @@ package main import ( - "time" - "github.com/bloeys/gglm/gglm" "github.com/bloeys/nmage/engine" "github.com/bloeys/nmage/input" @@ -22,6 +20,7 @@ type program struct { imguiInfo nmageimgui.ImguiInfo FontSize uint32 + Dpi float64 GlyphRend *glyphs.GlyphRend } @@ -71,10 +70,14 @@ func main() { func (p *program) Init() { - w, h := p.win.SDLWin.GetSize() + dpi, _, _, err := sdl.GetDisplayDPI(0) + if err != nil { + panic("Failed to get display DPI. Err: " + err.Error()) + } + println("DPI:", dpi) - var err error - p.GlyphRend, err = glyphs.NewGlyphRend("./res/fonts/Consolas.ttf", &truetype.Options{Size: float64(p.FontSize), DPI: 72}, w, h) + w, h := p.win.SDLWin.GetSize() + p.GlyphRend, err = glyphs.NewGlyphRend("./res/fonts/Consolas.ttf", &truetype.Options{Size: float64(p.FontSize), DPI: p.Dpi}, w, h) if err != nil { panic("Failed to create atlas from font file. Err: " + err.Error()) } @@ -84,11 +87,7 @@ func (p *program) Start() { } -var frameStartTime time.Time -var frameTime time.Duration = 16 * time.Millisecond - func (p *program) FrameStart() { - frameStartTime = time.Now() } func (p *program) Update() { @@ -97,6 +96,10 @@ func (p *program) Update() { p.shouldRun = false } + if input.KeyClicked(sdl.K_SPACE) { + p.handleWindowResize() + } + fontSizeChanged := false if input.KeyClicked(sdl.K_KP_PLUS) { p.FontSize += 2 @@ -107,7 +110,7 @@ func (p *program) Update() { } if fontSizeChanged { - p.GlyphRend.SetFace(&truetype.Options{Size: float64(p.FontSize), DPI: 72}) + p.GlyphRend.SetFace(&truetype.Options{Size: float64(p.FontSize), DPI: p.Dpi}) } } @@ -120,7 +123,6 @@ func (p *program) Render() { } func (p *program) FrameEnd() { - frameTime = time.Since(frameStartTime) } func (g *program) GetWindow() *engine.Window { @@ -140,6 +142,9 @@ func (p *program) Deinit() { } func (p *program) handleWindowResize() { + + println("Old size:", p.GlyphRend.ScreenWidth, ",", p.GlyphRend.ScreenHeight) w, h := p.win.SDLWin.GetSize() p.GlyphRend.SetScreenSize(w, h) + println("New size:", w, ",", h, "\n") } diff --git a/res/shaders/glyph.frag.glsl b/res/shaders/glyph.frag.glsl index 17493cd..1c347ab 100755 --- a/res/shaders/glyph.frag.glsl +++ b/res/shaders/glyph.frag.glsl @@ -1,13 +1,13 @@ #version 410 -uniform sampler2D diffTex; - in vec4 vertColor; in vec2 vertUV0; in vec3 fragPos; out vec4 fragColor; +uniform sampler2D diffTex; + void main() { vec4 texColor = texture(diffTex, vertUV0); diff --git a/res/shaders/glyph.vert.glsl b/res/shaders/glyph.vert.glsl index 6d35c4f..d7215b0 100755 --- a/res/shaders/glyph.vert.glsl +++ b/res/shaders/glyph.vert.glsl @@ -6,7 +6,6 @@ layout(location=5) in vec4 vertColorIn; layout(location=6) in vec3 modelPos; layout(location=7) in vec3 modelScale; - out vec2 vertUV0; out vec4 vertColor; out vec3 fragPos;