Fix bug with atlas texture not being updated on GPU

This commit is contained in:
bloeys
2022-07-01 11:27:37 +04:00
parent 094cc1acdd
commit 7a3ce51063
4 changed files with 98 additions and 75 deletions

View File

@ -1,6 +1,7 @@
package glyphs package glyphs
import ( import (
"errors"
"os" "os"
"unicode" "unicode"
@ -85,15 +86,6 @@ func (gr *GlyphRend) DrawTextOpenGL(text string, screenPos *gglm.Vec3, color *gg
screenHeightF32 := float32(gr.ScreenHeight) screenHeightF32 := float32(gr.ScreenHeight)
screenPos.Set(screenPos.X()*screenWidthF32, screenPos.Y()*screenHeightF32, screenPos.Z()) 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 //Prepass to pre-allocate the buffer
rs := []rune(text) rs := []rune(text)
const floatsPerGlyph = 18 const floatsPerGlyph = 18
@ -152,7 +144,6 @@ func (gr *GlyphRend) Draw() {
gr.GlyphMat.Bind() gr.GlyphMat.Bind()
gl.DrawElementsInstanced(gl.TRIANGLES, gr.GlyphMesh.Buf.IndexBufCount, gl.UNSIGNED_INT, gl.PtrOffset(0), gr.GlyphCount) gl.DrawElementsInstanced(gl.TRIANGLES, gr.GlyphMesh.Buf.IndexBufCount, gl.UNSIGNED_INT, gl.PtrOffset(0), gr.GlyphCount)
gr.InstancedBuf.UnBind()
gr.GlyphCount = 0 gr.GlyphCount = 0
gr.GlyphVBO = []float32{} gr.GlyphVBO = []float32{}
@ -161,6 +152,7 @@ func (gr *GlyphRend) Draw() {
func (gr *GlyphRend) SetFace(fontOptions *truetype.Options) { func (gr *GlyphRend) SetFace(fontOptions *truetype.Options) {
face := truetype.NewFace(gr.Atlas.Font, fontOptions) face := truetype.NewFace(gr.Atlas.Font, fontOptions)
gr.Atlas = NewFontAtlasFromFont(gr.Atlas.Font, face, uint(fontOptions.Size)) gr.Atlas = NewFontAtlasFromFont(gr.Atlas.Font, face, uint(fontOptions.Size))
gr.updateFontAtlasTexture("temp-atlas")
} }
func (gr *GlyphRend) SetFontFromFile(fontFile string, fontOptions *truetype.Options) error { 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.Atlas = atlas
gr.updateFontAtlasTexture(fontFile)
return nil return nil
} }
func (gr *GlyphRend) SetScreenSize(screenWidth, screenHeight int32) { //updateFontAtlasTexture uploads the texture representing the font atlas to the GPU
gr.ScreenWidth = screenWidth //and updates the GlyphRend.AtlasTex field.
gr.ScreenHeight = screenHeight //
} //Any old textures are deleted
func (gr *GlyphRend) updateFontAtlasTexture(fontFile string) error {
func NewGlyphRend(fontFile string, fontOptions *truetype.Options, screenWidth, screenHeight int32) (*GlyphRend, error) { if gr.AtlasTex != nil {
gl.DeleteTextures(1, &gr.AtlasTex.TexID)
atlas, err := NewFontAtlasFromFile(fontFile, fontOptions)
if err != nil {
return nil, err
} }
//Create OpenGL texture pngFileName := fontFile + "-atlas.png"
pngFileName := fontFile + "atlas.png" err := SaveImgToPNG(gr.Atlas.Img, pngFileName)
err = SaveImgToPNG(atlas.Img, pngFileName)
if err != nil { if err != nil {
return nil, err return err
} }
defer os.Remove(pngFileName)
atlasTex, err := assets.LoadPNGTexture(pngFileName) atlasTex, err := assets.LoadPNGTexture(pngFileName)
if err != nil { 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.BindTexture(gl.TEXTURE_2D, atlasTex.TexID)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) 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.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.BindTexture(gl.TEXTURE_2D, 0) 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 //Create glyph mesh
glyphMesh := &meshes.Mesh{ gr.GlyphMesh = &meshes.Mesh{
Name: "glypQuad", Name: "glypQuad",
//VertPos, UV, Color; Instanced attributes are stored separately //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,
-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, 0, 1, 2,
1, 3, 2, 1, 3, 2,
}) })
//Setup material //Setup material
glyphMat := materials.NewMaterial("glyphMat", "./res/shaders/glyph") gr.GlyphMat = materials.NewMaterial("glyphMat", "./res/shaders/glyph")
glyphMat.SetAttribute(glyphMesh.Buf) gr.GlyphMat.SetAttribute(gr.GlyphMesh.Buf)
gr.GlyphMat.DiffuseTex = gr.AtlasTex.TexID
//Create instanced buf and set its instanced attributes. //Create instanced buf and set its instanced attributes.
//Multiple VBOs under one VAO, one VBO for vertex data, and one VBO for instanced data. //Multiple VBOs under one VAO, one VBO for vertex data, and one VBO for instanced data.
instancedBuf := buffers.Buffer{ gr.InstancedBuf = buffers.Buffer{
VAOID: glyphMesh.Buf.VAOID, VAOID: gr.GlyphMesh.Buf.VAOID,
} }
instancedBuf.SetLayout( gr.InstancedBuf.SetLayout(
buffers.Element{ElementType: buffers.DataTypeVec2}, //UVST0 buffers.Element{ElementType: buffers.DataTypeVec2}, //UVST0
buffers.Element{ElementType: buffers.DataTypeVec2}, //UVST1 buffers.Element{ElementType: buffers.DataTypeVec2}, //UVST1
buffers.Element{ElementType: buffers.DataTypeVec2}, //UVST2 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 buffers.Element{ElementType: buffers.DataTypeVec3}, //ModelScale
) )
gl.GenBuffers(1, &instancedBuf.BufID) gl.GenBuffers(1, &gr.InstancedBuf.BufID)
if instancedBuf.BufID == 0 { if gr.InstancedBuf.BufID == 0 {
panic("Failed to create openGL buffer") return nil, errors.New("failed to create OpenGL VBO buffer")
} }
instancedBuf.Bind() gr.InstancedBuf.Bind()
gl.BindBuffer(gl.ARRAY_BUFFER, instancedBuf.BufID) gl.BindBuffer(gl.ARRAY_BUFFER, gr.InstancedBuf.BufID)
layout := instancedBuf.GetLayout() layout := gr.InstancedBuf.GetLayout()
//4 UV values //4 UV values
uvEle := layout[0] uvEle := layout[0]
gl.EnableVertexAttribArray(1) 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) gl.VertexAttribDivisor(1, 1)
uvEle = layout[1] uvEle = layout[1]
gl.EnableVertexAttribArray(2) 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) gl.VertexAttribDivisor(2, 1)
uvEle = layout[2] uvEle = layout[2]
gl.EnableVertexAttribArray(3) 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) gl.VertexAttribDivisor(3, 1)
uvEle = layout[3] uvEle = layout[3]
gl.EnableVertexAttribArray(4) 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) gl.VertexAttribDivisor(4, 1)
//Rest of instanced attributes //Rest of instanced attributes
colorEle := layout[4] colorEle := layout[4]
gl.EnableVertexAttribArray(5) 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) gl.VertexAttribDivisor(5, 1)
posEle := layout[5] posEle := layout[5]
gl.EnableVertexAttribArray(6) 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) gl.VertexAttribDivisor(6, 1)
scaleEle := layout[6] scaleEle := layout[6]
gl.EnableVertexAttribArray(7) 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.VertexAttribDivisor(7, 1)
gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BindBuffer(gl.ARRAY_BUFFER, 0)
instancedBuf.UnBind() gr.InstancedBuf.UnBind()
return &GlyphRend{ gr.SetScreenSize(screenWidth, screenHeight)
Atlas: atlas, return gr, nil
AtlasTex: &atlasTex,
GlyphMesh: glyphMesh,
InstancedBuf: instancedBuf,
GlyphMat: glyphMat,
GlyphCount: 0,
GlyphVBO: make([]float32, 0),
ScreenWidth: screenWidth,
ScreenHeight: screenHeight,
}, nil
} }

27
main.go
View File

@ -1,8 +1,6 @@
package main package main
import ( import (
"time"
"github.com/bloeys/gglm/gglm" "github.com/bloeys/gglm/gglm"
"github.com/bloeys/nmage/engine" "github.com/bloeys/nmage/engine"
"github.com/bloeys/nmage/input" "github.com/bloeys/nmage/input"
@ -22,6 +20,7 @@ type program struct {
imguiInfo nmageimgui.ImguiInfo imguiInfo nmageimgui.ImguiInfo
FontSize uint32 FontSize uint32
Dpi float64
GlyphRend *glyphs.GlyphRend GlyphRend *glyphs.GlyphRend
} }
@ -71,10 +70,14 @@ func main() {
func (p *program) Init() { 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 w, h := p.win.SDLWin.GetSize()
p.GlyphRend, err = glyphs.NewGlyphRend("./res/fonts/Consolas.ttf", &truetype.Options{Size: float64(p.FontSize), DPI: 72}, w, h) p.GlyphRend, err = glyphs.NewGlyphRend("./res/fonts/Consolas.ttf", &truetype.Options{Size: float64(p.FontSize), DPI: p.Dpi}, w, h)
if err != nil { if err != nil {
panic("Failed to create atlas from font file. Err: " + err.Error()) 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() { func (p *program) FrameStart() {
frameStartTime = time.Now()
} }
func (p *program) Update() { func (p *program) Update() {
@ -97,6 +96,10 @@ func (p *program) Update() {
p.shouldRun = false p.shouldRun = false
} }
if input.KeyClicked(sdl.K_SPACE) {
p.handleWindowResize()
}
fontSizeChanged := false fontSizeChanged := false
if input.KeyClicked(sdl.K_KP_PLUS) { if input.KeyClicked(sdl.K_KP_PLUS) {
p.FontSize += 2 p.FontSize += 2
@ -107,7 +110,7 @@ func (p *program) Update() {
} }
if fontSizeChanged { 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() { func (p *program) FrameEnd() {
frameTime = time.Since(frameStartTime)
} }
func (g *program) GetWindow() *engine.Window { func (g *program) GetWindow() *engine.Window {
@ -140,6 +142,9 @@ func (p *program) Deinit() {
} }
func (p *program) handleWindowResize() { func (p *program) handleWindowResize() {
println("Old size:", p.GlyphRend.ScreenWidth, ",", p.GlyphRend.ScreenHeight)
w, h := p.win.SDLWin.GetSize() w, h := p.win.SDLWin.GetSize()
p.GlyphRend.SetScreenSize(w, h) p.GlyphRend.SetScreenSize(w, h)
println("New size:", w, ",", h, "\n")
} }

View File

@ -1,13 +1,13 @@
#version 410 #version 410
uniform sampler2D diffTex;
in vec4 vertColor; in vec4 vertColor;
in vec2 vertUV0; in vec2 vertUV0;
in vec3 fragPos; in vec3 fragPos;
out vec4 fragColor; out vec4 fragColor;
uniform sampler2D diffTex;
void main() void main()
{ {
vec4 texColor = texture(diffTex, vertUV0); vec4 texColor = texture(diffTex, vertUV0);

View File

@ -6,7 +6,6 @@ layout(location=5) in vec4 vertColorIn;
layout(location=6) in vec3 modelPos; layout(location=6) in vec3 modelPos;
layout(location=7) in vec3 modelScale; layout(location=7) in vec3 modelScale;
out vec2 vertUV0; out vec2 vertUV0;
out vec4 vertColor; out vec4 vertColor;
out vec3 fragPos; out vec3 fragPos;