Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenGL2: Fix WebGL and add fallback to GLES2 #665

Merged
merged 2 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,12 @@ embedded System-on-a-Chip and mobile platforms.

The opengl1 renderer does not have OpenGL ES support.

The `r_useOpenGLES` cvar controls whether to use OpenGL or OpenGL ES API.
Set to -1 for auto (default), 0 for OpenGL, and 1 for OpenGL ES. It should be
The opengl2 renderer will try both OpenGL and OpenGL ES APIs to find one that
works. The `r_preferOpenGLES` cvar controls which API to try first.
Set it to -1 for auto (default), 0 for OpenGL, and 1 for OpenGL ES. It should be
set using command line arguments:

ioquake3 +set cl_renderer opengl2 +set r_useOpenGLES 1
ioquake3 +set cl_renderer opengl2 +set r_preferOpenGLES 1


# Console
Expand Down
2 changes: 1 addition & 1 deletion code/renderercommon/qgl.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void);
GLE(void, TexParameterf, GLenum target, GLenum pname, GLfloat param) \
GLE(void, TexParameteri, GLenum target, GLenum pname, GLint param) \
GLE(void, TexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels) \
GLE(void, Translatef, GLfloat x, GLfloat y, GLfloat z) \
GLE(void, Viewport, GLint x, GLint y, GLsizei width, GLsizei height) \

// OpenGL 1.0/1.1 and OpenGL ES 1.x but not OpenGL 3.2 core profile
Expand All @@ -98,6 +97,7 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void);
GLE(void, ShadeModel, GLenum mode) \
GLE(void, TexCoordPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) \
GLE(void, TexEnvf, GLenum target, GLenum pname, GLfloat param) \
GLE(void, Translatef, GLfloat x, GLfloat y, GLfloat z) \
GLE(void, VertexPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) \

// OpenGL 1.0/1.1 and 3.2 core profile but not OpenGL ES 1.x
Expand Down
5 changes: 2 additions & 3 deletions code/renderergl2/glsl/depthblur_fp.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,9 @@ vec4 depthGaussian1D(sampler2D imageMap, sampler2D depthMap, vec2 tex, float zFa
#endif

float zLimit = 5.0 / zFar;
int i, j;
for (i = 0; i < 2; i++)
for (int i = 0; i < 2; i++)
{
for (j = 1; j < BLUR_SIZE; j++)
for (int j = 1; j < BLUR_SIZE; j++)
{
vec2 offset = direction * (float(j) - 0.25) + nudge;
#if defined(USE_DEPTH)
Expand Down
3 changes: 1 addition & 2 deletions code/renderergl2/glsl/ssao_fp.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@ float ambientOcclusion(sampler2D depthMap, const vec2 tex, const float zFarDivZN

float invZFar = 1.0 / zFar;
float zLimit = 20.0 * invZFar;
int i;
for (i = 0; i < NUM_SAMPLES; i++)
for (int i = 0; i < NUM_SAMPLES; i++)
{
vec2 offset = rmat * poissonDisc[i] * offsetScale;
float sampleDiff = getLinearDepth(depthMap, tex + offset, zFarDivZNear) - sampleZ;
Expand Down
17 changes: 14 additions & 3 deletions code/renderergl2/tr_extensions.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,23 @@ void GLimp_InitExtraExtensions(void)
goto done;

extension = "GL_EXT_occlusion_query_boolean";
if (SDL_GL_ExtensionSupported(extension))
if (qglesMajorVersion >= 3 || SDL_GL_ExtensionSupported(extension))
{
glRefConfig.occlusionQuery = qtrue;
glRefConfig.occlusionQueryTarget = GL_ANY_SAMPLES_PASSED;

QGL_ARB_occlusion_query_PROCS;
if (qglesMajorVersion >= 3) {
QGL_ARB_occlusion_query_PROCS;
} else {
// GL_EXT_occlusion_query_boolean uses EXT suffix
#undef GLE
#define GLE(ret, name, ...) qgl##name = (name##proc *) SDL_GL_GetProcAddress("gl" #name "EXT");

QGL_ARB_occlusion_query_PROCS;

#undef GLE
#define GLE(ret, name, ...) qgl##name = (name##proc *) SDL_GL_GetProcAddress("gl" #name);
}

ri.Printf(PRINT_ALL, result[glRefConfig.occlusionQuery], extension);
}
Expand Down Expand Up @@ -137,7 +148,7 @@ void GLimp_InitExtraExtensions(void)

// GL_OES_element_index_uint
extension = "GL_OES_element_index_uint";
if (SDL_GL_ExtensionSupported(extension))
if (qglesMajorVersion >= 3 || SDL_GL_ExtensionSupported(extension))
{
glRefConfig.vaoCacheGlIndexType = GL_UNSIGNED_INT;
glRefConfig.vaoCacheGlIndexSize = sizeof(unsigned int);
Expand Down
7 changes: 6 additions & 1 deletion code/renderergl2/tr_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,12 @@ static void InitOpenGL( void )
glRefConfig.maxVertexAttribs = temp;

// reserve 160 components for other uniforms
qglGetIntegerv( GL_MAX_VERTEX_UNIFORM_COMPONENTS, &temp );
if ( qglesMajorVersion ) {
qglGetIntegerv( GL_MAX_VERTEX_UNIFORM_VECTORS, &temp );
temp *= 4;
} else {
qglGetIntegerv( GL_MAX_VERTEX_UNIFORM_COMPONENTS, &temp );
}
glRefConfig.glslMaxAnimatedBones = Com_Clamp( 0, IQM_MAX_JOINTS, ( temp - 160 ) / 16 );
if ( glRefConfig.glslMaxAnimatedBones < 12 ) {
glRefConfig.glslMaxAnimatedBones = 0;
Expand Down
192 changes: 96 additions & 96 deletions code/sdl/sdl_glimp.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ cvar_t *r_allowSoftwareGL; // Don't abort out if a hardware visual can't be obta
cvar_t *r_allowResize; // make window resizable
cvar_t *r_centerWindow;
cvar_t *r_sdlDriver;
cvar_t *r_useOpenGLES;
cvar_t *r_preferOpenGLES;

int qglMajorVersion, qglMinorVersion;
int qglesMajorVersion, qglesMinorVersion;
Expand Down Expand Up @@ -394,6 +394,12 @@ GLimp_SetMode
*/
static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder, qboolean fixedFunction)
{
struct GLimp_ContextType {
int profileMask;
int majorVersion;
int minorVersion;
} contexts[3];
int numContexts, type;
const char *glstring;
int perChannelColorBits;
int colorBits, depthBits, stencilBits;
Expand Down Expand Up @@ -524,6 +530,48 @@ static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder, qbool
stencilBits = r_stencilbits->value;
samples = r_ext_multisample->value;

numContexts = 0;

if ( !fixedFunction ) {
int profileMask;
qboolean preferOpenGLES;

SDL_GL_ResetAttributes();
SDL_GL_GetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, &profileMask );

preferOpenGLES = ( r_preferOpenGLES->integer == 1 ||
( r_preferOpenGLES->integer == -1 && profileMask == SDL_GL_CONTEXT_PROFILE_ES ) );

if ( preferOpenGLES ) {
contexts[numContexts].profileMask = SDL_GL_CONTEXT_PROFILE_ES;
contexts[numContexts].majorVersion = 2;
contexts[numContexts].minorVersion = 0;
numContexts++;
}

contexts[numContexts].profileMask = SDL_GL_CONTEXT_PROFILE_CORE;
contexts[numContexts].majorVersion = 3;
contexts[numContexts].minorVersion = 2;
numContexts++;

contexts[numContexts].profileMask = 0;
contexts[numContexts].majorVersion = 2;
contexts[numContexts].minorVersion = 0;
numContexts++;

if ( !preferOpenGLES ) {
contexts[numContexts].profileMask = SDL_GL_CONTEXT_PROFILE_ES;
contexts[numContexts].majorVersion = 2;
contexts[numContexts].minorVersion = 0;
numContexts++;
}
} else {
contexts[numContexts].profileMask = 0;
contexts[numContexts].majorVersion = 1;
contexts[numContexts].minorVersion = 1;
numContexts++;
}

for (i = 0; i < 16; i++)
{
int testColorBits, testDepthBits, testStencilBits;
Expand Down Expand Up @@ -656,116 +704,68 @@ static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder, qbool

SDL_SetWindowIcon( SDL_window, icon );

if (!fixedFunction)
{
int profileMask;

SDL_GL_GetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, &profileMask );

if ( r_useOpenGLES->integer == 1 || ( r_useOpenGLES->integer == -1 && profileMask == SDL_GL_CONTEXT_PROFILE_ES ) )
{
ri.Printf( PRINT_ALL, "Trying to get an OpenGL ES 2.0 context\n" );
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 );
for ( type = 0; type < numContexts; type++ ) {
char contextName[32];

SDL_glContext = SDL_GL_CreateContext( SDL_window );
if ( !SDL_glContext )
{
ri.Printf( PRINT_ALL, "SDL_GL_CreateContext failed: %s\n", SDL_GetError() );
}
else
{
ri.Printf( PRINT_ALL, "SDL_GL_CreateContext succeeded.\n" );

if ( !GLimp_GetProcAddresses( fixedFunction ) )
{
ri.Printf( PRINT_ALL, "GLimp_GetProcAddresses() failed for OpenGL ES 2.0 context\n" );
GLimp_ClearProcAddresses();
SDL_GL_DeleteContext( SDL_glContext );
SDL_glContext = NULL;
}
}
switch ( contexts[type].profileMask ) {
default:
case 0:
Com_sprintf( contextName, sizeof( contextName ), "OpenGL %d.%d",
contexts[type].majorVersion, contexts[type].minorVersion );
break;
case SDL_GL_CONTEXT_PROFILE_CORE:
Com_sprintf( contextName, sizeof( contextName ), "OpenGL %d.%d Core",
contexts[type].majorVersion, contexts[type].minorVersion );
break;
case SDL_GL_CONTEXT_PROFILE_ES:
Com_sprintf( contextName, sizeof( contextName ), "OpenGL ES %d.%d",
contexts[type].majorVersion, contexts[type].minorVersion );
break;
}

if ( !SDL_glContext )
{
ri.Printf( PRINT_ALL, "Trying to get an OpenGL 3.2 core context\n" );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 2 );

SDL_glContext = SDL_GL_CreateContext( SDL_window );
if ( !SDL_glContext )
{
ri.Printf( PRINT_ALL, "SDL_GL_CreateContext failed: %s\n", SDL_GetError() );
}
else
{
const char *renderer;

ri.Printf( PRINT_ALL, "SDL_GL_CreateContext succeeded.\n" );

if ( GLimp_GetProcAddresses( fixedFunction ) )
{
renderer = (const char *)qglGetString( GL_RENDERER );
}
else
{
ri.Printf( PRINT_ALL, "GLimp_GetProcAddresses() failed for OpenGL 3.2 core context\n" );
renderer = NULL;
}

if ( !renderer || strstr( renderer, "Software Renderer" ) || strstr( renderer, "Software Rasterizer" ) )
{
if ( renderer )
ri.Printf(PRINT_ALL, "GL_RENDERER is %s, rejecting context\n", renderer);

GLimp_ClearProcAddresses();
SDL_GL_DeleteContext( SDL_glContext );
SDL_glContext = NULL;
}
}
}
SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, contexts[type].profileMask );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, contexts[type].majorVersion );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, contexts[type].minorVersion );

SDL_glContext = SDL_GL_CreateContext( SDL_window );
if ( !SDL_glContext )
{
ri.Printf( PRINT_ALL, "Trying to get an OpenGL 2.0 context\n" );

SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, 0 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 2 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 0 );
}
}
else
{
SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, 0 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 1 );
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 1 );

SDL_glContext = NULL;
}

if ( !SDL_glContext )
{
if( ( SDL_glContext = SDL_GL_CreateContext( SDL_window ) ) == NULL )
{
ri.Printf( PRINT_DEVELOPER, "SDL_GL_CreateContext failed: %s\n", SDL_GetError( ) );
SDL_DestroyWindow( SDL_window );
SDL_window = NULL;
ri.Printf( PRINT_ALL, "SDL_GL_CreateContext() for %s context failed: %s\n", contextName, SDL_GetError() );
continue;
}

if ( !GLimp_GetProcAddresses( fixedFunction ) )
{
ri.Printf( PRINT_ALL, "GLimp_GetProcAddresses() failed\n" );
ri.Printf( PRINT_ALL, "GLimp_GetProcAddresses() for %s context failed\n", contextName );
GLimp_ClearProcAddresses();
SDL_GL_DeleteContext( SDL_glContext );
SDL_glContext = NULL;
SDL_DestroyWindow( SDL_window );
SDL_window = NULL;
continue;
}

if ( contexts[type].profileMask == SDL_GL_CONTEXT_PROFILE_CORE ) {
const char *renderer;

renderer = (const char *)qglGetString( GL_RENDERER );

if ( !renderer || strstr( renderer, "Software Renderer" ) || strstr( renderer, "Software Rasterizer" ) )
{
ri.Printf( PRINT_ALL, "GL_RENDERER is %s, rejecting %s context\n", renderer, contextName );

GLimp_ClearProcAddresses();
SDL_GL_DeleteContext( SDL_glContext );
SDL_glContext = NULL;
continue;
}
}

break;
}

if ( !SDL_glContext ) {
SDL_DestroyWindow( SDL_window );
SDL_window = NULL;
continue;
}

qglClearColor( 0, 0, 0, 1 );
Expand Down Expand Up @@ -1055,7 +1055,7 @@ void GLimp_Init( qboolean fixedFunction )
r_sdlDriver = ri.Cvar_Get( "r_sdlDriver", "", CVAR_ROM );
r_allowResize = ri.Cvar_Get( "r_allowResize", "0", CVAR_ARCHIVE | CVAR_LATCH );
r_centerWindow = ri.Cvar_Get( "r_centerWindow", "0", CVAR_ARCHIVE | CVAR_LATCH );
r_useOpenGLES = ri.Cvar_Get( "r_useOpenGLES", "-1", CVAR_ARCHIVE | CVAR_LATCH );
r_preferOpenGLES = ri.Cvar_Get( "r_preferOpenGLES", "-1", CVAR_ARCHIVE | CVAR_LATCH );

if( ri.Cvar_VariableIntegerValue( "com_abnormalExit" ) )
{
Expand Down
8 changes: 4 additions & 4 deletions opengl2-readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ For Win32:

Cvars for API:

* `r_useOpenGLES` - This enables using OpenGL ES 2+.
Many features are not supported such as sun shadows and HDR.
1 - Use OpenGL ES.
0 - Use desktop OpenGL.
* `r_preferOpenGLES` - This sets the preference for using OpenGL or OpenGL ES 2.
Many features are not supported when using OpenGL ES such as sun shadows and HDR.
1 - Prefer OpenGL ES 2+.
0 - Prefer desktop OpenGL.
-1 - Automatically pick (default).

Cvars for simple rendering features:
Expand Down