Skip to content

Instantly share code, notes, and snippets.

@lephilousophe
Created June 8, 2024 11:32
Show Gist options
  • Save lephilousophe/1f8bb6757a8d621e6400fc9edd57a306 to your computer and use it in GitHub Desktop.
Save lephilousophe/1f8bb6757a8d621e6400fc9edd57a306 to your computer and use it in GitHub Desktop.
Blend color bug on big-endian
//
// cc -g blend_color_bug.c -lSDL2 -lGLESv2 -o blend_color_bug
//
#include <GLES2/gl2.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#include <stdbool.h>
typedef struct
{
unsigned int width;
unsigned int height;
// Handle to a program object
GLuint testProg;
unsigned int drawMode;
} UserData;
///
// Create a shader object, load the shader source, and
// compile the shader.
//
GLuint LoadShader ( GLenum type, const char *shaderSrc )
{
GLuint shader;
GLint compiled;
// Create the shader object
shader = glCreateShader ( type );
if ( shader == 0 )
{
fprintf( stderr, "glCreateShader error: %d\n", glGetError() );
return 0;
}
// Load the shader source
glShaderSource ( shader, 1, &shaderSrc, NULL );
// Compile the shader
glCompileShader ( shader );
// Check the compile status
glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );
if ( !compiled )
{
GLint infoLen = 0;
glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );
if ( infoLen > 1 )
{
char* infoLog = malloc (sizeof(char) * infoLen );
glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );
fprintf( stderr, "Error compiling shader:\n%s\n", infoLog );
free ( infoLog );
}
glDeleteShader ( shader );
return 0;
}
return shader;
}
GLuint LinkProgram(const char *vertexSrc, const char *fragmentSrc) {
GLuint vertexShader;
GLuint fragmentShader;
GLuint programObject;
GLint linked;
// Load the vertex/fragment shaders
vertexShader = LoadShader ( GL_VERTEX_SHADER, vertexSrc );
fragmentShader = LoadShader ( GL_FRAGMENT_SHADER, fragmentSrc );
// Create the program object
programObject = glCreateProgram ( );
if ( programObject == 0 )
return 0;
glAttachShader ( programObject, vertexShader );
glAttachShader ( programObject, fragmentShader );
// Link the program
glLinkProgram ( programObject );
// Check the link status
glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );
if ( !linked )
{
GLint infoLen = 0;
glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );
if ( infoLen > 1 )
{
char* infoLog = malloc (sizeof(char) * infoLen );
glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );
fprintf ( stderr, "Error linking program:\n%s\n", infoLog );
free ( infoLog );
}
glDeleteProgram ( programObject );
return 0;
}
return programObject;
}
///
// Initialize the shader and program object
//
int Init ( UserData *userData )
{
GLbyte vShaderStr[] =
"#version 100\n"
"attribute vec4 aPosition;\n"
"attribute vec4 aColor;\n"
"varying vec4 vColor;\n"
"void main() {\n"
" gl_Position = aPosition;\n"
" vColor = aColor;\n"
"}\n";
GLbyte fShaderStr[] =
"#version 100\n"
"precision mediump float;\n"
"varying vec4 vColor;\n"
"void main() {\n"
" gl_FragColor = vColor;\n"
"}\n";
// Store the test program object
userData->testProg = LinkProgram(vShaderStr, fShaderStr);
if (userData->testProg == 0) {
return GL_FALSE;
}
glClearColor ( 0.0f, 0.0f, 0.0f, 0.0f );
return GL_TRUE;
}
///
// Draw a triangle using the shader pair created in Init()
//
void DrawTest ( UserData *userData )
{
GLfloat vVertices[] = {
0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.0f, 0.5f,
-0.5f, -0.0f, 0.5f,
-0.5f, -0.0f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, -0.0f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.0f, 0.5f,
-0.5f, 0.5f, 0.5f,
};
GLfloat vColors_f[] = {
0.f, 1.f, 0.f, 1.f,
0.f, 1.f, 0.f, 1.f,
0.f, 1.f, 0.f, 1.f,
0.f, 1.f, 0.f, 1.f,
1.f, 0.f, 0.f, 1.f,
1.f, 0.f, 0.f, 1.f,
1.f, 0.f, 0.f, 1.f,
0.f, 0.f, 0.f, 0.f,
0.f, 0.f, 1.f, 1.f,
0.f, 0.f, 1.f, 1.f,
0.f, 0.f, 1.f, 1.f,
};
glEnable(GL_BLEND);
switch(userData->drawMode) {
case 0:
glBlendColor(1.f, 1.f, 1.f, 0.f);
break;
case 1:
glBlendColor(0.f, 1.f, 1.f, 1.f);
break;
case 2:
glBlendColor(1.f, 0.f, 1.f, 1.f);
break;
case 3:
glBlendColor(1.f, 1.f, 0.f, 1.f);
break;
case 4:
glBlendColor(0.f, 0.f, 0.f, 0.f);
break;
}
glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR);
// Use the program object
glUseProgram ( userData->testProg );
// Load the vertex data
glVertexAttribPointer ( glGetAttribLocation(userData->testProg, "aPosition"), 3, GL_FLOAT, GL_FALSE, 0, vVertices );
glVertexAttribPointer ( glGetAttribLocation(userData->testProg, "aColor"), 4, GL_FLOAT, GL_FALSE, 0, vColors_f );
glEnableVertexAttribArray ( glGetAttribLocation(userData->testProg, "aPosition") );
glEnableVertexAttribArray ( glGetAttribLocation(userData->testProg, "aColor") );
glDrawArrays ( GL_TRIANGLE_STRIP, 0, sizeof(vVertices)/(sizeof(GLfloat)*3) );
}
void Draw(UserData *userData) {
// Set the viewport
glViewport ( 0, 0, userData->width, userData->height );
// Clear the color buffer
glClear ( GL_COLOR_BUFFER_BIT );
DrawTest(userData);
}
static const char *MODES[] = {
"Blue and Red triangles + Green square",
"Blue triangle + Green square",
"Blue and Red triangles",
"Red triangle + Green square",
"Black screen",
};
void changeState( const SDL_Event *e, UserData *userData ) {
switch (e->key.keysym.sym) {
case SDLK_g:
userData->drawMode++;
if (userData->drawMode > 4) {
userData->drawMode = 0;
}
break;
}
fprintf(stderr, "Current mode: <Current mode>=%d - %s\n",
userData->drawMode,
MODES[userData->drawMode]);
}
int main()
{
UserData userData;
memset(&userData, 0, sizeof(userData));
userData.width = 640;
userData.height = 480;
// For EGL on X11 to use with RenderDoc
#ifdef SDL_VIDEO_DRIVER_X11
SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, "1");
SDL_SetHint(SDL_HINT_OPENGL_ES_DRIVER, "1");
#endif
SDL_InitSubSystem(SDL_INIT_VIDEO);
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_Window *window = SDL_CreateWindow("SDL GLES2", -1, -1, userData.width, userData.height, SDL_WINDOW_OPENGL);
if (!window) {
fprintf(stderr, "SDL_CreateWindow: error=%s\n", SDL_GetError() );
return 1;
}
SDL_GLContext context = SDL_GL_CreateContext(window);
if (!context) {
fprintf(stderr, "SDL_GL_CreateContext: error=%s\n", SDL_GetError() );
return 1;
}
if (SDL_GL_MakeCurrent(window, context)) {
fprintf(stderr, "SDL_GL_MakeCurrent: error=%s\n", SDL_GetError() );
return 1;
}
fprintf(stderr, "%s\n", glGetString(GL_RENDERER) );
if ( !Init ( &userData ) ) {
fprintf(stderr, "Failed to init\n");
return 0;
}
bool quit = false;
SDL_Event e;
memset(&e, 0, sizeof(e));
// Print default state
changeState(&e, &userData);
while (!quit) {
while (SDL_PollEvent(&e) != 0) {
switch (e.type) {
case SDL_QUIT: quit = true; break;
case SDL_KEYUP: changeState( &e, &userData ); break;
}
}
Draw( &userData );
SDL_GL_SwapWindow(window);
}
SDL_GL_DeleteContext(context);
SDL_DestroyWindow(window);
SDL_Quit();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment