diff --git a/GL/framebuffer.c b/GL/framebuffer.c index 663a9c7..fa38f61 100644 --- a/GL/framebuffer.c +++ b/GL/framebuffer.c @@ -1,4 +1,6 @@ #include +#include + #include "private.h" #include "../include/glkos.h" #include "../include/glext.h" @@ -89,109 +91,164 @@ void APIENTRY glFramebufferTexture2DEXT(GLenum target, GLenum attachment, GLenum ACTIVE_FRAMEBUFFER->texture_id = texture; } -static inline GLuint A1555(GLuint v) { +static inline GLubyte A1555(GLushort v) { const GLuint MASK = (1 << 15); - return (v & MASK) >> 15; + return (v & MASK) >> 8; } -static inline GLuint R1555(GLuint v) { +static inline GLubyte R1555(GLushort v) { const GLuint MASK = (31 << 10); - return (v & MASK) >> 10; + return (v & MASK) >> 7; } -static inline GLuint G1555(GLuint v) { +static inline GLubyte G1555(GLushort v) { const GLuint MASK = (31 << 5); - return (v & MASK) >> 5; + return (v & MASK) >> 2; } -static inline GLuint B1555(GLuint v) { +static inline GLubyte B1555(GLushort v) { const GLuint MASK = (31 << 0); - return (v & MASK) >> 0; + return (v & MASK) << 3; } -static inline GLuint A4444(GLuint v) { +static inline GLubyte A4444(GLushort v) { const GLuint MASK = (0xF << 12); return (v & MASK) >> 12; } -static inline GLuint R4444(GLuint v) { +static inline GLubyte R4444(GLushort v) { const GLuint MASK = (0xF << 8); return (v & MASK) >> 8; } -static inline GLuint G4444(GLuint v) { +static inline GLubyte G4444(GLushort v) { const GLuint MASK = (0xF << 4); return (v & MASK) >> 4; } -static inline GLuint B4444(GLuint v) { +static inline GLubyte B4444(GLushort v) { const GLuint MASK = (0xF << 0); return (v & MASK) >> 0; } -static inline GLuint R565(GLuint v) { +static inline GLubyte R565(GLushort v) { const GLuint MASK = (31 << 11); - return (v & MASK) >> 11; + return (v & MASK) >> 8; } -static inline GLuint G565(GLuint v) { +static inline GLubyte G565(GLushort v) { const GLuint MASK = (63 << 5); - return (v & MASK) >> 5; + return (v & MASK) >> 3; } -static inline GLuint B565(GLuint v) { +static inline GLubyte B565(GLushort v) { const GLuint MASK = (31 << 0); - return (v & MASK) >> 0; + return (v & MASK) << 3; } -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); - +GLboolean _glCalculateAverageTexel(GLuint pvrFormat, const GLubyte* src1, const GLubyte* src2, const GLubyte* src3, const GLubyte* src4, GLubyte* t) { 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); + GLubyte format = ((pvrFormat & (1 << 27)) | (pvrFormat & (1 << 26))) >> 26; + + const GLubyte ARGB1555 = 0; + const GLubyte ARGB4444 = 1; + const GLubyte RGB565 = 2; + + if((pvrFormat & PVR_TXRFMT_PAL8BPP) == PVR_TXRFMT_PAL8BPP) { + /* Paletted... all we can do really is just pick one of the + * 4 texels.. unless we want to change the palette (bad) or + * pick the closest available colour (slow, and probably bad) + */ + *t = *src1; + } else if(format == RGB565) { + GLushort* s1 = (GLushort*) src1; + GLushort* s2 = (GLushort*) src2; + GLushort* s3 = (GLushort*) src3; + GLushort* s4 = (GLushort*) src4; + GLushort* d1 = (GLushort*) t; + + r = R565(*s1) + R565(*s2) + R565(*s3) + R565(*s4); + g = G565(*s1) + G565(*s2) + G565(*s3) + G565(*s4); + b = B565(*s1) + B565(*s2) + B565(*s3) + B565(*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) { + *d1 = PACK_RGB565(r, g, b); + } else if(format == ARGB4444) { + GLushort* s1 = (GLushort*) src1; + GLushort* s2 = (GLushort*) src2; + GLushort* s3 = (GLushort*) src3; + GLushort* s4 = (GLushort*) src4; + GLushort* d1 = (GLushort*) t; + 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); + g = G4444(*s1) + G4444(*s2) + G4444(*s3) + G4444(*s4); + b = B4444(*s1) + B4444(*s2) + B4444(*s3) + B4444(*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); + *d1 = PACK_ARGB4444(a, r, g, b); + } else { + assert(format == ARGB1555); + GLushort* s1 = (GLushort*) src1; + GLushort* s2 = (GLushort*) src2; + GLushort* s3 = (GLushort*) src3; + GLushort* s4 = (GLushort*) src4; + GLushort* d1 = (GLushort*) t; + + a = A1555(*s1) + A1555(*s2) + A1555(*s3) + A1555(*s4); + r = R1555(*s1) + R1555(*s2) + R1555(*s3) + R1555(*s4); + g = G1555(*s1) + G1555(*s2) + G1555(*s3) + G1555(*s4); + b = B1555(*s1) + B1555(*s2) + B1555(*s3) + B1555(*s4); + + a /= 4; r /= 4; g /= 4; b /= 4; - *d1 = (r << 11 | g << 5 | b); + *d1 = PACK_ARGB1555((GLubyte) a, (GLubyte) r, (GLubyte) g, (GLubyte) b); + } + + return GL_TRUE; +} + +GLboolean _glGenerateMipmapTwiddled(const GLuint pvrFormat, const GLubyte* prevData, GLuint thisWidth, GLuint thisHeight, GLubyte* thisData) { + uint32_t lastWidth = thisWidth * 2; + uint32_t lastHeight = thisHeight * 2; + + uint32_t i, j; + uint32_t stride = 0; + + if((pvrFormat & PVR_TXRFMT_PAL8BPP) == PVR_TXRFMT_PAL8BPP) { + stride = 1; } else { - fprintf(stderr, "ERROR: Unsupported PVR format for mipmap generation"); - _glKosThrowError(GL_INVALID_OPERATION, __func__); - _glKosPrintError(); - return GL_FALSE; + stride = 2; + } + + for(i = 0, j = 0; i < lastWidth * lastHeight; i += 4, j++) { + + /* In a twiddled texture, the neighbouring texels + * are next to each other. By averaging them we just basically shrink + * the reverse Ns so each reverse N becomes the next level down... if that makes sense!? */ + + const GLubyte* s1 = &prevData[i * stride]; + const GLubyte* s2 = s1 + stride; + const GLubyte* s3 = s2 + stride; + const GLubyte* s4 = s3 + stride; + GLubyte* t = &thisData[j * stride]; + + assert(s4 < prevData + (lastHeight * lastWidth * stride)); + assert(t < thisData + (thisHeight * thisWidth * stride)); + + _glCalculateAverageTexel(pvrFormat, s1, s2, s3, s4, t); } return GL_TRUE; @@ -219,53 +276,52 @@ void APIENTRY glGenerateMipmapEXT(GLenum target) { return; } - if(_glIsMipmapComplete(tex)) { + if((tex->color & PVR_TXRFMT_NONTWIDDLED) == PVR_TXRFMT_NONTWIDDLED) { + /* glTexImage2D should twiddle internally textures in nearly all cases + * so this error is unlikely */ + + fprintf(stderr, "[GL ERROR] Mipmaps are only supported on twiddled textures\n"); + _glKosThrowError(GL_INVALID_OPERATION, __func__); + _glKosPrintError(); + return; + } + + GLboolean complete = _glIsMipmapComplete(tex); + if(!complete && tex->isCompressed) { + fprintf(stderr, "[GL ERROR] Generating mipmaps for compressed textures is not yet supported\n"); + _glKosThrowError(GL_INVALID_OPERATION, __func__); + _glKosPrintError(); + return; + } + + if(complete) { /* Nothing to do */ return; } - GLuint i = 1; - GLuint sx, sy, dx, dy; + GLuint i; GLuint prevWidth = tex->width; GLuint prevHeight = tex->height; /* Make sure there is room for the mipmap data on the texture object */ _glAllocateSpaceForMipmaps(tex); - for(; i < _glGetMipmapLevelCount(tex); ++i) { + for(i = 1; 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, dy = 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; - } - } - } + _glGenerateMipmapTwiddled(tex->color, prevData, thisWidth, thisHeight, thisData); tex->mipmap |= (1 << i); prevWidth = thisWidth; prevHeight = thisHeight; } + + assert(_glIsMipmapComplete(tex)); } GLenum APIENTRY glCheckFramebufferStatusEXT(GLenum target) { diff --git a/GL/private.h b/GL/private.h index e1ad867..7fb68b8 100644 --- a/GL/private.h +++ b/GL/private.h @@ -12,6 +12,18 @@ #include "../containers/aligned_vector.h" #include "../containers/named_array.h" +#define FASTCPY(dst, src, bytes) \ + (bytes % 32 == 0) ? sq_cpy(dst, src, bytes) : memcpy(dst, src, bytes); + +#define _PACK4(v) ((v * 0xF) / 0xFF) +#define PACK_ARGB4444(a,r,g,b) (_PACK4(a) << 12) | (_PACK4(r) << 8) | (_PACK4(g) << 4) | (_PACK4(b)) +#define PACK_ARGB8888(a,r,g,b) ( ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF) ) +#define PACK_ARGB1555(a,r,g,b) \ + (((GLushort)(a > 0) << 15) | (((GLushort) r >> 3) << 10) | (((GLushort)g >> 3) << 5) | ((GLushort)b >> 3)) + +#define PACK_RGB565(r,g,b) \ + ((((GLushort)r & 0xf8) << 8) | (((GLushort) g & 0xfc) << 3) | ((GLushort) b >> 3)) + #define TRACE_ENABLED 0 #define TRACE() if(TRACE_ENABLED) {fprintf(stderr, "%s\n", __func__);} @@ -105,6 +117,7 @@ typedef struct { GLuint index; GLvoid *data; GLuint dataStride; + GLuint baseDataSize; /* The data size of mipmap level 0 */ GLenum minFilter; GLenum magFilter; @@ -112,6 +125,13 @@ typedef struct { GLboolean isCompressed; GLboolean isPaletted; + /* Mipmap textures have a different + * offset for the base level when supplying the data, this + * keeps track of that. baseDataOffset == 0 + * means that the texture has no mipmaps + */ + GLuint baseDataOffset; + TexturePalette* palette; /* When using the shared palette, this is the bank (0-3) */ diff --git a/GL/state.c b/GL/state.c index f927c54..ef026a5 100644 --- a/GL/state.c +++ b/GL/state.c @@ -208,11 +208,19 @@ void _glUpdatePVRTextureContext(pvr_poly_cxt_t* context, GLshort textureUnit) { if(tx1->data) { context->txr.enable = PVR_TEXTURE_ENABLE; context->txr.filter = filter; - context->txr.mipmap = (enableMipmaps) ? PVR_MIPMAP_ENABLE : PVR_MIPMAP_DISABLE; - context->txr.mipmap_bias = PVR_MIPBIAS_NORMAL; context->txr.width = tx1->width; context->txr.height = tx1->height; - context->txr.base = tx1->data; + + if(enableMipmaps) { + context->txr.base = tx1->data; + context->txr.mipmap = PVR_MIPMAP_ENABLE; + context->txr.mipmap_bias = PVR_MIPBIAS_NORMAL; + } else { + context->txr.base = tx1->data + tx1->baseDataOffset; + context->txr.mipmap = PVR_MIPMAP_DISABLE; + context->txr.mipmap_bias = PVR_MIPBIAS_NORMAL; + } + context->txr.format = tx1->color; if(tx1->isPaletted) { diff --git a/GL/texture.c b/GL/texture.c index f8fe327..e8cdeb9 100644 --- a/GL/texture.c +++ b/GL/texture.c @@ -23,12 +23,6 @@ static TexturePalette* SHARED_PALETTES[4] = {NULL, NULL, NULL, NULL}; static GLuint _determinePVRFormat(GLint internalFormat, GLenum type); -#define PACK_ARGB8888(a,r,g,b) ( ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF) ) - - -#define _PACK4(v) ((v * 0xF) / 0xFF) -#define PACK_ARGB4444(a,r,g,b) (_PACK4(a) << 12) | (_PACK4(r) << 8) | (_PACK4(g) << 4) | (_PACK4(b)) - static GLboolean BANKS_USED[4]; // Each time a 256 colour bank is used, this is set to true static GLboolean SUBBANKS_USED[4][16]; // 4 counts of the used 16 colour banks within the 256 ones static GLenum INTERNAL_PALETTE_FORMAT = GL_RGBA4; @@ -37,7 +31,7 @@ static TexturePalette* _initTexturePalette() { TexturePalette* palette = (TexturePalette*) malloc(sizeof(TexturePalette)); assert(palette); - sq_clr(palette, (sizeof(TexturePalette) & 0xfffffffc) + 4); + memset(palette, 0x0, sizeof(TexturePalette)); palette->bank = -1; return palette; } @@ -161,26 +155,94 @@ static GLint _determineStride(GLenum format, GLenum type) { return -1; } -GLubyte* _glGetMipmapLocation(TextureObject* obj, GLuint level) { +static GLuint _glGetMipmapDataOffset(TextureObject* obj, GLuint level) { GLuint offset = 0; + GLuint size = obj->height; - GLuint i = 0; - GLuint width = obj->width; - GLuint height = obj->height; + if(obj->width != obj->height) { + fprintf(stderr, "ERROR: Accessing memory location of mipmaps on non-square texture\n"); + return obj->baseDataOffset; + } - for(; i < level; ++i) { - offset += (width * height * obj->dataStride); - - if(width > 1) { - width /= 2; + if(obj->isPaletted){ + switch(size >> level){ + case 1024: + offset = 0x55558; + break; + case 512: + offset = 0x15558; + break; + case 256: + offset = 0x05558; + break; + case 128: + offset = 0x01558; + break; + case 64: + offset = 0x00558; + break; + case 32: + offset = 0x00158; + break; + case 16: + offset = 0x00058; + break; + case 8: + offset = 0x00018; + break; + case 4: + offset = 0x00008; + break; + case 2: + offset = 0x00004; + break; + case 1: + offset = 0x00003; + break; } - - if(height > 1) { - height /= 2; + } else { + switch(size >> level) { + case 1024: + offset = 0xAAAB0; + break; + case 512: + offset = 0x2AAB0; + break; + case 256: + offset = 0x0AAB0; + break; + case 128: + offset = 0x02AB0; + break; + case 64: + offset = 0x00AB0; + break; + case 32: + offset = 0x002B0; + break; + case 16: + offset = 0x000B0; + break; + case 8: + offset = 0x00030; + break; + case 4: + offset = 0x00010; + break; + case 2: + offset = 0x00008; + break; + case 1: + offset = 0x00006; + break; } } - return ((GLubyte*) obj->data) + offset; + return offset; +} + +GLubyte* _glGetMipmapLocation(TextureObject* obj, GLuint level) { + return ((GLubyte*) obj->data) + _glGetMipmapDataOffset(obj, level); } GLuint _glGetMipmapLevelCount(TextureObject* obj) { @@ -188,25 +250,13 @@ GLuint _glGetMipmapLevelCount(TextureObject* obj) { } static GLuint _glGetMipmapDataSize(TextureObject* obj) { - GLuint size = 0; + /* The mipmap data size is the offset + the size of the + * image */ - GLuint i = 0; - GLuint width = obj->width; - GLuint height = obj->height; + GLuint imageSize = obj->baseDataSize; + GLuint offset = _glGetMipmapDataOffset(obj, 0); - for(; i < _glGetMipmapLevelCount(obj); ++i) { - size += (width * height * obj->dataStride); - - if(width > 1) { - width /= 2; - } - - if(height > 1) { - height /= 2; - } - } - - return size; + return imageSize + offset; } GLubyte _glInitTextures() { @@ -269,6 +319,9 @@ static void _glInitializeTextureObject(TextureObject* txr, unsigned int id) { txr->isCompressed = GL_FALSE; txr->isPaletted = GL_FALSE; + /* Not mipmapped by default */ + txr->baseDataOffset = 0; + /* Always default to the first shared bank */ txr->shared_bank = 0; } @@ -768,6 +821,12 @@ static GLboolean _isSupportedFormat(GLenum format) { } GLboolean _glIsMipmapComplete(const TextureObject* obj) { + + // Non-square textures can't have mipmaps + if(obj->width != obj->height) { + return GL_FALSE; + } + if(!obj->mipmap || !obj->mipmapCount) { return GL_FALSE; } @@ -782,16 +841,9 @@ GLboolean _glIsMipmapComplete(const TextureObject* obj) { return GL_TRUE; } -#define TWIDTAB(x) ( (x&1)|((x&2)<<1)|((x&4)<<2)|((x&8)<<3)|((x&16)<<4)| \ - ((x&32)<<5)|((x&64)<<6)|((x&128)<<7)|((x&256)<<8)|((x&512)<<9) ) - -#define TWIDOUT(x, y) ( TWIDTAB((y)) | (TWIDTAB((x)) << 1) ) -#define MIN(a, b) ( (a)<(b)? (a):(b) ) - - void _glAllocateSpaceForMipmaps(TextureObject* active) { - if(active->data && active->mipmap > 1) { - /* Already done */ + if(active->data && active->baseDataOffset > 0) { + /* Already done - mipmaps have a dataOffset */ return; } @@ -800,18 +852,27 @@ void _glAllocateSpaceForMipmaps(TextureObject* active) { * then free the original */ - GLubyte* src = active->data; - GLubyte* dest = active->data = pvr_mem_malloc(_glGetMipmapDataSize(active)); + GLuint size = active->baseDataSize; - /* If there was existing data, then copy it across before freeing */ - if(src) { - GLuint i = 0; - for(; i < active->width * active->height * active->dataStride; ++i) { - *dest++ = *src++; - } + /* Copy the data out of the pvr and back to ram */ + GLubyte* temp = (GLubyte*) malloc(size); + memcpy(temp, active->data, size); - pvr_mem_free(src); - } + /* Free the PVR data */ + pvr_mem_free(active->data); + active->data = NULL; + + /* Figure out how much room to allocate for mipmaps */ + GLuint bytes = _glGetMipmapDataSize(active); + + active->data = pvr_mem_malloc(bytes); + + /* If there was existing data, then copy it where it should go */ + memcpy(_glGetMipmapLocation(active, 0), temp, size); + + /* Set the data offset depending on whether or not this is a + * paletted texure */ + active->baseDataOffset = _glGetMipmapDataOffset(active, 0); } void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, @@ -892,7 +953,7 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, assert(active); - if(active->data) { + if(active->data && level == 0) { /* pre-existing texture - check if changed */ if(active->width != width || active->height != height || @@ -903,6 +964,8 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, active->mipmap = 0; active->mipmapCount = 0; active->dataStride = 0; + active->baseDataOffset = 0; + active->baseDataSize = 0; } } @@ -925,25 +988,25 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, /* Set the required mipmap count */ active->mipmapCount = _glGetMipmapLevelCount(active); active->dataStride = destStride; + active->baseDataSize = bytes; - GLuint size = bytes; + assert(bytes); - /* If we're uploading a mipmap level, we need to allocate the full amount of space */ if(level > 0) { - size = _glGetMipmapDataSize(active); + /* If we're uploading a mipmap level, we need to allocate the full amount of space */ + _glAllocateSpaceForMipmaps(active); + } else { + active->data = pvr_mem_malloc(active->baseDataSize); } - assert(size); - active->data = pvr_mem_malloc(size); assert(active->data); - active->isCompressed = GL_FALSE; active->isPaletted = isPaletted; } /* We're supplying a mipmap level, but previously we only had - * data for the first level (level 0, e.g. 1 << 0 == 1) */ - if(level > 0 && active->mipmap == 1) { + * data for the first level (level 0) */ + if(level > 0 && active->baseDataOffset == 0) { _glAllocateSpaceForMipmaps(active); } @@ -952,7 +1015,9 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, /* Let's assume we need to convert */ GLboolean needsConversion = GL_TRUE; - GLboolean needsTwiddling = GL_FALSE; + + /* Let's assume we need twiddling - we always store things twiddled! */ + GLboolean needsTwiddling = GL_TRUE; /* * These are the only formats where the source format passed in matches the pvr format. @@ -961,7 +1026,10 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, if(format == GL_COLOR_INDEX) { /* Don't convert color indexes */ needsConversion = GL_FALSE; - needsTwiddling = type == GL_UNSIGNED_BYTE; + + if(type == GL_UNSIGNED_BYTE_TWID_KOS) { + needsTwiddling = GL_FALSE; + } } else if(format == GL_BGRA && type == GL_UNSIGNED_SHORT_4_4_4_4_REV && internalFormat == GL_RGBA) { needsConversion = GL_FALSE; } else if(format == GL_BGRA && type == GL_UNSIGNED_SHORT_1_5_5_5_REV && internalFormat == GL_RGBA) { @@ -970,45 +1038,32 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, needsConversion = GL_FALSE; } else if(format == GL_RGB && type == GL_UNSIGNED_SHORT_5_6_5_TWID_KOS && internalFormat == GL_RGB) { needsConversion = GL_FALSE; + needsTwiddling = GL_FALSE; } else if(format == GL_BGRA && type == GL_UNSIGNED_SHORT_1_5_5_5_REV_TWID_KOS && internalFormat == GL_RGBA) { needsConversion = GL_FALSE; + needsTwiddling = GL_FALSE; } else if(format == GL_BGRA && type == GL_UNSIGNED_SHORT_4_4_4_4_REV_TWID_KOS && internalFormat == GL_RGBA) { needsConversion = GL_FALSE; + needsTwiddling = GL_FALSE; } - GLubyte* targetData = _glGetMipmapLocation(active, level); + GLubyte* targetData = (active->baseDataOffset == 0) ? active->data : _glGetMipmapLocation(active, level); assert(targetData); + GLubyte* conversionBuffer = NULL; + if(!data) { /* No data? Do nothing! */ return; - } else if(!needsConversion) { + } else if(!needsConversion && !needsTwiddling) { assert(targetData); assert(data); assert(bytes); - if(needsTwiddling) { - assert(type == GL_UNSIGNED_BYTE); // Anything else needs this loop adjusting - GLuint x, y, min, mask; - - GLubyte *pixels = (GLubyte*) data; - GLushort *vtex = (GLushort*) targetData; - - min = MIN(w, h); - mask = min - 1; - - for(y = 0; y < h; y += 2) { - for(x = 0; x < w; x++) { - vtex[TWIDOUT((y & mask) / 2, x & mask) + (x / min + y / min)*min * min / 2] = pixels[y * w + x] | (pixels[(y + 1) * w + x] << 8); - } - } - } else { - /* No conversion? Just copy the data, and the pvr_format is correct */ - sq_cpy(targetData, data, bytes); - } - + /* No conversion? Just copy the data, and the pvr_format is correct */ + FASTCPY(targetData, data, bytes); return; - } else { + } else if(needsConversion) { TextureConversionFunc convert = _determineConversion( internalFormat, format, @@ -1020,12 +1075,6 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, return; } - GLubyte* dest = (GLubyte*) targetData; - const GLubyte* source = data; - - assert(dest); - assert(source); - GLint stride = _determineStride(format, type); assert(stride > -1); @@ -1034,6 +1083,14 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, return; } + conversionBuffer = malloc(bytes); + + GLubyte* dest = conversionBuffer; + const GLubyte* source = data; + + assert(conversionBuffer); + assert(source); + /* Perform the conversion */ GLuint i; for(i = 0; i < bytes; i += destStride) { @@ -1043,6 +1100,33 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, source += stride; } } + + if(needsTwiddling) { + const GLubyte *pixels = (GLubyte*) (conversionBuffer) ? conversionBuffer : data; + + if(internalFormat == GL_COLOR_INDEX8_EXT) { + pvr_txr_load_ex((void*) pixels, targetData, width, height, PVR_TXRLOAD_8BPP); + } else { + pvr_txr_load_ex((void*) pixels, targetData, width, height, PVR_TXRLOAD_16BPP); + } + + /* We make sure we remove nontwiddled and add twiddled. We could always + * make it twiddled when determining the format but I worry that would make the + * code less flexible to change in the future */ + active->color &= ~(1 << 26); + } else { + /* We should only get here if we converted twiddled data... which is never currently */ + assert(conversionBuffer); + + // We've already converted the data and we + // don't need to twiddle it! + FASTCPY(targetData, conversionBuffer, bytes); + } + + if(conversionBuffer) { + free(conversionBuffer); + conversionBuffer = NULL; + } } void APIENTRY glTexParameteri(GLenum target, GLenum pname, GLint param) { diff --git a/samples/Makefile b/samples/Makefile index d8df580..7fc4b0f 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -28,3 +28,4 @@ all: $(KOS_MAKE) -C polymark all $(KOS_MAKE) -C polygon_offset all $(KOS_MAKE) -C blend_test all + $(KOS_MAKE) -C mipmap all diff --git a/samples/mipmap/Makefile b/samples/mipmap/Makefile new file mode 100644 index 0000000..6b36730 --- /dev/null +++ b/samples/mipmap/Makefile @@ -0,0 +1,29 @@ +TARGET = mipmap.elf +OBJS = main.o + +all: rm-elf $(TARGET) + +include $(KOS_BASE)/Makefile.rules + +clean: + -rm -f $(TARGET) $(OBJS) romdisk.* + +rm-elf: + -rm -f $(TARGET) romdisk.* + +$(TARGET): $(OBJS) romdisk.o + $(KOS_CC) $(KOS_CFLAGS) $(KOS_LDFLAGS) -o $(TARGET) $(KOS_START) \ + $(OBJS) romdisk.o $(OBJEXTRA) -lm -lkosutils $(KOS_LIBS) + +romdisk.img: + $(KOS_GENROMFS) -f romdisk.img -d romdisk -v + +romdisk.o: romdisk.img + $(KOS_BASE)/utils/bin2o/bin2o romdisk.img romdisk romdisk.o + +run: $(TARGET) + $(KOS_LOADER) $(TARGET) + +dist: + rm -f $(OBJS) romdisk.o romdisk.img + $(KOS_STRIP) $(TARGET) diff --git a/samples/mipmap/main.c b/samples/mipmap/main.c new file mode 100644 index 0000000..15397d3 --- /dev/null +++ b/samples/mipmap/main.c @@ -0,0 +1,235 @@ +#include +#include + +#include "gl.h" +#include "glu.h" +#include "glkos.h" + +extern uint8 romdisk[]; +KOS_INIT_ROMDISK(romdisk); + +/* storage for one texture */ +int texture[1]; + +/* Image type - contains height, width, and data */ +struct Image { + unsigned long sizeX; + unsigned long sizeY; + char *data; +}; +typedef struct Image Image; + +// quick and dirty bitmap loader...for 24 bit bitmaps with 1 plane only. +// See http://www.dcs.ed.ac.uk/~mxr/gfx/2d/BMP.txt for more info. +int ImageLoad(char *filename, Image *image) { + FILE *file; + unsigned long size; // size of the image in bytes. + unsigned long i; // standard counter. + unsigned short int planes; // number of planes in image (must be 1) + unsigned short int bpp; // number of bits per pixel (must be 24) + char temp; // temporary color storage for bgr-rgb conversion. + + // make sure the file is there. + if ((file = fopen(filename, "rb"))==NULL) + { + printf("File Not Found : %s\n",filename); + return 0; + } + + // seek through the bmp header, up to the width/height: + fseek(file, 18, SEEK_CUR); + + // read the width + if ((i = fread(&image->sizeX, 4, 1, file)) != 1) { + printf("Error reading width from %s.\n", filename); + return 0; + } + printf("Width of %s: %lu\n", filename, image->sizeX); + + // read the height + if ((i = fread(&image->sizeY, 4, 1, file)) != 1) { + printf("Error reading height from %s.\n", filename); + return 0; + } + printf("Height of %s: %lu\n", filename, image->sizeY); + + // calculate the size (assuming 24 bits or 3 bytes per pixel). + size = image->sizeX * image->sizeY * 3; + + // read the planes + if ((fread(&planes, 2, 1, file)) != 1) { + printf("Error reading planes from %s.\n", filename); + return 0; + } + if (planes != 1) { + printf("Planes from %s is not 1: %u\n", filename, planes); + return 0; + } + + // read the bpp + if ((i = fread(&bpp, 2, 1, file)) != 1) { + printf("Error reading bpp from %s.\n", filename); + return 0; + } + if (bpp != 24) { + printf("Bpp from %s is not 24: %u\n", filename, bpp); + return 0; + } + + // seek past the rest of the bitmap header. + fseek(file, 24, SEEK_CUR); + + // read the data. + image->data = (char *) malloc(size); + if (image->data == NULL) { + printf("Error allocating memory for color-corrected image data"); + return 0; + } + + if ((i = fread(image->data, size, 1, file)) != 1) { + printf(stderr, "Error reading image data from %s.\n", filename); + return 0; + } + + for (i=0;i rgb) + temp = image->data[i]; + image->data[i] = image->data[i+2]; + image->data[i+2] = temp; + } + + // we're done. + return 1; +} + +// Load Bitmaps And Convert To Textures +void LoadGLTextures() { + // Load Texture + Image *image1; + + // allocate space for texture + image1 = (Image *) malloc(sizeof(Image)); + if (image1 == NULL) { + printf("Error allocating space for image"); + exit(0); + } + + if (!ImageLoad("/rd/NeHe.bmp", image1)) { + exit(1); + } + + // Create Texture + glGenTextures(1, &texture[0]); + glBindTexture(GL_TEXTURE_2D, texture[0]); // 2d texture (x and y size) + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // scale linearly when image bigger than texture + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR); // scale linearly when image smalled than texture + + // 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. */ +void InitGL(int Width, int Height) // We call this right after our OpenGL window is created. +{ + LoadGLTextures(); + glEnable(GL_TEXTURE_2D); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // This Will Clear The Background Color To Black + glClearDepth(1.0); // Enables Clearing Of The Depth Buffer + glDepthFunc(GL_LESS); // The Type Of Depth Test To Do + glEnable(GL_DEPTH_TEST); // Enables Depth Testing + glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); // Reset The Projection Matrix + + gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f); // Calculate The Aspect Ratio Of The Window + + glMatrixMode(GL_MODELVIEW); +} + +/* The function called when our window is resized (which shouldn't happen, because we're fullscreen) */ +void ReSizeGLScene(int Width, int Height) +{ + if (Height == 0) // Prevent A Divide By Zero If The Window Is Too Small + Height = 1; + + glViewport(0, 0, Width, Height); // Reset The Current Viewport And Perspective Transformation + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f); + glMatrixMode(GL_MODELVIEW); +} + +void DrawQuad() { + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f); // Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 0.0f); // Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 0.0f); // Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f); // Top Left Of The Texture and Quad + glEnd(); // done with the polygon. +} + +static GLboolean mipmap_enabled = GL_FALSE; +static GLuint timer = 0; + +/* The main drawing function. */ +void DrawGLScene() +{ + timer++; + if(timer > 60) { + timer = 0; + mipmap_enabled = !mipmap_enabled; + + if(mipmap_enabled) { + printf("Enabling mipmaps!\n"); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR); + } else { + printf("Disabling mipmaps!\n"); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + } + } + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + glClearColor(0.5, 0.5, 0.5, 1.0); + + glBindTexture(GL_TEXTURE_2D, texture[0]); + + glTranslatef(-1.5f, 0.0f, -4.5f); + DrawQuad(); + + glTranslatef(1.0f, 0.0f, -5.0f); + DrawQuad(); + + glTranslatef(1.5f, 0.0f, -5.0f); + DrawQuad(); + + glTranslatef(2.0f, 0.0f, -5.0f); + DrawQuad(); + + glTranslatef(3.5f, 0.0f, -5.0f); + DrawQuad(); + + glKosSwapBuffers(); +} + +int main(int argc, char **argv) +{ + glKosInit(); + + InitGL(640, 480); + ReSizeGLScene(640, 480); + + while(1) { + DrawGLScene(); + } + + return 0; +} diff --git a/samples/mipmap/romdisk/NeHe.bmp b/samples/mipmap/romdisk/NeHe.bmp new file mode 100644 index 0000000..6b3db10 Binary files /dev/null and b/samples/mipmap/romdisk/NeHe.bmp differ diff --git a/samples/mipmap/romdisk/PLACEHOLDER b/samples/mipmap/romdisk/PLACEHOLDER new file mode 100644 index 0000000..e69de29 diff --git a/samples/paletted/main.c b/samples/paletted/main.c index 3cbce97..d520ba2 100644 --- a/samples/paletted/main.c +++ b/samples/paletted/main.c @@ -123,6 +123,7 @@ 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, GL_COLOR_INDEX8_EXT, image1->width, image1->height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE_TWID_KOS, image1->data); + glGenerateMipmapEXT(GL_TEXTURE_2D); } /* A general OpenGL initialization function. Sets all of the initial parameters. */ @@ -130,7 +131,7 @@ void InitGL(int Width, int Height) // We call this right after our OpenG { LoadGLTextures(); glEnable(GL_TEXTURE_2D); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // This Will Clear The Background Color To Black + glClearColor(0.5f, 0.5f, 0.5f, 1.0f); glClearDepth(1.0); // Enables Clearing Of The Depth Buffer glDepthFunc(GL_LESS); // The Type Of Depth Test To Do glEnable(GL_DEPTH_TEST); // Enables Depth Testing