OpenGL glMultiDrawElementsIndirect crash on integrated intel GPU

Hello there. Currently working on a piece of software that would benefit from the use of MDI. My program works fine on all Windows machines I’ve tested that use a Discrete GPU. However, the exact same program crashes with GL_INVALID_OPERATION on my glMultiDrawElementsIndirect draw call. After some debugging, it appears it is crashing due to the igxelpicd64.dll driver, specifically a call to DrvValidateVersion at call address 0x9. My Intel Integrated CPU is on driver version 32.0.101.6299. However, on Intel’s website, the debug symbols download page only goes up to 32.0.101.6129, so this might actually not be the correct function name. In any case, my code is below. It is contained to just this file. For those wondering, the programming language is called Jai. It has compatible OpenGL bindings.

window          : Window_Type;
should_close    : bool  = false;
shader_program  : u32   = 0;
VAO             : u32   = 0;
VBO             : u32   = 0;
EBO             : u32   = 0;
IBO             : u32   = 0;
vertex_buffer   : [..] Vertex;
index_buffer    : [..] u64;
indirect_buffer : [..] IndirectCommand;


IndirectCommand :: struct {
    count           : u32;
    instance_count  : u32;
    first_index     : u32;
    base_vertex     : s32;
    base_instance   : u32;
}

Vertex :: struct {
    position : Vector3;
    color    : Vector3;
}

SAMPLE_VERTS : [..] Vertex = xx Vertex.[
    .{.{-.5, -.5, 0}, .{1, 0, 0}},
    .{.{ .5, -.5, 0}, .{0, 1, 0}},
    .{.{ .5,  .5, 0}, .{0, 0, 1}},
    .{.{-.5,  .5, 0}, .{1, 1, 1}},
];

SAMPLE_INDXS : [..] u32 = xx u32.[
    0, 1, 2,
    2, 3, 0
];

main :: () {
    print("Hello world!\n");
    create_game_window();
    create_opengl_context();
    setup_render();
    
    while !should_close {
        
        // Poll events
        update_window_events();
        for events_this_frame {
            if it.type == .QUIT {
                should_close = true;
            }
        }
        
        draw_frame();
        
    }  
}

create_game_window :: () {
    window_width  : int  : 640;
    window_height : int  : 480;
    window_title  : string : "This shit dont work";

    window = create_window(window_width, window_height,
                            window_title, 
                            wanted_msaa = 0); 
                                                
    return;
}

create_opengl_context :: () {
    gl_create_context(window, 4, 5, false, true, 0);
    gl_load(*gl);
    
    gl_enable_debug_output(true);
    
    glViewport(0, 0, 640, 480);
    
    //glEnable(GL_CULL_FACE);
    //glCullFace(GL_BACK);    
    //glDebugMessageCallback(debug_message_callback,xx  0);
}

setup_render :: () {

    // Create shader programs
    vert_file_location := tprint("%/shader.vert", get_program_data_dir());
    frag_file_location := tprint("%/shader.frag", get_program_data_dir());
    
    vert, vert_loaded := read_entire_file(vert_file_location);
    frag, frag_loaded := read_entire_file(frag_file_location);    
    
    vertex_shader : u32;
    vertex_shader = glCreateShader(GL_VERTEX_SHADER);
    
    glShaderSource(vertex_shader, 1, *vert.data, null);
    glCompileShader(vertex_shader);
    
    fragment_shader : u32;
    fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
    
    glShaderSource(fragment_shader, 1, *frag.data, null);
    glCompileShader(fragment_shader);
    
    shader_program = glCreateProgram();    
    
    glAttachShader(shader_program, vertex_shader);
    glAttachShader(shader_program, fragment_shader);
    
    glLinkProgram(shader_program);
    
    success : s32 = 0;
    result := glGetProgramiv(shader_program, GL_LINK_STATUS, *success);
    print("Result: %\n", success);
    
    
    glDeleteShader(vertex_shader);
    glDeleteShader(fragment_shader);  
    
    glUseProgram(shader_program);
    
    // Generate Buffers
    glGenVertexArrays(1, *VAO);
    glGenBuffers(1, *VBO);
    glGenBuffers(1, *EBO);
    glGenBuffers(1, *IBO);
    
    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBindBuffer(GL_DRAW_INDIRECT_BUFFER, IBO);
    
    // Preallocated Vertex and Index buffers
    array_resize(*vertex_buffer, 4);
    array_resize(*index_buffer, 6);
    
    // Size of allocated space, in bytes
    indirect : IndirectCommand = .{
        6, // Element Count
        1, // Instance Count
        0, // First Index
        0, // Base Vertex
        0  // Base Index
    };
        
    array_add(*indirect_buffer, indirect);
    
    indirect_buffer_size : s64 = indirect_buffer.count * size_of(IndirectCommand);
    vertex_buffer_size : s64 = SAMPLE_VERTS.count * size_of(Vertex);
    index_buffer_size  : s64 = SAMPLE_INDXS.count * size_of(u64);
            
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_TRUE,  6 * size_of(float), cast(*void) 0); // Position Attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * size_of(float), cast(*void) (3 * size_of(float))); // Color Attribute
    
    // Preallocate the data by sending the empty, reserved arrays.
    glBufferData(GL_ARRAY_BUFFER, vertex_buffer_size, vertex_buffer.data, GL_DYNAMIC_DRAW);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_buffer_size, index_buffer.data, GL_DYNAMIC_DRAW);
    glBufferData(GL_DRAW_INDIRECT_BUFFER, indirect_buffer_size, indirect_buffer.data, GL_DYNAMIC_DRAW);
    
    // Much later in the program, upload vertex and index data.  
    glBufferData(GL_ARRAY_BUFFER, vertex_buffer_size, SAMPLE_VERTS.data, GL_DYNAMIC_DRAW);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_buffer_size, SAMPLE_INDXS.data, GL_DYNAMIC_DRAW);
    
    glBufferSubData(GL_ARRAY_BUFFER, 0, indirect_buffer_size, SAMPLE_VERTS.data);

}

swap_buffers :: (window: Window_Type, vsync := true) {  //@TODO: Implement other platforms and vsync
    #if OS == .WINDOWS {
        dc := GetDC(window);
        SwapBuffers(dc);
    }
}

draw_frame :: () {
    glClearColor(.5, .5, 1, 0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBindBuffer(GL_DRAW_INDIRECT_BUFFER, IBO);
    
    glBufferData(GL_DRAW_INDIRECT_BUFFER, size_of(IndirectCommand) * indirect_buffer.count, indirect_buffer.data, GL_DYNAMIC_DRAW);
    
    aspect_ratio : float = (640 / 480);

    model := make_translation_matrix4(.{0, 0, 0});
    view  := make_look_at_matrix(.{2, 2, 2}, .{0, 0, 0}, .{0, 0, 1}, false);
    proj  := transpose(make_projection_matrix(75.0 * PI / 180.0, aspect_ratio, 0.1, 1000.0, 0, 0, true));


    model_location := glGetUniformLocation(shader_program, "model");
    glUniformMatrix4fv(model_location, 1, GL_FALSE, *transpose(model).coef[0][0]);
    
    view_location := glGetUniformLocation(shader_program, "view");
    glUniformMatrix4fv(view_location, 1, GL_FALSE, *transpose(view).coef[0][0]);
    
    projection_location := glGetUniformLocation(shader_program, "proj");
    glUniformMatrix4fv(projection_location, 1, GL_FALSE, *proj.coef[0][0]);

    glFinish();

    //glDrawElements(GL_TRIANGLES, xx index_buffer.count, GL_UNSIGNED_INT, cast(*void) 0);
    glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, xx 0, xx indirect_buffer.count, 0);
    
    swap_buffers(window);
}

debug_message_callback :: (source : GLenum, type : GLenum, id : GLuint, severity : GLenum, length : GLsizei, message : *u8, userParam : *void) -> void #c_call
{
    new_context : Context;
    push_context new_context {
        print("GL CALLBACK: % type = %, severity = %, message = %\n", "foo", type, severity, to_string(message));
    }
}

#scope_file
#import "Basic";
#import "GL";
#import "Window_Creation";
#import "Windows";
#import "Input";
#import "String";
#import "File";
#import "System";
#import "Math";

get_program_data_dir :: () -> string {
    dir := trim_right(path_strip_filename(get_path_of_running_executable()), "/");
    return copy_string(dir);
}

After asking a round, many people described that Intel OpenGL Drivers are known to be broken with Integrated GPUs and MDI (Multi Draw Indirect). I wanted to create this post so people know that this is apparently not a solo issue. If anyone has any insight on this issue, please let me know. Thank you.

Hi! I’ve a nvidia geforce 1660 super graphic card and glMultiDrawElementsIndirect works fine. If it crash, this means you’ve forgot to bind a VBO or the datas you pass to gl functions are wrong.

If you’ve GL_INVALID_OPERATION this mean that a parameter you passed to a glFunction is wrong.

Yes, that is a discrete GPU. My crash only occurs when running the command on an Integrated GPU

This is strange. This is why I never buy a PC with an integrated GPU.

Well, integrated GPUs most commonly appear in Laptops, and i think its pretty important that my software supports laptops

As you said, Intel HD Graphics drivers are known to be broken and it is hopeless fact. MDI is a thing that used to be broken in API entry-points or in pipeline/shader/GLSL side. Probably because it was not a feature introduced by Direct3D 11, so Intel did not put real effort to make it work with stability. Also, appearently Intel is used to push experimental drivers to low end machines. You even can grab the features but you can’t rely on it.
I have worked with OpenGL specially for Intel HD Graphics. This experience may be described in two words: disgrace and frustration.