OpenGL2: Add OpenGL ES 2.0+ support

This mainly targets OpenGL ES 2.0 but it also supports compiling GLSL as
ESSL 3.00. It's missing support for framebuffer objects which should be
possible on ES 2. (Though using renderbuffers instead of textures.)

opengl1 cvars that are not supported will display a message and disable
the cvar. This has not been reviewed for new opengl2 cvars. Enabling
cvars may cause rendering issues. Some of the broken cvars may be
possible to support using OpenGL ES 3 features.

The game displays okay with the default cvars.
This commit is contained in:
Zack Middleton committed 2024-06-05 21:33:08 -05:00
1 parent 1fc83e4845
commit 3b984d2b51
14 files changed
+658 -167

No files matched your search

+175 -13
View File
@@ -1455,6 +1455,106 @@ byte mipBlendColors[16][4] = {
{0,0,255,128},
};
/*
==================
R_ConvertTextureFormat
Convert RGBA unsigned byte to specified format and type
==================
*/
#define ROW_PADDING( width, bpp, alignment ) PAD( (width) * (bpp), (alignment) ) - (width) * (bpp)
void R_ConvertTextureFormat( const byte *in, int width, int height, GLenum format, GLenum type, byte *out )
{
int x, y, rowPadding;
int unpackAlign = 4; // matches GL_UNPACK_ALIGNMENT default
if ( format == GL_RGB && type == GL_UNSIGNED_BYTE )
{
rowPadding = ROW_PADDING( width, 3, unpackAlign );
for ( y = 0; y < height; y++ )
{
for ( x = 0; x < width; x++ )
{
*out++ = *in++;
*out++ = *in++;
*out++ = *in++;
in++;
}
out += rowPadding;
}
}
else if ( format == GL_LUMINANCE && type == GL_UNSIGNED_BYTE )
{
rowPadding = ROW_PADDING( width, 1, unpackAlign );
for ( y = 0; y < height; y++ )
{
for ( x = 0; x < width; x++ )
{
*out++ = *in++; // red
in += 3;
}
out += rowPadding;
}
}
else if ( format == GL_LUMINANCE_ALPHA && type == GL_UNSIGNED_BYTE )
{
rowPadding = ROW_PADDING( width, 2, unpackAlign );
for ( y = 0; y < height; y++ )
{
for ( x = 0; x < width; x++ )
{
*out++ = *in++; // red
in += 2;
*out++ = *in++; // alpha
}
out += rowPadding;
}
}
else if ( format == GL_RGB && type == GL_UNSIGNED_SHORT_5_6_5 )
{
rowPadding = ROW_PADDING( width, 2, unpackAlign );
for ( y = 0; y < height; y++ )
{
for ( x = 0; x < width; x++, in += 4, out += 2 )
{
*((unsigned short*)out) = ( (unsigned short)( in[0] >> 3 ) << 11 )
| ( (unsigned short)( in[1] >> 2 ) << 5 )
| ( (unsigned short)( in[2] >> 3 ) << 0 );
}
out += rowPadding;
}
}
else if ( format == GL_RGBA && type == GL_UNSIGNED_SHORT_4_4_4_4 )
{
rowPadding = ROW_PADDING( width, 2, unpackAlign );
for ( y = 0; y < height; y++ )
{
for ( x = 0; x < width; x++, in += 4, out += 2 )
{
*((unsigned short*)out) = ( (unsigned short)( in[0] >> 4 ) << 12 )
| ( (unsigned short)( in[1] >> 4 ) << 8 )
| ( (unsigned short)( in[2] >> 4 ) << 4 )
| ( (unsigned short)( in[3] >> 4 ) << 0 );
}
out += rowPadding;
}
}
else
{
ri.Error( ERR_DROP, "Unable to convert RGBA image to OpenGL format 0x%X and type 0x%X", format, type );
}
}
static void RawImage_SwizzleRA( byte *data, int width, int height )
{
int i;
@@ -1944,18 +2044,20 @@ static GLenum PixelDataFormatFromInternalFormat(GLenum internalFormat)
}
}
static void RawImage_UploadTexture(GLuint texture, byte *data, int x, int y, int width, int height, GLenum target, GLenum picFormat, int numMips, GLenum internalFormat, imgType_t type, imgFlags_t flags, qboolean subtexture )
static void RawImage_UploadTexture(GLuint texture, byte *data, int x, int y, int width, int height, GLenum target, GLenum picFormat, GLenum dataFormat, GLenum dataType, int numMips, GLenum internalFormat, imgType_t type, imgFlags_t flags, qboolean subtexture )
{
GLenum dataFormat, dataType;
qboolean rgtc = internalFormat == GL_COMPRESSED_RG_RGTC2;
qboolean rgba8 = picFormat == GL_RGBA8 || picFormat == GL_SRGB8_ALPHA8_EXT;
qboolean rgba = rgba8 || picFormat == GL_RGBA16;
qboolean mipmap = !!(flags & IMGFLAG_MIPMAP);
int size, miplevel;
qboolean lastMip = qfalse;
byte *formatBuffer = NULL;
dataFormat = PixelDataFormatFromInternalFormat(internalFormat);
dataType = picFormat == GL_RGBA16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE;
if (qglesMajorVersion && rgba8 && (dataFormat != GL_RGBA || dataType != GL_UNSIGNED_BYTE))
{
formatBuffer = ri.Hunk_AllocateTempMemory(4 * width * height);
}
miplevel = 0;
do
@@ -1974,6 +2076,11 @@ static void RawImage_UploadTexture(GLuint texture, byte *data, int x, int y, int
if (rgba8 && rgtc)
RawImage_UploadToRgtc2Texture(texture, miplevel, x, y, width, height, data);
else if (formatBuffer)
{
R_ConvertTextureFormat(data, width, height, dataFormat, dataType, formatBuffer);
qglTextureSubImage2DEXT(texture, target, miplevel, x, y, width, height, dataFormat, dataType, formatBuffer);
}
else
qglTextureSubImage2DEXT(texture, target, miplevel, x, y, width, height, dataFormat, dataType, data);
}
@@ -2007,6 +2114,9 @@ static void RawImage_UploadTexture(GLuint texture, byte *data, int x, int y, int
}
}
while (!lastMip);
if (formatBuffer != NULL)
ri.Hunk_FreeTempMemory(formatBuffer);
}
@@ -2016,7 +2126,7 @@ Upload32
===============
*/
static void Upload32(byte *data, int x, int y, int width, int height, GLenum picFormat, int numMips, image_t *image, qboolean scaled)
static void Upload32(byte *data, int x, int y, int width, int height, GLenum picFormat, GLenum dataFormat, GLenum dataType, int numMips, image_t *image, qboolean scaled)
{
int i, c;
byte *scan;
@@ -2071,7 +2181,7 @@ static void Upload32(byte *data, int x, int y, int width, int height, GLenum pic
for (i = 0; i < 6; i++)
{
int w2 = width, h2 = height;
RawImage_UploadTexture(image->texnum, data, x, y, width, height, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, picFormat, numMips, internalFormat, type, flags, qfalse);
RawImage_UploadTexture(image->texnum, data, x, y, width, height, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, picFormat, dataFormat, dataType, numMips, internalFormat, type, flags, qfalse);
for (c = numMips; c; c--)
{
data += CalculateMipSize(w2, h2, picFormat);
@@ -2082,7 +2192,7 @@ static void Upload32(byte *data, int x, int y, int width, int height, GLenum pic
}
else
{
RawImage_UploadTexture(image->texnum, data, x, y, width, height, GL_TEXTURE_2D, picFormat, numMips, internalFormat, type, flags, qfalse);
RawImage_UploadTexture(image->texnum, data, x, y, width, height, GL_TEXTURE_2D, picFormat, dataFormat, dataType, numMips, internalFormat, type, flags, qfalse);
}
GL_CheckErrors();
@@ -2108,7 +2218,7 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
qboolean picmip = !!(flags & IMGFLAG_PICMIP);
qboolean lastMip;
GLenum textureTarget = cubemap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
GLenum dataFormat;
GLenum dataFormat, dataType;
if (strlen(name) >= MAX_QPATH ) {
ri.Error (ERR_DROP, "R_CreateImage: \"%s\" is too long", name);
@@ -2140,6 +2250,53 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
if (!internalFormat)
internalFormat = RawImage_GetFormat(pic, width * height, picFormat, isLightmap, image->type, image->flags);
dataFormat = PixelDataFormatFromInternalFormat(internalFormat);
dataType = picFormat == GL_RGBA16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE;
// Convert image data format for OpenGL ES, data is converted for each mip level
if (qglesMajorVersion)
{
switch (internalFormat)
{
case GL_LUMINANCE:
case GL_LUMINANCE8:
internalFormat = GL_LUMINANCE;
dataFormat = GL_LUMINANCE;
dataType = GL_UNSIGNED_BYTE;
break;
case GL_LUMINANCE_ALPHA:
case GL_LUMINANCE8_ALPHA8:
internalFormat = GL_LUMINANCE_ALPHA;
dataFormat = GL_LUMINANCE_ALPHA;
dataType = GL_UNSIGNED_BYTE;
break;
case GL_RGB:
case GL_RGB8:
internalFormat = GL_RGB;
dataFormat = GL_RGB;
dataType = GL_UNSIGNED_BYTE;
break;
case GL_RGB5:
internalFormat = GL_RGB;
dataFormat = GL_RGB;
dataType = GL_UNSIGNED_SHORT_5_6_5;
break;
case GL_RGBA:
case GL_RGBA8:
internalFormat = GL_RGBA;
dataFormat = GL_RGBA;
dataType = GL_UNSIGNED_BYTE;
break;
case GL_RGBA4:
internalFormat = GL_RGBA;
dataFormat = GL_RGBA;
dataType = GL_UNSIGNED_SHORT_4_4_4_4;
break;
default:
ri.Error( ERR_DROP, "Missing OpenGL ES support for image '%s' with internal format 0x%X\n", name, internalFormat );
}
}
image->internalFormat = internalFormat;
// Possibly scale image before uploading.
@@ -2164,7 +2321,6 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
image->uploadHeight = height;
// Allocate texture storage so we don't have to worry about it later.
dataFormat = PixelDataFormatFromInternalFormat(internalFormat);
mipWidth = width;
mipHeight = height;
miplevel = 0;
@@ -2176,11 +2332,11 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
int i;
for (i = 0; i < 6; i++)
qglTextureImage2DEXT(image->texnum, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, miplevel, internalFormat, mipWidth, mipHeight, 0, dataFormat, GL_UNSIGNED_BYTE, NULL);
qglTextureImage2DEXT(image->texnum, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, miplevel, internalFormat, mipWidth, mipHeight, 0, dataFormat, dataType, NULL);
}
else
{
qglTextureImage2DEXT(image->texnum, GL_TEXTURE_2D, miplevel, internalFormat, mipWidth, mipHeight, 0, dataFormat, GL_UNSIGNED_BYTE, NULL);
qglTextureImage2DEXT(image->texnum, GL_TEXTURE_2D, miplevel, internalFormat, mipWidth, mipHeight, 0, dataFormat, dataType, NULL);
}
mipWidth = MAX(1, mipWidth >> 1);
@@ -2191,7 +2347,7 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
// Upload data.
if (pic)
Upload32(pic, 0, 0, width, height, picFormat, numMips, image, scaled);
Upload32(pic, 0, 0, width, height, picFormat, dataFormat, dataType, numMips, image, scaled);
if (resampledBuffer != NULL)
ri.Hunk_FreeTempMemory(resampledBuffer);
@@ -2252,7 +2408,13 @@ image_t *R_CreateImage(const char *name, byte *pic, int width, int height, imgTy
void R_UpdateSubImage( image_t *image, byte *pic, int x, int y, int width, int height, GLenum picFormat )
{
Upload32(pic, x, y, width, height, picFormat, 0, image, qfalse);
GLenum dataFormat, dataType;
// TODO: This is fine for lightmaps but (unused) general RGBA images need to store dataFormat / dataType in image_t for OpenGL ES?
dataFormat = PixelDataFormatFromInternalFormat(image->internalFormat);
dataType = picFormat == GL_RGBA16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE;
Upload32(pic, x, y, width, height, picFormat, dataFormat, dataType, 0, image, qfalse);
}
//===================================================================