我写了一个简单的 Go 程序。它的目标是使用 OpenGL 在屏幕上旋转一个三角形。
编辑:主循环似乎是负责任的,OpenGl 很好,但我在 goroutine 和/或通道上做错了。请参阅这篇文章的底部。我正在适当地更改这个问题的标题。
该程序几乎可以工作。它口吃,在按预期旋转之间交替,只是在不绘制任何东西的情况下闪烁两个 OpenGL 缓冲区。我尝试渲染的大约三分之二的帧都以静默方式失败,我不明白为什么。
这不取决于我的帧速率。无论我以 50 FPS 还是 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 很好,但我在 goroutine 和/或通道上做错了。
如果我用不依赖通道和 goroutine 的东西替换我的主循环,那么 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 中,它在等待通道传递滴答声或 SDL 事件时阻塞。我不知道,我很困惑。
对于OpenGL不存在的属性,从 glGetUniformLocation 更改标题通常返回 0 而不是 -1,而我的主循环等待通道滴答并传递事件时似乎卡住了。