SDL2_RenderPresent aborts with i965: Failed to submit batchbuffer: Invalid argument

Hi all,

we’re migrating a SDL1 application to SDL2 which should run on two different x86 devices with Yocto and GBM/KMSDRM.
The first one which runs without problem is a Intel Tiger Lake-based device with Iris driver. The second one is older, a Intel Haswell-based device with i965 driver. This is the problematic one.

Our application is writing to a directly to a surface/backbuffer and then flips it to be visible. We need to use the Software renderer for that. The behavior with that should be the same as with SDL1 and X11 graphics stack.

We’re using kernel 5.4, amd64, SDL 2.0.20 and Mesa 21.3.5.

Here’s the problem:

When calling SDL_RenderPresent the application crashes on the i965-based device with the message

i965: Failed to submit batchbuffer: Invalid argument

while it’s running without problem on the Iris-based device.
I’m able to reproduce it with this simple test program:

#include <SDL2/SDL.h>
#include <iostream>
#include <vector>

int main( int argc, char** argv )
{
    SDL_Init( 0 );

//	SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE);

    std::cout << "Testing video drivers..." << '\n';
    std::vector< bool > drivers( SDL_GetNumVideoDrivers() );
    for( int i = 0; i < drivers.size(); ++i )
    {
        drivers[ i ] = ( 0 == SDL_VideoInit( SDL_GetVideoDriver( i ) ) );
        SDL_VideoQuit();
    }

    std::cout << "SDL_VIDEODRIVER available:";
    for( int i = 0; i < drivers.size(); ++i )
    {
        std::cout << " " << SDL_GetVideoDriver( i );
    }
    std::cout << '\n';

    std::cout << "SDL_VIDEODRIVER usable   :";
    for( int i = 0; i < drivers.size(); ++i )
    {
        if( !drivers[ i ] ) continue;
        std::cout << " " << SDL_GetVideoDriver( i );
    }
    std::cout << '\n';



    if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
        std::cerr << "SDL_Init(): " << SDL_GetError() << '\n';
        return EXIT_FAILURE;
    }
    std::cout << "SDL_VIDEODRIVER selected : " << SDL_GetCurrentVideoDriver() << '\n';

	std::cout << "DISPLAYS available:";
    for( int i = 0; i < SDL_GetNumVideoDisplays(); ++i )
    {
        std::cout << " " << * SDL_GetDisplayName(i);
    }
    std::cout << '\n';

    SDL_Window* window = SDL_CreateWindow
        (
        "SDL2 Test2",
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
        800, 600,
        SDL_WINDOW_FULLSCREEN_DESKTOP
        );
    if( nullptr == window )
    {
        std::cerr << "SDL_CreateWindow(): " << SDL_GetError() << '\n';
        return EXIT_FAILURE;
    }

    std::cout << "SDL_RENDER_DRIVER available:";
    for( int i = 0; i < SDL_GetNumRenderDrivers(); ++i )
    {
        SDL_RendererInfo info;
        SDL_GetRenderDriverInfo( i, &info );
        std::cout << " " << info.name;
    }
    std::cout << '\n';

//	SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengles2");
	SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
	SDL_SetHint(SDL_HINT_RENDER_OPENGL_SHADERS, 0);

    SDL_Renderer* renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_PRESENTVSYNC|SDL_RENDERER_ACCELERATED );
    if( nullptr == renderer )
    {
        std::cerr << "SDL_CreateRenderer(): " << SDL_GetError() << '\n';
        return EXIT_FAILURE;
    }
    SDL_RendererInfo info;
    SDL_GetRendererInfo( renderer, &info );
    std::cout << "SDL_RENDER_DRIVER selected : " << info.name << '\n';

	SDL_Surface* screen = ::SDL_GetWindowSurface(window);
	if( nullptr == screen )
    {
        std::cerr << "SDL_GetWindowSurface(): " << SDL_GetError() << '\n';
        return EXIT_FAILURE;
    }

	Uint32 background_color = 			::SDL_MapRGB(screen->format, 255, 255, 255);
	Uint32 background_color_overbright = ::SDL_MapRGB(screen->format, 160, 160, 160);

    bool running = true;
    unsigned char i = 0;
	int ret = 0;
    while( running )
    {
        SDL_Event ev;
        while( SDL_PollEvent( &ev ) )
        {
			std::cerr << "SDL_PollEvent(): " << ev.type << '\n';
            if( ( ev.type == SDL_QUIT ) ||
                ( ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_ESCAPE ) )
            {
                running = false;
            }
        }

	    ret = SDL_SetRenderDrawColor( renderer, i, i, i, SDL_ALPHA_OPAQUE );
		if( 0 != ret )
	    {
        	std::cerr << "SDL_SetRenderDrawColor(): " << SDL_GetError() << '\n';
    	    running = false;
    	}
        ret = SDL_RenderClear( renderer );
		if( 0 != ret )
	    {
        	std::cerr << "SDL_RenderClear(): " << SDL_GetError() << '\n';
    	    running = false;
    	}

    	std::cerr << "SDL_RenderPresent(): "  << '\n';
        SDL_RenderPresent( renderer );

/*    	std::cerr << "SDL_UpdateWindowSurface(): "  << '\n';
		ret = SDL_UpdateWindowSurface( window );
		if( 0 != ret )
	    {
        	std::cerr << "SDL_UpdateWindowSurface(): " << SDL_GetError() << '\n';
    	    running = false;
    	}*/
        i++;
    }

    SDL_DestroyRenderer( renderer );
    SDL_DestroyWindow( window );
    SDL_Quit();
    return 0;
}

The output of this program:

Testing video drivers...
error: XDG_RUNTIME_DIR not set in the environment.
SDL_VIDEODRIVER available: x11 wayland KMSDRM
SDL_VIDEODRIVER usable   : KMSDRM
error: XDG_RUNTIME_DIR not set in the environment.
SDL_VIDEODRIVER selected : KMSDRM
DISPLAYS available: 0
SDL_RENDER_DRIVER available: opengl opengles2 opengles software
SDL_RENDER_DRIVER selected : software
SDL_PollEvent(): 512
SDL_PollEvent(): 512
SDL_PollEvent(): 1024
SDL_PollEvent(): 512
SDL_PollEvent(): 1024
SDL_PollEvent(): 512
SDL_RenderPresent():
i965: Failed to submit batchbuffer: Invalid argument
Aborted

Any ideas how to fix this? I’ve already tried SDL_UpdateWindowSurface but it aborts with the same error message.

Thank you in advance!

When overriding the Mesa driver with

MESA_LOADER_DRIVER_OVERRIDE=crocus

the program still crashes but without any driver error message. Using

MESA_LOADER_DRIVER_OVERRIDE=iris

makes it work but the process produces 100% load on one CPU core.