RetroPie forum home
    • Recent
    • Tags
    • Popular
    • Home
    • Docs
    • Register
    • Login

    Run SDL2 application on RetroPie for Raspberry Pi

    Scheduled Pinned Locked Moved Ideas and Development
    openglsdl2
    3 Posts 2 Posters 1.2k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Z
      zer0divider
      last edited by zer0divider

      Hi, a while ago I made a little game with SDL2/OpenGL that I now want to port to the Raspberry Pi (running a pure RetroPie image ), so that it can be run via the EmulationStation. However I made a small test application to check, whether basic window-creation and rendering with GLES works on the Raspberry, but the application only shows a black screen and the mouse cursor.

      #include <SDL2/SDL.h>
      #include <SDL2/SDL_opengles2.h>
      
      #define TRIANGLE_SIZE 0.9f
      
      // Shader sources
      const GLchar* vertexSource =
      	"\n"
          "attribute vec2 position;\n"
      	"attribute vec4 color;\n"
      	"varying vec4 out_color;\n"
      	"uniform mat4 proj_mat;\n"
      	"uniform mat4 model_mat;\n"
          "void main()\n"
          "{\n"
          "    gl_Position = proj_mat*model_mat*vec4(position, 0, 1.0);\n"
      	"    out_color = color;\n"
          "}\n";
      
      const GLchar* fragmentSource =
          "varying vec4 out_color;\n"
          "void main()\n"
          "{\n"
          "    gl_FragColor = out_color;\n"
          "}\n";
      
      typedef struct{
      	float x;
      	float y;
      	float r;
      	float g;
      	float b;
      	float a;
      }Vertex;
      
      void resize(int w, int h, GLint proj_mat_location);
      
      void setRotationMatrix(float rad, GLint model_mat_location);
      
      void setOrthoMatrix(float left, float right, float bottom,
      					float top, float n, float f, GLint proj_mat_location);
      
      int main()
      {
      	printf("Initializing Framework...\n");
      	//initialize SDL
      	if (SDL_Init(SDL_INIT_VIDEO) < 0)
      	{
      		printf("ERROR: Failed to initialize SDL: %s\n", SDL_GetError());
      		return 1;
      	}
      
      	//setting flags
      	unsigned int sdl_flags = SDL_WINDOW_OPENGL;
      	SDL_DisplayMode current;
      	current.w = 800;
      	current.h = 600;
      	if(sdl_flags & SDL_WINDOW_FULLSCREEN){
      		//getting current display-resolution
      		if (SDL_GetDesktopDisplayMode(0, &current) != 0){
      			printf("Could not retrieve current display resolution: %s\n", SDL_GetError());
      		}
      	}
      	else{
      		sdl_flags |= SDL_WINDOW_RESIZABLE; 
      	}
      	printf("Setting resolution to %dx%d\n", current.w, current.h);
      
      	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
      	SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32);
      	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
      	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
      	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
      	SDL_Window * window = SDL_CreateWindow("Triangles!", 0,0, current.w, current.h, sdl_flags);
      	if(window == NULL)
      	{
      		printf("Error while creating window: %s\n", SDL_GetError());
      		return 1;
      	}
      
      	printf("Creating OpenGL context...\n");
      	//create gl-context
      	if(!SDL_GL_CreateContext(window))
      	{
      		printf("Error while creating OpenGL Context: %s\n", SDL_GetError());
      		return 1;
      	}
      
      	//init GL parameters
      	glClearColor(1.f, 1.f, 1.f, 1.f);
      	glEnable(GL_BLEND);
      	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      	glDisable(GL_DEPTH_TEST);
      	glDisable(GL_CULL_FACE);
      
          // Create a Vertex Buffer Object and copy the vertex data to it
          GLuint vbo;
          glGenBuffers(1, &vbo);
      	Vertex vertices[3];
      	for(int i = 0; i < 3; i++){
      		vertices[i].x = cos(i*(2*M_PI/3.0f))*TRIANGLE_SIZE;
      		vertices[i].y = sin(i*(2*M_PI/3.0f))*TRIANGLE_SIZE;
      		vertices[i].r = 0.0f;
      		vertices[i].g = 0.0f;
      		vertices[i].b = 0.0f;
      		vertices[i].a = 1.0f;
      	}
      	vertices[0].r = 1.0f;
      	vertices[1].g = 1.0f;
      	vertices[2].b = 1.0f;
      
          glBindBuffer(GL_ARRAY_BUFFER, vbo);
          glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
      
      	char buffer[1024];
      	buffer[0] = '\0';
          // Create and compile the vertex shader
          GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
          glShaderSource(vertexShader, 1, &vertexSource, NULL);
          glCompileShader(vertexShader);
      	glGetShaderInfoLog(vertexShader, 1024, 0, buffer);
      	if(buffer[0] != '\0')//non-empty
      	{
      		printf("Error while compiling vertex shader:\n%s\n", (const char*)buffer);
      	}
      
      	buffer[0] = '\0';
          // Create and compile the fragment shader
          GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
          glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
          glCompileShader(fragmentShader);
      	glGetShaderInfoLog(fragmentShader, 1024, 0, buffer);
      	if(buffer[0] != '\0')//non-empty
      	{
      		printf("Error while compiling fragment shader:\n%s\n", (const char*)buffer);
      	}
      
      	buffer[0] = '\0';
          // Link the vertex and fragment shader into a shader program
          GLuint shaderProgram = glCreateProgram();
          glAttachShader(shaderProgram, vertexShader);
          glAttachShader(shaderProgram, fragmentShader);
          // glBindFragDataLocation(shaderProgram, 0, "outColor");
          glLinkProgram(shaderProgram);
      	//Program Error-Log
      	glGetProgramInfoLog(shaderProgram, 1024, 0, buffer);
      	if(buffer[0] != '\0')//non-empty
      	{
      		printf("Error while linking shader program:\n%s\n", (const char*)buffer);
      	}
      	int status;
      	glGetShaderiv(shaderProgram, GL_LINK_STATUS, &status);
      	if(status == GL_FALSE){
      		return 1;
      	}
          glUseProgram(shaderProgram);
      
          // Specify the layout of the vertex data
          GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
          glEnableVertexAttribArray(posAttrib);
          glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
      
      	GLint colorAttrib = glGetAttribLocation(shaderProgram, "color");
      	glEnableVertexAttribArray(colorAttrib);
      	glVertexAttribPointer(colorAttrib, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*)(2*sizeof(float)));
      
      	GLint proj_mat_location = glGetUniformLocation(shaderProgram, "proj_mat");
      	GLint model_mat_location = glGetUniformLocation(shaderProgram, "model_mat");
      
      	resize(current.w, current.h, proj_mat_location);
          printf("Running...\n");
      	// run main loop
      	while(1){
      
              SDL_Event e;
              while(SDL_PollEvent(&e)){
      			if(e.type == SDL_QUIT)
      				return 0;
      			else if(e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_RESIZED)
      				resize(e.window.data1, e.window.data2, proj_mat_location);
      		}
      
      		glClear(GL_COLOR_BUFFER_BIT);
      		setRotationMatrix(SDL_GetTicks()/1000.f * M_PI/2.f, model_mat_location);
      		glDrawArrays(GL_TRIANGLES, 0, 3);
      		SDL_GL_SwapWindow(window);
      		SDL_Delay(10);
      		if(SDL_GetTicks() > 5000)// quit after 5 seconds
      			return 0;
      	}
      
      	return 0;
      }
      
      void setRotationMatrix(float rad, GLint model_mat_location)
      {
      	float sin_angle = sin(rad);
      	float cos_angle = cos(rad);
      	float mat[16];
      	mat[0] = cos_angle;
      	mat[1] = sin_angle;
      	mat[2] = 0;
      	mat[3] = 0;
      
      	mat[4] = -sin_angle;
      	mat[5] = cos_angle;
      	mat[6] = 0;
      	mat[7] = 0;
      
      	mat[8] = 0;
      	mat[9] = 0;
      	mat[10] = 1;
      	mat[11] = 0;
      
      	mat[12] = 0;
      	mat[13] = 0;
      	mat[14] = 0;
      	mat[15] = 1;
      	glUniformMatrix4fv(model_mat_location, 1, GL_FALSE, mat);
      }
      
      void resize(int w, int h, GLint proj_mat_location)
      {
      	glViewport(0, 0, w, h);
      	if(w > h){
      		float f = w/(float)h;
      		setOrthoMatrix(-f, f, -1, 1, -1, 1, proj_mat_location);
      	}else{
      		float f = h/(float)w;
      		setOrthoMatrix(-1, 1, -f, f, -1, 1, proj_mat_location);
      	}
      }
      
      void setOrthoMatrix(float left, float right, float bottom,
      					float top, float n, float f, GLint proj_mat_location)
      {
      	float mat[16];
      	mat[0]  = 2.0f/(right-left);
              mat[1]  = 0.f;
              mat[2]  = 0.f;
             mat[3]  = 0.f;
             mat[4]  = 0.f;
             mat[5]  = 2.0f/(top-bottom);
             mat[6]  = 0.f;
             mat[7]  = 0.f;
             mat[8]  = 0.f;
             mat[9]  = 0.f;
             mat[10] =-2.f/(f-n);
             mat[11] = 0.f;
      	mat[12] =-(right+left)/(right-left);
      	mat[13] =-(top+bottom)/(top-bottom);
      	mat[14] =-(f+n)/(f-n);
              mat[15] = 1.f;
      	glUniformMatrix4fv(proj_mat_location, 1, GL_FALSE, mat);
      }
      

      The code can be compiled with gcc:

      gcc main.c -o gles_test -lSDL2 -lSDL2main -lGLESv2 -lm
      

      On my linux machine (manjaro) this code works as expected, but not on the Raspberry Pi with RetroPie though. I tried launching in windowed and fullscreen-mode (SDL-flag) but nothing is shown on my Raspberry.
      Any clues?

      1 Reply Last reply Reply Quote 0
      • mituM
        mitu Global Moderator
        last edited by

        Depending on the Pi model, you'll need to use a different GLES implementation. On the RetroPie Pi3 image, where the legacy/proprietary GLES driver is used, you need to link against libbrcmGLESv2:

        gcc  main.c -o gles_test -lSDL2 -lSDL2main -L/opt/vc/lib -lbrcmGLESv2 -lm
        

        On the Pi4, where the Mesa GL driver is used for GL, the program works without any changes.

        1 Reply Last reply Reply Quote 1
        • Z
          zer0divider
          last edited by

          Thanks, the incorrect library was the main issue with this program! Another few things I noticed while experimenting with this code snippet:

          • apparently Vertex Arrays don't work with GLESv2 (stackoverflow), so you have to use Vertex Buffers
          • errors in vertex/fragment shader are only detected when linking the program, not on compilation
          • when using opengl extensions (e.g. GL_OES_mapbuffer, see Raspberry Pi Video Core API for available extensions) you have to put#define GL_GLEXT_PROTOTYPES before including SDL2/SDL_opengles2.h and additionally link with -lbrcmEGL in order to get the functions
          • if you don't call SDL_Quit() before exiting your program, the raspberry pi's console will be blocked (it seems like the input focus is still on the SDL window otherwise)

          I updated the code with a few more comments and fixed some stuff:
          main.c:

          #include <SDL2/SDL.h>
          #include <SDL2/SDL_opengles2.h>
          
          #define FULLSCREEN 1
          #define TRIANGLE_SIZE 0.9f
          #define INFO_LOG_BUFFER_SIZE 1024
          
          // Shader sources
          const GLchar* vertexSource =
          	"attribute vec2 position;\n"
          	"attribute vec4 color;\n"
          	"varying vec4 out_color;\n"
          	"uniform mat4 proj_mat;\n"
          	"uniform mat4 model_mat;\n"
          	"void main()\n"
          	"{\n"
          	"    gl_Position = proj_mat*model_mat*vec4(position, 0, 1.0);\n"
          	"    out_color = color;\n"
          	"}\n";
          
          const GLchar* fragmentSource =
          	"varying vec4 out_color;\n"
          	"void main()\n"
          	"{\n"
          	"    gl_FragColor = out_color;\n"
          	"}\n";
          
          // struct representing a colored 2D vertex
          typedef struct{
          	float x;
          	float y;
          	float r;
          	float g;
          	float b;
          	float a;
          }Vertex;
          
          //  adjusting viewport and projection matrix to fit current window dimensions 
          void resize(int w, int h, GLint proj_mat_location);
          
          // create rotation matrix (z axis) and upload to shader at given uniform location
          void setRotationMatrix(float rad, GLint model_mat_location);
          
          // create orthogonal matrix and upload to shader at given uniform location
          void setOrthoMatrix(float left, float right, float bottom,
          					float top, float n, float f, GLint proj_mat_location);
          
          int main()
          {
          	// initialize SDL
          	printf("Initializing SDL...\n");
          	if (SDL_Init(SDL_INIT_VIDEO) < 0)
          	{
          		printf("ERROR: Failed to initialize SDL: %s\n", SDL_GetError());
          		return 1;
          	}
          
          	// setting SDL flags
          	unsigned int sdl_flags = SDL_WINDOW_OPENGL;
          	SDL_DisplayMode current;
          	current.w = 800;
          	current.h = 600;
          	if(FULLSCREEN){
          		sdl_flags |= SDL_WINDOW_FULLSCREEN;
          		// getting current display-resolution
          		if (SDL_GetDesktopDisplayMode(0, &current) != 0){
          			printf("Could not retrieve current display resolution: %s\n", SDL_GetError());
          		}
          	}else{
          		sdl_flags |= SDL_WINDOW_RESIZABLE; 
          	}
          	const char * fullscreen_string = FULLSCREEN ? "(fullscreen)" : "";
          	printf("Creating window %dx%d%s...\n", current.w, current.h, fullscreen_string);
          
          	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
          	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
          	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
          	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
          	// use GLESv2
          	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
          	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
          	// activate V-Sync
          	SDL_GL_SetSwapInterval(1);
          	SDL_Window * window = SDL_CreateWindow("Hello GLES!", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
          											current.w, current.h, sdl_flags);
          	if(window == NULL)
          	{
          		printf("Error while creating window: %s\n", SDL_GetError());
          		SDL_Quit();
          		return 1;
          	}
          
          	// create gl context
          	printf("Creating OpenGL context...\n");
          	SDL_GLContext context = SDL_GL_CreateContext(window);
          	if(context == NULL)
          	{
          		printf("Error while creating OpenGL Context: %s\n", SDL_GetError());
          		SDL_DestroyWindow(window);
          		SDL_Quit();
          		return 1;
          	}
          
          	// hide mouse cursor
          	SDL_ShowCursor(0);
          
          	// set gl parameters
          	glClearColor(1.f, 1.f, 1.f, 1.f);
          	glEnable(GL_BLEND);
          	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
          	glDisable(GL_DEPTH_TEST);
          	glDisable(GL_CULL_FACE);
          
          	// create RGB-triangle data and copy to vertex buffer
          	GLuint vbo;
          	glGenBuffers(1, &vbo);
          	glBindBuffer(GL_ARRAY_BUFFER, vbo);
          	Vertex vertices[3];
          	for(int i = 0; i < 3; i++){
          		vertices[i].x = cos(i*(2*M_PI/3.0f))*TRIANGLE_SIZE;
          		vertices[i].y = sin(i*(2*M_PI/3.0f))*TRIANGLE_SIZE;
          		vertices[i].r = 0.0f;
          		vertices[i].g = 0.0f;
          		vertices[i].b = 0.0f;
          		vertices[i].a = 1.0f;
          	}
          	vertices[0].r = 1.0f;
          	vertices[1].g = 1.0f;
          	vertices[2].b = 1.0f;
          	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
          
          	printf("Compiling Shader...\n");
          	// compile the vertex shader
          	GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
          	glShaderSource(vertexShader, 1, &vertexSource, NULL);
          	glCompileShader(vertexShader);
          
          	// compile the fragment shader
          	GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
          	glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
          	glCompileShader(fragmentShader);
          
          	// link vertex and fragment shader into shader program
          	GLuint shader_program = glCreateProgram();
          	glAttachShader(shader_program, vertexShader);
          	glAttachShader(shader_program, fragmentShader);
          	glLinkProgram(shader_program);
          	// program info log
          	char info_log_buffer[INFO_LOG_BUFFER_SIZE];
          	info_log_buffer[0] = '\0';
          	glGetProgramInfoLog(shader_program, INFO_LOG_BUFFER_SIZE, 0, info_log_buffer);
          	if(info_log_buffer[0] != '\0')
          		printf("Shader-Program-Info:\n%s\n", info_log_buffer);
          
          	// linking successfull?
          	int status;
          	glGetProgramiv(shader_program, GL_LINK_STATUS, &status);
          	if(status == GL_FALSE){
          		SDL_GL_DeleteContext(context);
          		SDL_DestroyWindow(window);
          		SDL_Quit();
          		return 1;
          	}
          	// use custom shader
          	glUseProgram(shader_program);
          
          	// feed vertex attributes with triangle-data
          	// position
          	GLint posAttrib = glGetAttribLocation(shader_program, "position");
          	glEnableVertexAttribArray(posAttrib);
          	glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
          
          	// color
          	GLint colorAttrib = glGetAttribLocation(shader_program, "color");
          	glEnableVertexAttribArray(colorAttrib);
          	glVertexAttribPointer(colorAttrib, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*)(2*sizeof(float)));
          
          	// retrieve uniform locations
          	GLint proj_mat_location = glGetUniformLocation(shader_program, "proj_mat");
          	GLint model_mat_location = glGetUniformLocation(shader_program, "model_mat");
          
          	// inital resize to set viewport and projection mastrix
          	resize(current.w, current.h, proj_mat_location);
          
          	// run main loop
          	printf("Rendering triangle...\n");
          	while(1){
          		int quit = 0;
          		SDL_Event e;
          		while(SDL_PollEvent(&e)){
          			if(e.type == SDL_QUIT || e.type == SDL_KEYDOWN)
          				quit = 1;
          			else if(e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_RESIZED)
          				resize(e.window.data1, e.window.data2, proj_mat_location);
          		}
          		if(quit){
          			printf("Program was quit by user!\n");
          			break;
          		}
          		glClearColor(1,1,1,1);
          		glClear(GL_COLOR_BUFFER_BIT);
          		setRotationMatrix(SDL_GetTicks()/1000.f * M_PI/2.f, model_mat_location);
          		glDrawArrays(GL_TRIANGLES, 0, 3);
          		SDL_GL_SwapWindow(window);
          	}
          
          	// clean up
          	SDL_GL_DeleteContext(context);
          	SDL_DestroyWindow(window);
          	SDL_Quit();
          
          	return 0;
          }
          
          void setRotationMatrix(float rad, GLint model_mat_location)
          {
          	// rotation around z axis
          	float sin_angle = sin(rad);
          	float cos_angle = cos(rad);
          	float mat[16];
          	mat[0] = cos_angle;
          	mat[1] = sin_angle;
          	mat[2] = 0;
          	mat[3] = 0;
          
          	mat[4] = -sin_angle;
          	mat[5] = cos_angle;
          	mat[6] = 0;
          	mat[7] = 0;
          
          	mat[8] = 0;
          	mat[9] = 0;
          	mat[10] = 1;
          	mat[11] = 0;
          
          	mat[12] = 0;
          	mat[13] = 0;
          	mat[14] = 0;
          	mat[15] = 1;
          	glUniformMatrix4fv(model_mat_location, 1, GL_FALSE, mat);
          }
          
          void resize(int w, int h, GLint proj_mat_location)
          {
          	glViewport(0, 0, w, h);
          	// set orthogonal view so that coordinates [-1, 1] area always visible and proportional on x and y axis
          	if(w > h){
          		float f = w/(float)h;
          		setOrthoMatrix(-f, f, -1, 1, -1, 1, proj_mat_location);
          	}else{
          		float f = h/(float)w;
          		setOrthoMatrix(-1, 1, -f, f, -1, 1, proj_mat_location);
          	}
          }
          
          void setOrthoMatrix(float left, float right, float bottom,
          					float top, float n, float f, GLint proj_mat_location)
          {
          	// set orthogonal matrix
          	float mat[16];
          	mat[0]  = 2.0f/(right-left);
          	mat[1]  = 0.f;
          	mat[2]  = 0.f;
          	mat[3]  = 0.f;
          
          	mat[4]  = 0.f;
          	mat[5]  = 2.0f/(top-bottom);
          	mat[6]  = 0.f;
          	mat[7]  = 0.f;
          
          	mat[8]  = 0.f;
          	mat[9]  = 0.f;
          	mat[10] =-2.f/(f-n);
          	mat[11] = 0.f;
          
          	mat[12] =-(right+left)/(right-left);
          	mat[13] =-(top+bottom)/(top-bottom);
          	mat[14] =-(f+n)/(f-n);
          	mat[15] = 1.f;
          	glUniformMatrix4fv(proj_mat_location, 1, GL_FALSE, mat);
          }
          
          

          Compiles and runs on Raspberry Pi 3:

          gcc main.c -o gles_test -lSDL2 -lSDL2main -L/opt/vc/lib -lbrcmGLESv2 -lm
          ./gles_test
          

          triangle.png

          I hope this might be usefull to someone :)

          @mitu Thanks again for your quick reply!

          1 Reply Last reply Reply Quote 1
          • First post
            Last post

          Contributions to the project are always appreciated, so if you would like to support us with a donation you can do so here.

          Hosting provided by Mythic-Beasts. See the Hosting Information page for more information.