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.

If you want your software support laptops with integrated GPU, don’t use modern opengl functions which are broken, I had an old laptop who didn’t support shaders so I had to use the stencil buffer. I’ve bought a new one with an amd graphic card, but the processor is too slow for playing games. (it’s a dual core) But it supports modern opengl.

Well, if it makes any difference, since posting this thread a driver update appeared which solved the crash with absolutely 0 changes to code, so, problem solved!?

You have to disable all onboard graphics due to a faulty AMD driver setting for “MultiGPU”.

  • Reinstall the Vulkan driver or reconfigure it.
  • Blacklist or exclude the driver.
  • If you get into a crash on Windows and you end up in recovery you risk the device reinstalling itself.
  • Check your bios settings.

There are no currently known overrides. You could pluck from Git and make…

The AMD drivers in WIn currently override anyway.

Since your program works fine on discrete GPUs but crashes on Intel’s integrated graphics, here are a few things to check:

  • Check for OpenGL Version Support:

  • Ensure your Intel iGPU actually supports glMultiDrawElementsIndirect. It’s an OpenGL 4.3+ function, but some Intel drivers have spotty support for indirect drawing, even if they claim OpenGL 4.5 compliance.

  • Validate Buffer Bindings:
    Before calling glMultiDrawElementsIndirect, ensure IBO (Indirect Buffer Object) is properly bound with GL_DRAW_INDIRECT_BUFFER.
    You might also want to use glGetIntegerv(GL_DRAW_INDIRECT_BUFFER_BINDING, &someVar); to confirm it’s bound before drawing.

  • Check for Driver Bugs:
    Intel iGPUs have known issues with certain indirect draw calls. You could try using glDrawElementsInstancedBaseVertexBaseInstance instead of glMultiDrawElementsIndirect as a test.
    If possible, downgrade to a different Intel driver version (e.g., 32.0.101.6129 or earlier).

  • Try a Debug Context:
    Enable GL_DEBUG_OUTPUT and register a glDebugMessageCallback to get more detailed error messages.

  • Check Struct Alignment:
    Ensure your IndirectCommand struct matches the OpenGL specification. It should be tightly packed with proper alignment.

Let me know what you find! If needed, I can help tweak the code for debugging.