Merge branch '40-fix-mipmap-offsets' into 'master'

Resolve "Fix mipmap offsets"

Closes #40

See merge request simulant/GLdc!48
This commit is contained in:
Luke Benstead 2019-09-26 08:19:23 +00:00
commit 7ac318e002
10 changed files with 604 additions and 170 deletions

View File

@ -1,4 +1,6 @@
#include <stdio.h>
#include <assert.h>
#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) {

View File

@ -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) */

View File

@ -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) {

View File

@ -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) {

View File

@ -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

29
samples/mipmap/Makefile Normal file
View File

@ -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)

235
samples/mipmap/main.c Normal file
View File

@ -0,0 +1,235 @@
#include <stdio.h>
#include <stdlib.h>
#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<size;i+=3) { // reverse all of the colors. (bgr -> 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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

View File

View File

@ -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