From 5c190c109513efb2ee8272f09e5fc2954b5c0b97 Mon Sep 17 00:00:00 2001 From: Luke Benstead Date: Thu, 9 Aug 2018 08:56:43 +0100 Subject: [PATCH] Implement glGenerateMipmapEXT --- GL/framebuffer.c | 165 +++++++++++++++++++++++++++++++++++++++++- GL/private.h | 2 + GL/texture.c | 19 ++--- samples/nehe06/main.c | 2 + 4 files changed, 178 insertions(+), 10 deletions(-) diff --git a/GL/framebuffer.c b/GL/framebuffer.c index 9faac02..e52600f 100644 --- a/GL/framebuffer.c +++ b/GL/framebuffer.c @@ -86,8 +86,171 @@ void APIENTRY glFramebufferTexture2DEXT(GLenum target, GLenum attachment, GLenum ACTIVE_FRAMEBUFFER->texture_id = texture; } -void APIENTRY glGenerateMipmapEXT(GLenum target) { +static inline GLuint A1555(GLuint v) { + const GLuint MASK = (1 << 15); + return (v & MASK) >> 15; +} +static inline GLuint R1555(GLuint v) { + const GLuint MASK = (31 << 10); + return (v & MASK) >> 10; +} + +static inline GLuint G1555(GLuint v) { + const GLuint MASK = (31 << 5); + return (v & MASK) >> 5; +} + +static inline GLuint B1555(GLuint v) { + const GLuint MASK = (31 << 0); + return (v & MASK) >> 0; +} + +static inline GLuint A4444(GLuint v) { + const GLuint MASK = (0xF << 12); + return (v & MASK) >> 12; +} + +static inline GLuint R4444(GLuint v) { + const GLuint MASK = (0xF << 8); + return (v & MASK) >> 8; +} + +static inline GLuint G4444(GLuint v) { + const GLuint MASK = (0xF << 4); + return (v & MASK) >> 4; +} + +static inline GLuint B4444(GLuint v) { + const GLuint MASK = (0xF << 0); + return (v & MASK) >> 0; +} + +static inline GLuint R565(GLuint v) { + const GLuint MASK = (31 << 11); + return (v & MASK) >> 11; +} + +static inline GLuint G565(GLuint v) { + const GLuint MASK = (63 << 5); + return (v & MASK) >> 5; +} + +static inline GLuint B565(GLuint v) { + const GLuint MASK = (31 << 0); + return (v & MASK) >> 0; +} + +GLboolean _glCalculateAverageTexel(const GLubyte* src, const GLuint srcWidth, const GLuint pvrFormat, GLubyte* dest) { + GLushort* s1 = ((GLushort*) src); + GLushort* s2 = ((GLushort*) src) + 1; + GLushort* s3 = ((GLushort*) src) + srcWidth; + GLushort* s4 = ((GLushort*) src) + srcWidth + 1; + GLushort* d1 = ((GLushort*) dest); + + GLuint a, r, g, b; + + if((pvrFormat & PVR_TXRFMT_ARGB1555) == PVR_TXRFMT_ARGB1555) { + a = A1555(*s1) + A1555(*s2) + A1555(*s3) + A1555(*s4); + r = R1555(*s1) + R1555(*s2) + R1555(*s3) + R1555(*s4); + g = G1555(*s1) + R1555(*s2) + R1555(*s3) + R1555(*s4); + b = B1555(*s1) + R1555(*s2) + R1555(*s3) + R1555(*s4); + + a /= 4; + r /= 4; + g /= 4; + b /= 4; + + *d1 = (a << 15 | r << 10 | g << 5 | b); + } else if((pvrFormat & PVR_TXRFMT_ARGB4444) == PVR_TXRFMT_ARGB4444) { + a = A4444(*s1) + A4444(*s2) + A4444(*s3) + A4444(*s4); + r = R4444(*s1) + R4444(*s2) + R4444(*s3) + R4444(*s4); + g = G4444(*s1) + R4444(*s2) + R4444(*s3) + R4444(*s4); + b = B4444(*s1) + R4444(*s2) + R4444(*s3) + R4444(*s4); + + a /= 4; + r /= 4; + g /= 4; + b /= 4; + + *d1 = (a << 12 | r << 8 | g << 4 | b); + } else if((pvrFormat & PVR_TXRFMT_RGB565) == PVR_TXRFMT_ARGB4444) { + r = R565(*s1) + R565(*s2) + R565(*s3) + R565(*s4); + g = G565(*s1) + R565(*s2) + R565(*s3) + R565(*s4); + b = B565(*s1) + R565(*s2) + R565(*s3) + R565(*s4); + + r /= 4; + g /= 4; + b /= 4; + + *d1 = (r << 11 | g << 5 | b); + } else { + fprintf(stderr, "ERROR: Unsupported PVR format for mipmap generation"); + _glKosThrowError(GL_INVALID_OPERATION, "glGenerateMipmapEXT"); + _glKosPrintError(); + return GL_FALSE; + } + + return GL_TRUE; +} + +void APIENTRY glGenerateMipmapEXT(GLenum target) { + if(target != GL_TEXTURE_2D) { + _glKosThrowError(GL_INVALID_OPERATION, __func__); + _glKosPrintError(); + return; + } + + TextureObject* tex = getBoundTexture(); + + if(!tex || !tex->data || !tex->mipmapCount) { + _glKosThrowError(GL_INVALID_OPERATION, __func__); + _glKosPrintError(); + return; + } + + if(_glIsMipmapComplete(tex)) { + /* Nothing to do */ + return; + } + + GLuint i = 1; + GLuint sx, sy, dx, dy; + GLuint prevWidth = tex->width; + GLuint prevHeight = tex->height; + + for(; i < _glGetMipmapLevelCount(tex); ++i) { + GLubyte* prevData = _glGetMipmapLocation(tex, i - 1); + GLubyte* thisData = _glGetMipmapLocation(tex, i); + + GLuint thisWidth = (prevWidth > 1) ? prevWidth / 2 : 1; + GLuint thisHeight = (prevHeight > 1) ? prevHeight / 2 : 1; + + /* Do the minification */ + for(sx = 0, dx = 0; sx < prevWidth; sx += 2, dx += 1) { + for(sy = 0, sy = 0; sy < prevHeight; sy += 2, dy += 1) { + GLubyte* srcTexel = &prevData[ + ((sy * prevWidth) + sx) * tex->dataStride + ]; + + GLubyte* destTexel = &thisData[ + ((dy * thisWidth) + dx) * tex->dataStride + ]; + + if(!_glCalculateAverageTexel( + srcTexel, + prevWidth, + tex->color, + destTexel + )) { + return; + } + } + } + + prevWidth = thisWidth; + prevHeight = thisHeight; + } } GLenum APIENTRY glCheckFramebufferStatusEXT(GLenum target) { diff --git a/GL/private.h b/GL/private.h index aae32ee..e75220d 100644 --- a/GL/private.h +++ b/GL/private.h @@ -92,6 +92,8 @@ TextureObject* getTexture1(); TextureObject* getBoundTexture(); GLboolean isBlendingEnabled(); GLboolean _glIsMipmapComplete(TextureObject* obj); +GLubyte* _glGetMipmapLocation(TextureObject* obj, GLuint level); +GLuint _glGetMipmapLevelCount(TextureObject* obj); GLboolean isLightingEnabled(); GLboolean isLightEnabled(GLubyte light); diff --git a/GL/texture.c b/GL/texture.c index 2dd04df..c5832f9 100644 --- a/GL/texture.c +++ b/GL/texture.c @@ -605,12 +605,6 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, } } - /* Set the required mipmap count */ - active->mipmapCount = _glGetMipmapLevelCount(active); - - /* Mark this level as set in the mipmap bitmask */ - active->mipmap |= (1 << level); - GLuint bytes = (width * height * sizeof(GLushort)); if(!active->data) { @@ -618,10 +612,15 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, active->width = width; active->height = height; active->color = pvr_format; + /* Set the required mipmap count */ + active->mipmapCount = _glGetMipmapLevelCount(active); active->data = pvr_mem_malloc(_glGetMipmapDataSize(active)); - active->dataStride = _determineStride(format, type); + active->dataStride = sizeof(GLshort); } + /* Mark this level as set in the mipmap bitmask */ + active->mipmap |= (1 << level); + /* Let's assume we need to convert */ GLboolean needsConversion = GL_TRUE; @@ -667,7 +666,9 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, GLushort* dest = (GLushort*) targetData; const GLubyte* source = data; - if(active->dataStride == -1) { + GLint stride = _determineStride(format, type); + + if(stride == -1) { _glKosThrowError(GL_INVALID_OPERATION, __func__); return; } @@ -678,7 +679,7 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, convert(source, dest); dest++; - source += active->dataStride; + source += stride; } } } diff --git a/samples/nehe06/main.c b/samples/nehe06/main.c index ce9a502..32ff339 100644 --- a/samples/nehe06/main.c +++ b/samples/nehe06/main.c @@ -129,6 +129,8 @@ void LoadGLTextures() { // 2d texture, level of detail 0 (normal), 3 components (red, green, blue), x size from image, y size from image, // border 0 (normal), rgb color data, unsigned byte data, and finally the data itself. glTexImage2D(GL_TEXTURE_2D, 0, 3, image1->sizeX, image1->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, image1->data); + + glGenerateMipmapEXT(GL_TEXTURE_2D); }; /* A general OpenGL initialization function. Sets all of the initial parameters. */