refactor debug info, measure render times
This commit is contained in:
parent
057e8907a9
commit
4a9afb4246
@ -52,13 +52,17 @@ func (g *Game) initImgui(win *glfw.Window) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) imgui() {
|
func (g *Game) imgui() {
|
||||||
|
|
||||||
|
io.Diagnostics.CgoCalls = runtime.NumCgoCall() - g.gui.lastframeCgoCalls
|
||||||
|
g.gui.lastframeCgoCalls = runtime.NumCgoCall()
|
||||||
|
|
||||||
if io.ShowDebugInfo {
|
if io.ShowDebugInfo {
|
||||||
imgui.SetNextWindowPosV(imgui.Vec2{}, imgui.ConditionAlways, imgui.Vec2{})
|
imgui.SetNextWindowPosV(imgui.Vec2{}, imgui.ConditionAlways, imgui.Vec2{})
|
||||||
imgui.SetNextWindowSize(imgui.Vec2{X: float32(io.DisplaySize[0]), Y: float32(io.DisplaySize[1])})
|
imgui.SetNextWindowSize(imgui.Vec2{X: float32(io.DisplaySize[0]), Y: float32(io.DisplaySize[1])})
|
||||||
if igwrap.Begin("F3", nil, igwrap.WindowFlagsOverlay) {
|
if igwrap.Begin("F3", nil, igwrap.WindowFlagsOverlay) {
|
||||||
igwrap.TextBackground("Gl01 compiled by %s/%s [%s/%s] (120AVG) %.1f FPS (%.3f frame)", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH, imgui.CurrentIO().Framerate(), 1000/imgui.CurrentIO().Framerate())
|
igwrap.TextBackground("Gl01 compiled by %s/%s [%s/%s] (120AVG) %.1f FPS (%.3f frame)", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH, imgui.CurrentIO().Framerate(), 1000/imgui.CurrentIO().Framerate())
|
||||||
igwrap.TextBackground("GLFW %s, Dear ImGUI %s", glfw.GetVersionString(), imgui.Version())
|
igwrap.TextBackground("GLFW %s, Dear ImGUI %s", glfw.GetVersionString(), imgui.Version())
|
||||||
igwrap.TextBackground("CgoCalls:%d (%d lastframe), Goroutines:%d", runtime.NumCgoCall(), runtime.NumCgoCall()-g.gui.lastframeCgoCalls, runtime.NumGoroutine())
|
igwrap.TextBackground("CgoCalls:%d (%d lastframe), Goroutines:%d", g.gui.lastframeCgoCalls, io.Diagnostics.CgoCalls, runtime.NumGoroutine())
|
||||||
igwrap.TextBlank()
|
igwrap.TextBlank()
|
||||||
|
|
||||||
pos := g.player.Position()
|
pos := g.player.Position()
|
||||||
@ -66,7 +70,6 @@ func (g *Game) imgui() {
|
|||||||
imgui.End()
|
imgui.End()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g.gui.lastframeCgoCalls = runtime.NumCgoCall()
|
|
||||||
|
|
||||||
if imgui.BeginV("Player", nil, imgui.WindowFlagsAlwaysAutoResize) {
|
if imgui.BeginV("Player", nil, imgui.WindowFlagsAlwaysAutoResize) {
|
||||||
pos := g.player.Position()
|
pos := g.player.Position()
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"edgaru089.ml/go/gl01/internal/igwrap/backend"
|
"edgaru089.ml/go/gl01/internal/igwrap/backend"
|
||||||
"edgaru089.ml/go/gl01/internal/io"
|
"edgaru089.ml/go/gl01/internal/io"
|
||||||
"edgaru089.ml/go/gl01/internal/render"
|
"edgaru089.ml/go/gl01/internal/render"
|
||||||
|
"edgaru089.ml/go/gl01/internal/util"
|
||||||
"edgaru089.ml/go/gl01/internal/util/itype"
|
"edgaru089.ml/go/gl01/internal/util/itype"
|
||||||
"edgaru089.ml/go/gl01/internal/world"
|
"edgaru089.ml/go/gl01/internal/world"
|
||||||
"edgaru089.ml/go/gl01/internal/world/worldgen"
|
"edgaru089.ml/go/gl01/internal/world/worldgen"
|
||||||
@ -174,7 +175,8 @@ const airAccel = 0.1
|
|||||||
// Update updates the game state, not necessarily in the main thread.
|
// Update updates the game state, not necessarily in the main thread.
|
||||||
func (g *Game) Update(win *glfw.Window, delta time.Duration) {
|
func (g *Game) Update(win *glfw.Window, delta time.Duration) {
|
||||||
backend.NewFrame()
|
backend.NewFrame()
|
||||||
imgui.ShowDemoWindow(nil)
|
|
||||||
|
clock := util.NewClock()
|
||||||
|
|
||||||
if !g.paused {
|
if !g.paused {
|
||||||
|
|
||||||
@ -242,7 +244,12 @@ func (g *Game) Update(win *glfw.Window, delta time.Duration) {
|
|||||||
g.player.SetSpeed(itype.Vec3d{})
|
g.player.SetSpeed(itype.Vec3d{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
io.Diagnostics.Times.Logic = clock.Restart()
|
||||||
|
|
||||||
|
imgui.ShowDemoWindow(nil)
|
||||||
g.imgui()
|
g.imgui()
|
||||||
|
|
||||||
|
io.Diagnostics.Times.GUI = clock.Restart()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render, called with a OpenGL context, renders the game.
|
// Render, called with a OpenGL context, renders the game.
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package io
|
package io
|
||||||
|
|
||||||
import "edgaru089.ml/go/gl01/internal/util/itype"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"edgaru089.ml/go/gl01/internal/util/itype"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DisplaySize itype.Vec2i // Size of the window viewport in pixels.
|
DisplaySize itype.Vec2i // Size of the window viewport in pixels.
|
||||||
@ -13,4 +17,19 @@ var (
|
|||||||
RenderPos, RenderDir itype.Vec3d // Position and Direction of view for the current render pass. Might be different for e.g. lighting passes
|
RenderPos, RenderDir itype.Vec3d // Position and Direction of view for the current render pass. Might be different for e.g. lighting passes
|
||||||
|
|
||||||
ShowDebugInfo bool // Show debug info (F3 screen)?
|
ShowDebugInfo bool // Show debug info (F3 screen)?
|
||||||
|
|
||||||
|
// Per-Frame Diagnostics information
|
||||||
|
Diagnostics struct {
|
||||||
|
CgoCalls int64 // Cgo calls in the last frame
|
||||||
|
Times struct { // Times spent
|
||||||
|
GUI, Logic, Render time.Duration
|
||||||
|
RenderPasses struct {
|
||||||
|
Depthmap,
|
||||||
|
Geometry,
|
||||||
|
SSAO,
|
||||||
|
Lighting,
|
||||||
|
Postfx time.Duration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
@ -2,7 +2,6 @@ package render
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"math"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@ -305,6 +304,9 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (r *WorldRenderer) Render(world *world.World, view *View) {
|
func (r *WorldRenderer) Render(world *world.World, view *View) {
|
||||||
|
allclock := util.NewClock()
|
||||||
|
lastclock := util.NewClock()
|
||||||
|
|
||||||
io.RenderPos = io.ViewPos
|
io.RenderPos = io.ViewPos
|
||||||
io.RenderDir = io.ViewDir
|
io.RenderDir = io.ViewDir
|
||||||
|
|
||||||
@ -337,6 +339,8 @@ func (r *WorldRenderer) Render(world *world.World, view *View) {
|
|||||||
gl.Enable(gl.DEPTH_TEST)
|
gl.Enable(gl.DEPTH_TEST)
|
||||||
gl.DepthFunc(gl.LESS)
|
gl.DepthFunc(gl.LESS)
|
||||||
|
|
||||||
|
lastclock.Restart()
|
||||||
|
|
||||||
// 1. Render to depth map
|
// 1. Render to depth map
|
||||||
gl.Viewport(0, 0, int32(ShadowmapSize[0]), int32(ShadowmapSize[1]))
|
gl.Viewport(0, 0, int32(ShadowmapSize[0]), int32(ShadowmapSize[1]))
|
||||||
gl.BindFramebuffer(gl.FRAMEBUFFER, r.depthmap.fbo)
|
gl.BindFramebuffer(gl.FRAMEBUFFER, r.depthmap.fbo)
|
||||||
@ -356,6 +360,8 @@ func (r *WorldRenderer) Render(world *world.World, view *View) {
|
|||||||
world.Render()
|
world.Render()
|
||||||
world.RenderWater()
|
world.RenderWater()
|
||||||
|
|
||||||
|
io.Diagnostics.Times.RenderPasses.Depthmap = lastclock.Restart()
|
||||||
|
|
||||||
// 2. Geometry pass, render to G-buffer
|
// 2. Geometry pass, render to G-buffer
|
||||||
io.RenderPos = io.ViewPos
|
io.RenderPos = io.ViewPos
|
||||||
io.RenderDir = io.ViewDir
|
io.RenderDir = io.ViewDir
|
||||||
@ -376,6 +382,8 @@ func (r *WorldRenderer) Render(world *world.World, view *View) {
|
|||||||
|
|
||||||
world.Render()
|
world.Render()
|
||||||
|
|
||||||
|
io.Diagnostics.Times.RenderPasses.Geometry = lastclock.Restart()
|
||||||
|
|
||||||
// 3/1. SSAO pass
|
// 3/1. SSAO pass
|
||||||
gl.BindFramebuffer(gl.FRAMEBUFFER, r.ssao.fbo)
|
gl.BindFramebuffer(gl.FRAMEBUFFER, r.ssao.fbo)
|
||||||
gl.ClearColor(1, 1, 1, 1)
|
gl.ClearColor(1, 1, 1, 1)
|
||||||
@ -386,7 +394,6 @@ func (r *WorldRenderer) Render(world *world.World, view *View) {
|
|||||||
r.ssao.shader.SetUniformMat4("view", view.View())
|
r.ssao.shader.SetUniformMat4("view", view.View())
|
||||||
r.ssao.shader.SetUniformMat4("projection", view.Perspective())
|
r.ssao.shader.SetUniformMat4("projection", view.Perspective())
|
||||||
r.ssao.shader.SetUniformVec3f("viewPos", view.EyePos)
|
r.ssao.shader.SetUniformVec3f("viewPos", view.EyePos)
|
||||||
r.ssao.shader.SetUniformFloat("time", float32(time.Since(r.startTime).Seconds()))
|
|
||||||
|
|
||||||
DrawScreenQuad()
|
DrawScreenQuad()
|
||||||
|
|
||||||
@ -398,6 +405,8 @@ func (r *WorldRenderer) Render(world *world.World, view *View) {
|
|||||||
|
|
||||||
DrawScreenQuad()
|
DrawScreenQuad()
|
||||||
|
|
||||||
|
io.Diagnostics.Times.RenderPasses.SSAO = lastclock.Restart()
|
||||||
|
|
||||||
// 4. Render the actual output with deferred lighting
|
// 4. Render the actual output with deferred lighting
|
||||||
gl.BindFramebuffer(gl.FRAMEBUFFER, r.output.fbo)
|
gl.BindFramebuffer(gl.FRAMEBUFFER, r.output.fbo)
|
||||||
gl.ClearColor(0, 0, 0, 0)
|
gl.ClearColor(0, 0, 0, 0)
|
||||||
@ -412,6 +421,8 @@ func (r *WorldRenderer) Render(world *world.World, view *View) {
|
|||||||
|
|
||||||
DrawScreenQuad()
|
DrawScreenQuad()
|
||||||
|
|
||||||
|
io.Diagnostics.Times.RenderPasses.Lighting = lastclock.Restart()
|
||||||
|
|
||||||
// 5. Render water
|
// 5. Render water
|
||||||
gl.Enable(gl.DEPTH_TEST)
|
gl.Enable(gl.DEPTH_TEST)
|
||||||
gl.DepthFunc(gl.LESS)
|
gl.DepthFunc(gl.LESS)
|
||||||
@ -445,46 +456,11 @@ func (r *WorldRenderer) Render(world *world.World, view *View) {
|
|||||||
|
|
||||||
DrawScreenQuad()
|
DrawScreenQuad()
|
||||||
|
|
||||||
// Show G-buffers?
|
io.Diagnostics.Times.RenderPasses.Postfx = lastclock.Restart()
|
||||||
|
io.Diagnostics.Times.Render = allclock.Elapsed()
|
||||||
|
|
||||||
|
// Show Information?
|
||||||
if io.ShowDebugInfo {
|
if io.ShowDebugInfo {
|
||||||
imgui.SetNextWindowPosV(imgui.Vec2{X: float32(r.lastDisplaySize[0]), Y: 0}, imgui.ConditionAlways, imgui.Vec2{X: 1, Y: 0})
|
r.renderDebugInfo()
|
||||||
if igwrap.Begin("Renderer Textures/Outputs", nil, igwrap.WindowFlagsOverlay) {
|
|
||||||
imgui.PushStyleVarVec2(imgui.StyleVarItemSpacing, imgui.Vec2{})
|
|
||||||
|
|
||||||
imageSize := r.lastDisplaySize.ToFloat32().Multiply(0.25)
|
|
||||||
imageSize[1] -= imgui.CurrentStyle().WindowPadding().Y / 2
|
|
||||||
imageSize[0] = imageSize[1] / float32(r.lastDisplaySize[1]) * float32(r.lastDisplaySize[0])
|
|
||||||
|
|
||||||
igwrap.Image(r.gbuffer.pos, imageSize, itype.Rectf{0, 0, 1, 1})
|
|
||||||
igwrap.Image(r.gbuffer.norm, imageSize, itype.Rectf{0, 0, 1, 1})
|
|
||||||
igwrap.Image(r.gbuffer.color, imageSize, itype.Rectf{0, 0, 1, 1})
|
|
||||||
igwrap.Image(r.ssao.ambient, imageSize, itype.Rectf{0, 0, 1, 1})
|
|
||||||
|
|
||||||
imgui.PopStyleVar()
|
|
||||||
imgui.End()
|
|
||||||
}
|
|
||||||
|
|
||||||
if igwrap.Begin("F3", nil, 0) {
|
|
||||||
imgui.PushStyleColor(imgui.StyleColorButton, imgui.Vec4{0, 0, 0, 0.5})
|
|
||||||
imgui.PushStyleColor(imgui.StyleColorButtonHovered, imgui.Vec4{0, 0, 0, 0.6})
|
|
||||||
imgui.PushStyleColor(imgui.StyleColorButtonActive, imgui.Vec4{0, 0, 0, 0.8})
|
|
||||||
igwrap.TextBlank()
|
|
||||||
isize := asset.WorldTextureAtlas.ImageSize
|
|
||||||
igwrap.TextBackground("Texture Atlas Size: (%dx%d)", isize[0], isize[1])
|
|
||||||
imgui.SameLine()
|
|
||||||
igwrap.TextBackground("[Hover]")
|
|
||||||
if imgui.IsItemHoveredV(imgui.HoveredFlagsAllowWhenDisabled) {
|
|
||||||
_, wheely := imgui.CurrentIO().MouseWheel()
|
|
||||||
if math.Abs(float64(wheely)) > 1e-3 {
|
|
||||||
atlasScale = util.Maxf(1, atlasScale+wheely)
|
|
||||||
}
|
|
||||||
imgui.BeginTooltip()
|
|
||||||
igwrap.Image(r.texture.Handle(), isize.ToFloat32().Multiply(atlasScale), itype.Rectf{0, 0, 1, 1})
|
|
||||||
imgui.EndTooltip()
|
|
||||||
}
|
|
||||||
|
|
||||||
imgui.PopStyleColorV(3)
|
|
||||||
imgui.End()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
56
internal/render/render_world_debuginfo.go
Normal file
56
internal/render/render_world_debuginfo.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package render
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"edgaru089.ml/go/gl01/internal/asset"
|
||||||
|
"edgaru089.ml/go/gl01/internal/igwrap"
|
||||||
|
"edgaru089.ml/go/gl01/internal/io"
|
||||||
|
"edgaru089.ml/go/gl01/internal/util"
|
||||||
|
"edgaru089.ml/go/gl01/internal/util/itype"
|
||||||
|
"github.com/inkyblackness/imgui-go/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *WorldRenderer) renderDebugInfo() {
|
||||||
|
// Render information
|
||||||
|
if igwrap.Begin("F3", nil, 0) {
|
||||||
|
igwrap.TextBlank()
|
||||||
|
|
||||||
|
igwrap.TextBackground("WorldRender: lastframe %.3fms", float64(io.Diagnostics.Times.Render.Nanoseconds())/float64(time.Millisecond))
|
||||||
|
|
||||||
|
isize := asset.WorldTextureAtlas.ImageSize
|
||||||
|
igwrap.TextBackground("Texture Atlas Size: (%dx%d)", isize[0], isize[1])
|
||||||
|
imgui.SameLine()
|
||||||
|
igwrap.TextBackground("[Hover]")
|
||||||
|
if imgui.IsItemHoveredV(imgui.HoveredFlagsAllowWhenDisabled) {
|
||||||
|
_, wheely := imgui.CurrentIO().MouseWheel()
|
||||||
|
if math.Abs(float64(wheely)) > 1e-3 {
|
||||||
|
atlasScale = util.Maxf(1, atlasScale+wheely)
|
||||||
|
}
|
||||||
|
imgui.BeginTooltip()
|
||||||
|
igwrap.Image(r.texture.Handle(), isize.ToFloat32().Multiply(atlasScale), itype.Rectf{0, 0, 1, 1})
|
||||||
|
imgui.EndTooltip()
|
||||||
|
}
|
||||||
|
|
||||||
|
imgui.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
imgui.SetNextWindowPosV(imgui.Vec2{X: float32(r.lastDisplaySize[0]), Y: 0}, imgui.ConditionAlways, imgui.Vec2{X: 1, Y: 0})
|
||||||
|
if igwrap.Begin("Renderer Textures/Outputs", nil, igwrap.WindowFlagsOverlay) {
|
||||||
|
imgui.PushStyleVarVec2(imgui.StyleVarItemSpacing, imgui.Vec2{})
|
||||||
|
|
||||||
|
imageSize := r.lastDisplaySize.ToFloat32().Multiply(0.25)
|
||||||
|
imageSize[1] -= imgui.CurrentStyle().WindowPadding().Y / 2
|
||||||
|
imageSize[0] = imageSize[1] / float32(r.lastDisplaySize[1]) * float32(r.lastDisplaySize[0])
|
||||||
|
|
||||||
|
igwrap.Image(r.gbuffer.pos, imageSize, itype.Rectf{0, 0, 1, 1})
|
||||||
|
igwrap.Image(r.gbuffer.norm, imageSize, itype.Rectf{0, 0, 1, 1})
|
||||||
|
igwrap.Image(r.gbuffer.color, imageSize, itype.Rectf{0, 0, 1, 1})
|
||||||
|
igwrap.Image(r.ssao.ambient, imageSize, itype.Rectf{0, 0, 1, 1})
|
||||||
|
|
||||||
|
imgui.PopStyleVar()
|
||||||
|
imgui.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
internal/util/clock.go
Normal file
28
internal/util/clock.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Clock is a clock to measure elapsed time.
|
||||||
|
// It is a shorthand for the time.Since() and time.Now() combo.
|
||||||
|
type Clock struct {
|
||||||
|
t time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClock returns a new clock.
|
||||||
|
// It also starts it.
|
||||||
|
func NewClock() (c *Clock) {
|
||||||
|
return &Clock{t: time.Now()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart resets the start time.
|
||||||
|
// It also returns the elapsed time.
|
||||||
|
func (c *Clock) Restart() (t time.Duration) {
|
||||||
|
t = time.Since(c.t)
|
||||||
|
c.t = time.Now()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elapsed returns the elapsed time.
|
||||||
|
func (c *Clock) Elapsed() time.Duration {
|
||||||
|
return time.Since(c.t)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user