Huge refactor of mipmap handling. All textures are now stored twiddled on the PVR
This commit is contained in:
parent
aafb99dede
commit
150c95bd33
|
@ -105,6 +105,7 @@ typedef struct {
|
|||
GLuint index;
|
||||
GLvoid *data;
|
||||
GLuint dataStride;
|
||||
GLuint baseDataSize; /* The data size of mipmap level 0 */
|
||||
|
||||
GLenum minFilter;
|
||||
GLenum magFilter;
|
||||
|
@ -112,6 +113,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) */
|
||||
|
|
14
GL/state.c
14
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) {
|
||||
|
|
256
GL/texture.c
256
GL/texture.c
|
@ -161,26 +161,83 @@ 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 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:
|
||||
case 0:
|
||||
offset = 0x00000;
|
||||
break;
|
||||
}
|
||||
|
||||
if(height > 1) {
|
||||
height /= 2;
|
||||
} else {
|
||||
switch(size >> level) {
|
||||
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 +245,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->width * obj->height * obj->dataStride;
|
||||
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 +314,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 +816,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 +836,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 +847,29 @@ 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);
|
||||
|
||||
fprintf(stderr, "Allocating %d PVR bytes\n", bytes);
|
||||
active->data = pvr_mem_malloc(bytes);
|
||||
|
||||
/* If there was existing data, then copy it where it should go */
|
||||
fprintf(stderr, "Copying data\n");
|
||||
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 +950,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 +961,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 +985,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 +1012,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 +1023,6 @@ 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;
|
||||
} 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 +1031,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 */
|
||||
sq_cpy(targetData, data, bytes);
|
||||
return;
|
||||
} else {
|
||||
} else if(needsConversion) {
|
||||
TextureConversionFunc convert = _determineConversion(
|
||||
internalFormat,
|
||||
format,
|
||||
|
@ -1020,12 +1068,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 +1076,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 +1093,34 @@ 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 &= ~PVR_TXRFMT_NONTWIDDLED;
|
||||
active->color |= PVR_TXRFMT_TWIDDLED;
|
||||
} 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!
|
||||
sq_cpy(targetData, conversionBuffer, bytes);
|
||||
}
|
||||
|
||||
if(conversionBuffer) {
|
||||
free(conversionBuffer);
|
||||
conversionBuffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void APIENTRY glTexParameteri(GLenum target, GLenum pname, GLint param) {
|
||||
|
|
|
@ -174,9 +174,28 @@ void DrawQuad() {
|
|||
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);
|
||||
|
|
Loading…
Reference in New Issue
Block a user