opengl – Golang fmt.Println()导致游戏崩溃
我在Go中有这个简单的OpenGL程序.
当我编译并运行它时,主游戏循环在崩溃之前会经历大约9次迭代. rendering for the 0 time rendering for the 1 time rendering for the 2 time rendering for the 3 time rendering for the 4 time rendering for the 5 time rendering for the 6 time SIGSEGV: segmentation violation PC=0x7fdab95a0e29 signal arrived during cgo execution runtime.cgocall(0x414f90,0x7fdab9887e88) /usr/lib/go/src/pkg/runtime/cgocall.c:149 +0x11b fp=0x7fdab9887e70 github.com/go-gl/gl._Cfunc_glClear(0xc200004100) github.com/go-gl/gl/_obj/_cgo_defun.c:340 +0x31 fp=0x7fdab9887e88 github.com/go-gl/gl.Clear(0x4100) /mnt/data/Dropbox/Coding/Go/src/github.com/go-gl/gl/gl.go:161 +0x25 fp=0x7fdab9887e98 main.draw() /home/josh/Coding/Go/src/github.com/JoshWillik/Wander/wander.go:120 +0x25 fp=0x7fdab9887eb8 main.main() /home/josh/Coding/Go/src/github.com/JoshWillik/Wander/wander.go:52 +0x300 fp=0x7fdab9887f48 runtime.main() /usr/lib/go/src/pkg/runtime/proc.c:220 +0x11f fp=0x7fdab9887fa0 runtime.goexit() /usr/lib/go/src/pkg/runtime/proc.c:1394 fp=0x7fdab9887fa8 goroutine 3 [syscall]: runtime.goexit() /usr/lib/go/src/pkg/runtime/proc.c:1394 rax 0x0 rbx 0x7fdab9887e88 rcx 0x7fdab9887e88 rdx 0x7fdab9887e20 rdi 0x4100 rsi 0xc210001900 rbp 0xc21002a000 rsp 0x7fdab2a4ddd8 r8 0xc210001120 r9 0x7fdab9887e20 r10 0x0 r11 0x286 r12 0x0 r13 0x7fdab9a74000 r14 0x0 r15 0x7fdab2a4e700 rip 0x7fdab95a0e29 rflags 0x10202 cs 0x33 fs 0x0 gs 0x0 如果我删除了shouldRender函数中基于时间的逻辑,那么它会在崩溃之前进行大约28-29次迭代. 如果我在draw函数中删除对gl.Clear()的调用,它会在崩溃前持续到90s. 如果我在shouldRender中删除对fmt.Println()的调用,游戏将按预期运行而不会崩溃. (我已经测试了大约2或3分钟,所以差不多有1万帧) 这让我怀疑对fmt.Println()的调用在某种程度上导致了分段违规.我误读了这些迹象吗?如果没有,像Println()这样的核心功能如何不稳定? package main import ( f "fmt" "github.com/go-gl/gl" glfw "github.com/go-gl/glfw3" "math" "time" ) var ( numRendered = 0 lastDraw = time.Now() fps = 60 seconds = time.Now() attr gl.AttribLocation ) func main(){ if !glfw.Init(){ f.Println("Failed to init glfw") panic("Cannot initialize glfw library") } defer glfw.Terminate() //glfw.WindowHint(glfw.DepthBits,16) window,err := glfw.CreateWindow(300,300,"Wander",nil,nil) if err != nil{ panic(err) } window.SetFramebufferSizeCallback(reshape) window.SetKeyCallback(key) window.MakeContextCurrent() glfw.SwapInterval(1) width,height := window.GetFramebufferSize() reshape(window,width,height) if gl.Init() != 0 { panic("Failed to init GL") } prog := setupProgram() defer prog.Delete() prog.Use() attr = prog.GetAttribLocation("offset") setup() for !window.ShouldClose() { if shouldRender(){ draw() } animate() window.SwapBuffers() glfw.PollEvents() } } func setupProgram()(prog gl.Program){ vertexSource := ` #version 430 core layout (location = 0) in vec4 offset; const vec4 vertecies[3] = vec4[3]( vec4(0.25,0.5,1.0),vec4(-0.25,-0.5,1.0) ); void main(){ gl_Position = vertecies[gl_VertexID] + offset; }` fragmentSource := ` #version 430 core out vec4 color; void main(){ color = vec4(1.0,0.0,0.0); // red,blue,green,?? }` vert,frag := gl.CreateShader(gl.VERTEX_SHADER),gl.CreateShader(gl.FRAGMENT_SHADER) defer vert.Delete() defer frag.Delete() vert.Source(vertexSource) frag.Source(fragmentSource) vert.Compile() frag.Compile() prog = gl.CreateProgram() prog.AttachShader(vert) prog.AttachShader(frag) prog.Link() prog.Use() f.Println(prog.GetInfoLog()) return } func key(window *glfw.Window,k glfw.Key,s int,action glfw.Action,mods glfw.ModifierKey) { if action != glfw.Press { return } switch glfw.Key(k){ case glfw.KeyEscape: window.SetShouldClose(true); default: return } } func reshape(window *glfw.Window,height int){ gl.Viewport(0,height) } func draw(){ gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) gl.DrawArrays(gl.TRIANGLES,3) } func shouldRender() bool{ if int(time.Since(lastDraw) * time.Second) >= 1000/fps{ //f.Println("rendering for the ",numRendered," time") numRendered++ lastDraw = time.Now() return true } return false; } func animate(){ now := float64(time.Since(seconds)) offset := [4]float32{ float32(math.Sin(now)),float32(math.Cos(now)),0.0} attr.Attrib4fv(&offset) red := gl.GLclampf(math.Sin(now) * 0.25 + 0.75) blue := gl.GLclampf(math.Cos(now) * 0.25 + 0.75) green := gl.GLclampf(time.Since(seconds)) _ = green; gl.ClearColor(red,0.2,0.0) }
我在我的机器上运行你的代码 – 在64位Windows上运行MinGW-w64.虽然我的println代码比你的代码运行时间长(崩溃前超过30k的调用)但我观察到了同样的行为.
堆栈跟踪在调用gl函数时报告它,这给了我一个暗示:可能错误与OpenGL上下文有关. 的确,如果加上 import ( //... "runtime" //... ) 和线 runtime.LockOSThread() 在主函数的顶部,错误消失(或者至少在线程锁定它在我的机器上运行了几分钟,显然我无法证明它永远不会崩溃). 当Goroutines被阻止执行某些任务(如IO)时,Go运行时会偶尔拆分额外的线程以保持程序移动. 我怀疑它发生的事情是,有时在调用Println时,Goroutine在系统调用中被阻止,因此运行时通过在不同的线程上运行主goroutine来“帮助”你.由于OpenGL上下文绑定到一个线程,这导致您的程序在GL调用上崩溃,因为您正在调用错误的线程. 将runtime.LockOSThread()添加到main的顶部会强制主Goroutine始终在同一个线程上执行,从而将所有GL调用保持在正确的上下文中. 我应该补充一点,我简要地浏览了fmt软件包的源代码,从我看到的我无法证明goroutine / thread废话肯定会发生;但是,我强烈怀疑这是基于我对Go运行时的了解以及LockOSThread似乎修复它的事实. 无论哪种方式:当您使用OpenGL或OpenAL等C库时,依赖于将上下文绑定到单个线程,请确保始终使用runtime.LockOSThread将其运行的Goroutine锁定到一个线程. 更新:我在the Go scheduler找到了这个文件,如果我正确地阅读它,证实了我的怀疑.诸如打印之类的系统调用可以调用生成的新线程,以允许在IO上阻止程序时继续调用goroutine. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |