当我的主循环等待 channel 滴答和传递事件时,OpenGl 似乎卡住了

标签 opengl go

我写了一个简单的 Go 程序。它的目标是使用 OpenGl 在屏幕上旋转一个三角形。

编辑:主循环似乎负责,OpenGl 很好,但我在 goroutines 和/或 channel 上做错了。请参阅本文底部。我正在适本地更改此问题的标题。

该程序几乎可以运行。它断断续续地在按预期旋转和只闪烁两个 OpenGl 缓冲区而不绘制任何东西之间交替。我尝试渲染的大约三分之二的帧无提示地失败了,我不明白为什么。

这不取决于我的帧率。无论我以 50 还是 1 FPS 更新,我都会得到一长串困惑的帧,然后是较短的工作帧系列。我不认为我要求 OpenGl 工作得太快(而且我还是调用了 glFinish())。

我开始试验并发现了一个奇怪的行为。 我使用 glGetUniformLocation 作为将我的新旋转矩阵发送到顶点缓冲区的第一步。 我注意到,如果我要求 glGetUniformLocation 告诉我在哪里可以找到 "dummy",一个显然在着色器中不存在的参数,它并不总是返回 -1正如预期的那样;它有时会返回 0。该虚拟属性与问题无关,它只是一种症状。

我在每次 OpenGl 调用后检查 glGetError,它们总是返回 NO_ERROR。

我在这里粘贴我的代码。我希望我能缩短它。我的真实代码在每次调用 OpenGl 后检查 glError,并查看 0 个缓冲区或 -1 个位置。这个版本更轻。 func main 位于顶部,后面是负责旋转的循环。

package main

import (
    "fmt"
    "github.com/0xe2-0x9a-0x9b/Go-SDL/sdl"
    gl "github.com/chsc/gogl/gl33"
    "math"
    "time"
    "unsafe"
)

const DEG_TO_RAD = math.Pi / 180

type GoMatrix [16]float64
type GlMatrix [16]gl.Float

var good_frames, bad_frames, sdl_events int

func main() {

    //=================================================================
    // Just opening a window, skip to the next part.

    if status := sdl.Init(sdl.INIT_VIDEO); status != 0 {
        panic("Could not initialize SDL: " + sdl.GetError())
    }
    defer sdl.Quit()

    sdl.GL_SetAttribute(sdl.GL_DOUBLEBUFFER, 1)
    const FLAGS = sdl.OPENGL
    if screen := sdl.SetVideoMode(640, 480, 32, FLAGS); screen == nil {
        panic("Could not open SDL window: " + sdl.GetError())
    }

    if err := gl.Init(); err != nil {
        panic(err)
    }

    gl.Viewport(0, 0, 640, 480)
    gl.ClearColor(.5, .5, .5, 1)

    //=================================================================
    // Simplest shaders ever.

    // A matrix to move the model, nothing else.
    vertex_code := gl.GLString(`
    #version 330 core
    in vec3 vpos;
    uniform mat4 MVP;
    void main() { 
        gl_Position = MVP * vec4(vpos, 1);
    }   
    `)
    // Everything is red.
    fragment_code := gl.GLString(`
    #version 330 core
    void main(){
        gl_FragColor = vec4(1,0,0,1);
    }
    `)
    vs := gl.CreateShader(gl.VERTEX_SHADER)
    fs := gl.CreateShader(gl.FRAGMENT_SHADER)
    gl.ShaderSource(vs, 1, &vertex_code, nil)
    gl.ShaderSource(fs, 1, &fragment_code, nil)
    gl.CompileShader(vs)
    gl.CompileShader(fs)
    prog := gl.CreateProgram()
    gl.AttachShader(prog, vs)
    gl.AttachShader(prog, fs)
    gl.LinkProgram(prog)
    // Did it compile?
    var link_status gl.Int
    gl.GetProgramiv(prog, gl.LINK_STATUS, &link_status)
    if link_status == gl.FALSE {
        var info_log_length gl.Int
        gl.GetProgramiv(prog, gl.INFO_LOG_LENGTH, &info_log_length)
        if info_log_length == 0 {
            panic("Program linking failed but OpenGL has no log about it.")
        } else {
            info_log_gl := gl.GLStringAlloc(gl.Sizei(info_log_length))
            defer gl.GLStringFree(info_log_gl)
            gl.GetProgramInfoLog(prog, gl.Sizei(info_log_length), nil, info_log_gl)
            info_log := gl.GoString(info_log_gl)
            panic(info_log)
        }
    }
    gl.UseProgram(prog)
    attrib_vpos := gl.Uint(gl.GetAttribLocation(prog, gl.GLString("vpos")))

    //=================================================================
    // One triangle.

    positions := [...]gl.Float{-.5, -.5, 0, .5, -.5, 0, 0, .5, 0}

    var vao gl.Uint
    gl.GenVertexArrays(1, &vao)
    gl.BindVertexArray(vao)

    var vbo gl.Uint
    gl.GenBuffers(1, &vbo)
    gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
    gl.BufferData(gl.ARRAY_BUFFER,
        gl.Sizeiptr(unsafe.Sizeof(positions)),
        gl.Pointer(&positions[0]),
        gl.STATIC_DRAW)

    gl.EnableVertexAttribArray(attrib_vpos)
    gl.VertexAttribPointer(attrib_vpos, 3, gl.FLOAT, gl.FALSE, 0, gl.Pointer(nil))

    //=================================================================

    Loop(prog)
    fmt.Println("Good frames", good_frames)
    fmt.Println("Bad frames ", bad_frames)
    fmt.Println("SDL events ", sdl_events)
}

func Loop(program gl.Uint) {
    start_time := time.Now()
    ticker := time.NewTicker(100 * time.Millisecond)
    defer ticker.Stop()
    running := true
    for running {
        select {
        case tick_time := <-ticker.C:
            OnTick(start_time, tick_time, program)
        case event := <-sdl.Events:
            running = OnSdlEvent(event)
        }
    }
}

func OnSdlEvent(event interface{}) bool {
    sdl_events++
    switch event.(type) {
    case sdl.QuitEvent:
        return false // Stop the main loop.
    }
    return true // Do not stop the main loop.
}

func OnTick(start_time, tick_time time.Time, program gl.Uint) {
    duration := tick_time.Sub(start_time).Seconds()
    speed := 10.
    angle := math.Mod(duration*speed, 360)
    gom := RotZ(angle)
    MVP := ToGlMatrix(gom)

    /* HERE, SOMETHING FISHY HAPPENS.

    Problem: sometimes, actually often, OpenGl returns 0 instead of -1 for
    the dummy parameter.  This is entirely correlated to the stuttering.

    With my implementation of OpenGl, swap buffer does a real swap.
    That means I get to see the last two pictures rendered.
    Thing is, I can see the swap, that means the pictures are different.
    That means that the call to DrawArrays is ignored.

    OpenGl is just crapping its pants.
    */
    matrix_loc := gl.GetUniformLocation(program, gl.GLString("MVP"))
    dummy_matrix_loc := gl.GetUniformLocation(program, gl.GLString("dummy"))
    if gl.GetError() != gl.NO_ERROR {
        fmt.Println("Error get location") // Never happens.
    }
    if dummy_matrix_loc == -1 {
        good_frames++ // Because is SHOULD fail.
    } else {
        bad_frames++ // That's not normal.
    }
    gl.UniformMatrix4fv(matrix_loc, 16, gl.TRUE, &MVP[0])
    if gl.GetError() != gl.NO_ERROR {
        fmt.Println("Error send matrix") // Never happens.
    }
    gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
    if gl.GetError() != gl.NO_ERROR {
        fmt.Println("Error clearing") // Never happens.
    }
    gl.DrawArrays(gl.TRIANGLES, 0, 3)
    if gl.GetError() != gl.NO_ERROR {
        fmt.Println("Error drawing") // Never happens.
    }
    gl.Finish() // Does not seem to make anything work better.
    sdl.GL_SwapBuffers()
}

func RotZ(angle float64) GoMatrix {
    var gom GoMatrix
    a := angle * DEG_TO_RAD
    c := math.Cos(a)
    s := math.Sin(a)
    gom[0] = c
    gom[1] = s
    gom[4] = -s
    gom[5] = c
    gom[10] = 1
    gom[15] = 1
    return gom
}

func ToGlMatrix(gom GoMatrix) GlMatrix {
    var glm GlMatrix
    glm[0] = gl.Float(gom[0])
    glm[1] = gl.Float(gom[1])
    glm[2] = gl.Float(gom[2])
    glm[3] = gl.Float(gom[3])
    glm[4] = gl.Float(gom[4])
    glm[5] = gl.Float(gom[5])
    glm[6] = gl.Float(gom[6])
    glm[7] = gl.Float(gom[7])
    glm[8] = gl.Float(gom[8])
    glm[9] = gl.Float(gom[9])
    glm[10] = gl.Float(gom[10])
    glm[11] = gl.Float(gom[11])
    glm[12] = gl.Float(gom[12])
    glm[13] = gl.Float(gom[13])
    glm[14] = gl.Float(gom[14])
    glm[15] = gl.Float(gom[15])
    return glm
}

根据要求,glxinfo 的输出。

> glxinfo
name of display: :0
display: :0  screen: 0
direct rendering: Yes
server glx vendor string: ATI
server glx version string: 1.4
server glx extensions:
    GLX_ARB_multisample, GLX_EXT_import_context, GLX_EXT_texture_from_pixmap, 
    GLX_EXT_visual_info, GLX_EXT_visual_rating, GLX_OML_swap_method, 
    GLX_SGI_make_current_read, GLX_SGI_swap_control, GLX_SGIS_multisample, 
    GLX_SGIX_fbconfig, GLX_SGIX_pbuffer, GLX_SGIX_visual_select_group
client glx vendor string: ATI
client glx version string: 1.4
client glx extensions:
    GLX_ARB_create_context, GLX_ARB_create_context_profile, 
    GLX_ARB_get_proc_address, GLX_ARB_multisample, GLX_EXT_import_context, 
    GLX_EXT_visual_info, GLX_EXT_visual_rating, GLX_MESA_allocate_memory, 
    GLX_MESA_copy_sub_buffer, GLX_MESA_swap_control, 
    GLX_MESA_swap_frame_usage, GLX_NV_swap_group, GLX_OML_swap_method, 
    GLX_SGI_make_current_read, GLX_SGI_swap_control, GLX_SGI_video_sync, 
    GLX_SGIS_multisample, GLX_SGIX_fbconfig, GLX_SGIX_pbuffer, 
    GLX_SGIX_swap_barrier, GLX_SGIX_swap_group, GLX_SGIX_visual_select_group, 
    GLX_EXT_texture_from_pixmap, GLX_EXT_framebuffer_sRGB, 
    GLX_ARB_fbconfig_float, GLX_AMD_gpu_association
GLX version: 1.4
GLX extensions:
    GLX_ARB_create_context, GLX_ARB_create_context_profile, 
    GLX_ARB_get_proc_address, GLX_ARB_multisample, GLX_EXT_import_context, 
    GLX_EXT_visual_info, GLX_EXT_visual_rating, GLX_MESA_swap_control, 
    GLX_NV_swap_group, GLX_OML_swap_method, GLX_SGI_make_current_read, 
    GLX_SGI_swap_control, GLX_SGI_video_sync, GLX_SGIS_multisample, 
    GLX_SGIX_fbconfig, GLX_SGIX_pbuffer, GLX_SGIX_swap_barrier, 
    GLX_SGIX_swap_group, GLX_SGIX_visual_select_group, 
    GLX_EXT_texture_from_pixmap
OpenGL vendor string: ATI Technologies Inc.
OpenGL renderer string: ATI Mobility Radeon HD 4500 Series
OpenGL version string: 3.3.11005 Compatibility Profile Context
OpenGL shading language version string: 3.30
OpenGL extensions:
    GL_AMDX_debug_output, GL_AMDX_vertex_shader_tessellator, 
    GL_AMD_conservative_depth, GL_AMD_debug_output, 
    GL_AMD_depth_clamp_separate, GL_AMD_draw_buffers_blend, 
    GL_AMD_name_gen_delete, GL_AMD_performance_monitor, GL_AMD_pinned_memory, 
    GL_AMD_sample_positions, GL_AMD_seamless_cubemap_per_texture, 
    GL_AMD_shader_stencil_export, GL_AMD_texture_cube_map_array, 
    GL_AMD_texture_texture4, GL_AMD_vertex_shader_tessellator, 
    GL_ARB_ES2_compatibility, GL_ARB_blend_func_extended, 
    GL_ARB_color_buffer_float, GL_ARB_copy_buffer, GL_ARB_depth_buffer_float, 
    GL_ARB_depth_clamp, GL_ARB_depth_texture, GL_ARB_draw_buffers, 
    GL_ARB_draw_buffers_blend, GL_ARB_draw_elements_base_vertex, 
    GL_ARB_draw_instanced, GL_ARB_explicit_attrib_location, 
    GL_ARB_fragment_coord_conventions, GL_ARB_fragment_program, 
    GL_ARB_fragment_program_shadow, GL_ARB_fragment_shader, 
    GL_ARB_framebuffer_object, GL_ARB_framebuffer_sRGB, 
    GL_ARB_geometry_shader4, GL_ARB_get_program_binary, 
    GL_ARB_half_float_pixel, GL_ARB_half_float_vertex, GL_ARB_imaging, 
    GL_ARB_instanced_arrays, GL_ARB_map_buffer_range, GL_ARB_multisample, 
    GL_ARB_multitexture, GL_ARB_occlusion_query, GL_ARB_occlusion_query2, 
    GL_ARB_pixel_buffer_object, GL_ARB_point_parameters, GL_ARB_point_sprite, 
    GL_ARB_provoking_vertex, GL_ARB_sample_shading, GL_ARB_sampler_objects, 
    GL_ARB_seamless_cube_map, GL_ARB_separate_shader_objects, 
    GL_ARB_shader_bit_encoding, GL_ARB_shader_objects, 
    GL_ARB_shader_precision, GL_ARB_shader_stencil_export, 
    GL_ARB_shader_texture_lod, GL_ARB_shading_language_100, GL_ARB_shadow, 
    GL_ARB_shadow_ambient, GL_ARB_sync, GL_ARB_texture_border_clamp, 
    GL_ARB_texture_buffer_object, GL_ARB_texture_buffer_object_rgb32, 
    GL_ARB_texture_compression, GL_ARB_texture_compression_rgtc, 
    GL_ARB_texture_cube_map, GL_ARB_texture_cube_map_array, 
    GL_ARB_texture_env_add, GL_ARB_texture_env_combine, 
    GL_ARB_texture_env_crossbar, GL_ARB_texture_env_dot3, 
    GL_ARB_texture_float, GL_ARB_texture_gather, 
    GL_ARB_texture_mirrored_repeat, GL_ARB_texture_multisample, 
    GL_ARB_texture_non_power_of_two, GL_ARB_texture_query_lod, 
    GL_ARB_texture_rectangle, GL_ARB_texture_rg, GL_ARB_texture_rgb10_a2ui, 
    GL_ARB_texture_snorm, GL_ARB_timer_query, GL_ARB_transform_feedback2, 
    GL_ARB_transform_feedback3, GL_ARB_transpose_matrix, 
    GL_ARB_uniform_buffer_object, GL_ARB_vertex_array_bgra, 
    GL_ARB_vertex_array_object, GL_ARB_vertex_buffer_object, 
    GL_ARB_vertex_program, GL_ARB_vertex_shader, 
    GL_ARB_vertex_type_2_10_10_10_rev, GL_ARB_viewport_array, 
    GL_ARB_window_pos, GL_ATI_draw_buffers, GL_ATI_envmap_bumpmap, 
    GL_ATI_fragment_shader, GL_ATI_meminfo, GL_ATI_separate_stencil, 
    GL_ATI_texture_compression_3dc, GL_ATI_texture_env_combine3, 
    GL_ATI_texture_float, GL_ATI_texture_mirror_once, GL_EXT_abgr, 
    GL_EXT_bgra, GL_EXT_bindable_uniform, GL_EXT_blend_color, 
    GL_EXT_blend_equation_separate, GL_EXT_blend_func_separate, 
    GL_EXT_blend_minmax, GL_EXT_blend_subtract, GL_EXT_compiled_vertex_array, 
    GL_EXT_copy_buffer, GL_EXT_copy_texture, GL_EXT_direct_state_access, 
    GL_EXT_draw_buffers2, GL_EXT_draw_instanced, GL_EXT_draw_range_elements, 
    GL_EXT_fog_coord, GL_EXT_framebuffer_blit, GL_EXT_framebuffer_multisample, 
    GL_EXT_framebuffer_object, GL_EXT_framebuffer_sRGB, 
    GL_EXT_geometry_shader4, GL_EXT_gpu_program_parameters, 
    GL_EXT_gpu_shader4, GL_EXT_histogram, GL_EXT_multi_draw_arrays, 
    GL_EXT_packed_depth_stencil, GL_EXT_packed_float, GL_EXT_packed_pixels, 
    GL_EXT_pixel_buffer_object, GL_EXT_point_parameters, 
    GL_EXT_provoking_vertex, GL_EXT_rescale_normal, GL_EXT_secondary_color, 
    GL_EXT_separate_specular_color, GL_EXT_shadow_funcs, GL_EXT_stencil_wrap, 
    GL_EXT_subtexture, GL_EXT_texgen_reflection, GL_EXT_texture3D, 
    GL_EXT_texture_array, GL_EXT_texture_buffer_object, 
    GL_EXT_texture_compression_latc, GL_EXT_texture_compression_rgtc, 
    GL_EXT_texture_compression_s3tc, GL_EXT_texture_cube_map, 
    GL_EXT_texture_edge_clamp, GL_EXT_texture_env_add, 
    GL_EXT_texture_env_combine, GL_EXT_texture_env_dot3, 
    GL_EXT_texture_filter_anisotropic, GL_EXT_texture_integer, 
    GL_EXT_texture_lod, GL_EXT_texture_lod_bias, GL_EXT_texture_mirror_clamp, 
    GL_EXT_texture_object, GL_EXT_texture_rectangle, GL_EXT_texture_sRGB, 
    GL_EXT_texture_shared_exponent, GL_EXT_texture_snorm, 
    GL_EXT_texture_swizzle, GL_EXT_timer_query, GL_EXT_transform_feedback, 
    GL_EXT_vertex_array, GL_EXT_vertex_array_bgra, 
    GL_IBM_texture_mirrored_repeat, GL_KTX_buffer_region, GL_NV_blend_square, 
    GL_NV_conditional_render, GL_NV_copy_depth_to_color, 
    GL_NV_explicit_multisample, GL_NV_float_buffer, GL_NV_half_float, 
    GL_NV_primitive_restart, GL_NV_texgen_reflection, GL_NV_texture_barrier, 
    GL_SGIS_generate_mipmap, GL_SGIS_texture_edge_clamp, GL_SGIS_texture_lod, 
    GL_SUN_multi_draw_arrays, GL_WIN_swap_hint, WGL_EXT_swap_control

编辑:主循环似乎是负责任的,OpenGl 很好,但我在 goroutines 和/或 channel 上做错了。

如果我用不依赖 channel 和 goroutines 的东西替换我的主循环,那么 OpenGl 表现完美。

func Loop(program gl.Uint) {
    start_time := time.Now()
    stop_time := start_time.Add(time.Duration(30 * time.Second))
    running := true
    for running {
        tick_time := time.Now()
        OnTick(start_time, tick_time, program)
        time.Sleep(10 * time.Millisecond)
        if tick_time.After(stop_time) {
            running = false
        }
    }
}

我的问题可能是因为 OpenGl 在主 Goroutine 中,它在等待 channel 传递 tick 或 SDL 事件时阻塞。我不知道,我很困惑。

将标题从 glGetUniformLocation 对于不存在的属性通常返回 0 而不是 -1 更改为 OpenGl 似乎卡住了,而我的主循环等待 channel 滴答和传递事件。

最佳答案

问题出在图形库 SDL 和 OpenGl 对线程的使用施加了限制。 Golang 在它认为合适的情况下在线程之间移动 goroutines,不知道这些限制。因此,一些调用会丢失,出现不可预知的行为。

通过将 OpenGl 锁定到主线程,我可以让它工作,而不必放弃使用 goroutines。

这是我采用的解决方案: http://code.google.com/p/go-wiki/wiki/LockOSThread 请注意函数 do 中有一个拼写错误:应该读作 mainfunc 而不是 ch

我的主文件现在看起来像这样:

// Arrange that main.main runs on main thread.
func init() {
    runtime.LockOSThread()
}

// Queue of work to run in main thread.
var mainfunc = make(chan func())

// Run all the functions that need to run in the main thread.
func Main() {
    for f = range mainfunc {
        f()
    }
}

// Put the function f on the main thread function queue.
func do(f func()) {
    done := make(chan bool, 1)
    mainfunc <- func() {
        f()
        done <- true
    }
    <-done

// The real main function.
func main() {
    go Everything()
    Main()
}

// Your entire application comes here.
func Everything() {
    defer close(mainfunc)
    do(func(){
        // init SDL, OpenGl, all that.
    })
    defer sdl.Quit()

    var some_variable int // Starts at 0.
    do(func(){
        // OpenGl code
        some_variable = 42 // Closure allows for communication.
    })
    Loop(some_variable) // Now 42.
}

关于当我的主循环等待 channel 滴答和传递事件时,OpenGl 似乎卡住了,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14732354/

相关文章:

opengl - 将模板渲染缓冲区绑定(bind)到opengl中的帧缓冲区

c++ - 使用影响 TEXTURE_2D 的 TEXTURE_RECTANGLE_ARB

c++ - OpenGL圆形绘图得到一个椭圆

c++ - 从其他线程使用qglcontext

c++ - 为什么 glReadPixels 这么慢并且有其他选择吗?

当它们应该是值时返回为 0 的 JSON 整数

go - 如何通过结构字段名称获取 Gmail API 错误代码?

struct - 高语 : Variable length array in struct for use with binary read

go - 测试内存消耗

go - 如何处理 stderr?