Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 9259767
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 18, 20262026-06-18T12:44:10+00:00 2026-06-18T12:44:10+00:00

I wrote a simple Go program. Its goal is to rotate a triangle on

  • 0

I wrote a simple Go program. Its goal is to rotate a triangle on the screen using OpenGl.

EDIT: The main loop seems to be responsible, OpenGl is fine, but I am doing something wrong with goroutines and/or channels. See at the bottom of this post. I am changing the title of this question appropriately.

The program almost works. It stutters, alternating between rotating as it is supposed to, and just flashing the two OpenGl buffers without drawing anything. About two thirds of the frames I try to render fail silently, and I do not understand why.

This does not depend on my frame rate. Whether I update at 50 or 1 FPS, I get long series of messed up frames followed by shorter series of working frames. I don’t think I am asking OpenGl to work too fast (and I call glFinish() anyway).

I started experimenting and found a strange behavior.
I use glGetUniformLocation as a first step to send my new rotation matrix to the vertex buffer.
I noticed that if I ask glGetUniformLocation to tell me where I can find "dummy", a parameter that obviously does not exist in the shader, it does not always return -1 as expected ; it sometimes returns 0. That dummy attribute is not responsible for the problem, it is merely a symptom.

I check glGetError after every OpenGl call, they always return NO_ERROR.

I am pasting here my code. I wish I could have made it shorter. My real code checks glError after every single call to OpenGl, and also looks at 0 buffers or -1 locations. This version is lighter. func main is at the top, followed my the loop that takes care of the rotation.

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
}

As requested, the output of 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

EDIT: The main loop seems to be responsible, OpenGl is fine, but I am doing something wrong with goroutines and/or channels.

If I replace my main loop with something that does not rely on channels and goroutines, then OpenGl behaves perfectly.

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
        }
    }
}

My problem may come from the fact that OpenGl is in the main Goroutine, which blocks while waiting for the channels to deliver a tick or a SDL event. I don’t know, I am confused.

Changing the title from glGetUniformLocation often returns 0 instead of -1 for an attribute that does not exist to OpenGl seems stuck while my main loop waits for channels to tick and deliver events.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-18T12:44:12+00:00Added an answer on June 18, 2026 at 12:44 pm

    The problem comes from the fact that the graphic libraries SDL and OpenGl impose restrictions to the use of threads. Golang shuffles goroutines around between threads as it sees fit, unaware of these restrictions. Consequently, some calls are lost, unpredictable behaviors occur.

    By locking OpenGl to the main thread, I could have it working, without having to give up using goroutines.

    This is the solution I adopted:
    http://code.google.com/p/go-wiki/wiki/LockOSThread
    Note that there is a typo in the function do: one should read mainfunc instead of ch.

    My main file now looks like this:

    // 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.
    }
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I wrote a simple C program: #include <unistd.h> #include <stdio.h> int main( int argc,
I am using the windows phone emulator. I wrote a very simple program: draw
I wrote a simple program that accesses a file called input.txt and pushes its
I wrote a simple program in C and compiled it using GCC on Ubuntu.
I wrote a simple program solving the Readers-Writers problem using semaphores. It runs perfectly
I wrote a simple test program for opencv to see if it's working after
I wrote a simple program to solve the math problem: A^2+B^2 = 12 A*B
I wrote a simple program to test a theory that finally block will always
I wrote a simple program in java web forms but i am receiving the
A while back I wrote a simple python program to brute-force the single solution

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.