diff --git a/CMakeLists.txt b/CMakeLists.txt index 06ed35c..f208e42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,9 +152,11 @@ function(gen_sample sample) if(PLATFORM_DREAMCAST) if(EXISTS "${CMAKE_SOURCE_DIR}/samples/${sample}/romdisk") message("Generating romdisk for sample: ${sample}") + file(GLOB_RECURSE ROMDISK_FILES "${ROMDISK_DIR}/*") add_custom_command( OUTPUT ${ROMDISK_IMG} COMMAND ${GENROMFS} -f ${ROMDISK_IMG} -d ${ROMDISK_DIR} -v + DEPENDS ${ROMDISK_FILES} ) add_custom_command( @@ -201,12 +203,14 @@ if(BUILD_SAMPLES) gen_sample(nehe04 samples/nehe04/main.c) gen_sample(nehe05 samples/nehe05/main.c) gen_sample(nehe06 samples/nehe06/main.c samples/loadbmp.c) + gen_sample(nehe06_strided samples/nehe06_strided/main.c samples/loadbmp.c) gen_sample(nehe06_vq samples/nehe06_vq/main.c) gen_sample(nehe06_4444twid samples/nehe06_4444twid/main.c) gen_sample(nehe08 samples/nehe08/main.c samples/nehe08/pvr-texture.c) gen_sample(nehe10 samples/nehe10/main.c samples/loadbmp.c) gen_sample(nehe16 samples/nehe16/main.c samples/nehe16/pvr-texture.c) gen_sample(nehe20 samples/nehe20/main.c samples/loadbmp.c) + gen_sample(nehe20_strided samples/nehe20_strided/main.c samples/loadbmp.c) gen_sample(ortho2d samples/ortho2d/main.c) gen_sample(paletted samples/paletted/main.c) gen_sample(paletted_pcx samples/paletted_pcx/main.c) diff --git a/GL/draw.c b/GL/draw.c index 6a17ecc..d9b8940 100644 --- a/GL/draw.c +++ b/GL/draw.c @@ -751,6 +751,25 @@ GL_FORCE_INLINE void apply_poly_header(PolyHeader* header, GLboolean multiTextur */ } +GL_FORCE_INLINE void apply_strided_texture_uv_scale(SubmissionTarget* target) { + TextureObject* texture = _glGetTexture0(); + + if(!texture || !texture->isStrided || !texture->pvrWidth || !texture->pvrHeight) { + return; + } + + const float uScale = (float) texture->logicalWidth / (float) texture->pvrWidth; + const float vScale = (float) texture->logicalHeight / (float) texture->pvrHeight; + Vertex* it = _glSubmissionTargetStart(target); + Vertex* end = _glSubmissionTargetEnd(target); + + while(it < end) { + it->uv[0] *= uScale; + it->uv[1] *= vScale; + ++it; + } +} + #define DEBUG_CLIPPING 0 @@ -842,6 +861,8 @@ GL_FORCE_INLINE void submitVertices(GLenum mode, GLsizei first, GLuint count, GL _glTnlApplyEffects(target); + apply_strided_texture_uv_scale(target); + // /* // Now, if multitexturing is enabled, we want to send exactly the same vertices again, except: // - We want to enable blending, and send them to the TR list diff --git a/GL/platform.h b/GL/platform.h index 4e9e23b..683bc32 100644 --- a/GL/platform.h +++ b/GL/platform.h @@ -67,8 +67,10 @@ typedef enum GPUTextureFormat { GPU_TXRFMT_PAL8BPP = (6 << 27), GPU_TXRFMT_TWIDDLED = (0 << 26), GPU_TXRFMT_NONTWIDDLED = (1 << 26), - GPU_TXRFMT_NOSTRIDE = (0 << 21), - GPU_TXRFMT_STRIDE = (1 << 21) + GPU_TXRFMT_POW2_STRIDE = (0 << 25), + GPU_TXRFMT_NOSTRIDE = GPU_TXRFMT_POW2_STRIDE, + GPU_TXRFMT_X32_STRIDE = (1 << 25), + GPU_TXRFMT_STRIDE = GPU_TXRFMT_X32_STRIDE } GPUTextureFormat; static inline uint32_t GPUPaletteSelect8BPP(uint32_t x) { @@ -218,6 +220,10 @@ typedef struct { int width; int height; int format; + int stride_width; + float uv_scale_u; + float uv_scale_v; + int is_strided; void* base; } txr; struct { @@ -232,6 +238,10 @@ typedef struct { int width; int height; int format; + int stride_width; + float uv_scale_u; + float uv_scale_v; + int is_strided; void* base; } txr2; } PolyContext; @@ -245,7 +255,13 @@ typedef struct { uint32_t d2; uint32_t d3; uint32_t d4; - uint8_t padding[32]; + struct { + uint32_t texture_stride; + float uv_scale_u; + float uv_scale_v; + uint8_t texture_is_strided; + uint8_t padding[19]; + } meta; } PolyHeader; enum GPUCommand { @@ -384,6 +400,10 @@ static inline void CompilePolyHeader(PolyHeader *dst, const PolyContext *src) { /* The base values for CMD */ dst->cmd = GPU_CMD_POLYHDR; + dst->meta.texture_is_strided = 0; + dst->meta.texture_stride = 0; + dst->meta.uv_scale_u = 1.0f; + dst->meta.uv_scale_v = 1.0f; /* Big hack! This enables texturing no matter what. If we disable texturing then * we lose offset color and the vertex format changes meaning we have to manipulate @@ -448,6 +468,10 @@ static inline void CompilePolyHeader(PolyHeader *dst, const PolyContext *src) { /* Polygon mode 3 */ dst->mode3 = (src->txr.mipmap << GPU_TA_PM3_MIPMAP_SHIFT) & GPU_TA_PM3_MIPMAP_MASK; dst->mode3 |= (src->txr.format << GPU_TA_PM3_TXRFMT_SHIFT) & GPU_TA_PM3_TXRFMT_MASK; + dst->meta.texture_is_strided = src->txr.is_strided ? 1 : 0; + dst->meta.texture_stride = src->txr.stride_width; + dst->meta.uv_scale_u = src->txr.uv_scale_u; + dst->meta.uv_scale_v = src->txr.uv_scale_v; /* Convert the texture address */ txr_base = (uint32_t) src->txr.base; diff --git a/GL/platforms/sh4.c b/GL/platforms/sh4.c index 35d73b1..116c480 100644 --- a/GL/platforms/sh4.c +++ b/GL/platforms/sh4.c @@ -87,6 +87,7 @@ GL_FORCE_INLINE void _glPerspectiveDivideVertex(Vertex* vertex, int count) { } static uintptr_t sq_dest_addr = 0; +static size_t CURRENT_TEXTURE_STRIDE = 0; static inline void _glPushHeader(Vertex* v, size_t count) { TRACE(); @@ -195,6 +196,12 @@ void SceneListSubmit(Vertex* vertices, int n) { Vertex* v0 = vertices; for(int i = 0; i < n - 1; ++i, ++v0) { if(is_header(v0)) { + PolyHeader* header = (PolyHeader*) v0; + if(header->meta.texture_is_strided && header->meta.texture_stride != CURRENT_TEXTURE_STRIDE) { + pvr_txr_set_stride(header->meta.texture_stride); + CURRENT_TEXTURE_STRIDE = header->meta.texture_stride; + } + _glPushHeader(v0, 1); visible_mask = 0; continue; @@ -378,6 +385,7 @@ void SceneListSubmit(Vertex* vertices, int n) { } void SceneBegin() { + CURRENT_TEXTURE_STRIDE = 0; pvr_wait_ready(); pvr_scene_begin(); } diff --git a/GL/private.h b/GL/private.h index 19289e8..2d7ecdf 100644 --- a/GL/private.h +++ b/GL/private.h @@ -134,8 +134,12 @@ typedef struct { GLboolean isPaletted; //50 GLenum internalFormat; - //54 - GLubyte padding[10]; // Pad to 64-bytes + GLushort logicalWidth; + GLushort logicalHeight; + GLushort pvrWidth; + GLushort pvrHeight; + GLushort strideWidth; + GLboolean isStrided; } __attribute__((aligned(32))) TextureObject; typedef struct { diff --git a/GL/state.c b/GL/state.c index 5c2860a..2a85f68 100644 --- a/GL/state.c +++ b/GL/state.c @@ -422,8 +422,8 @@ void _glUpdatePVRTextureContext(PolyContext *context, GLshort textureUnit) { if(tx1->data) { context->txr.enable = GPU_TEXTURE_ENABLE; context->txr.filter = filter; - context->txr.width = tx1->width; - context->txr.height = tx1->height; + context->txr.width = tx1->pvrWidth ? tx1->pvrWidth : tx1->width; + context->txr.height = tx1->pvrHeight ? tx1->pvrHeight : tx1->height; context->txr.mipmap = enableMipmaps; context->txr.mipmap_bias = tx1->mipmap_bias; @@ -434,6 +434,14 @@ void _glUpdatePVRTextureContext(PolyContext *context, GLshort textureUnit) { } context->txr.format = tx1->color; + context->txr.is_strided = tx1->isStrided; + context->txr.stride_width = tx1->strideWidth; + context->txr.uv_scale_u = tx1->isStrided ? ((float) tx1->logicalWidth / (float) tx1->pvrWidth) : 1.0f; + context->txr.uv_scale_v = tx1->isStrided ? ((float) tx1->logicalHeight / (float) tx1->pvrHeight) : 1.0f; + + if(tx1->isStrided) { + context->txr.format |= GPU_TXRFMT_X32_STRIDE; + } if(tx1->isPaletted) { if(_glIsSharedTexturePaletteEnabled()) { @@ -1173,7 +1181,7 @@ const GLubyte *glGetString(GLenum name) { return (const GLubyte*) "1.2 (partial) - GLdc 1.1"; case GL_EXTENSIONS: - return (const GLubyte*)"GL_ARB_framebuffer_object, GL_ARB_multitexture, GL_ARB_texture_rg, GL_OES_compressed_paletted_texture, GL_EXT_paletted_texture, GL_EXT_shared_texture_palette, GL_KOS_multiple_shared_palette, GL_ARB_vertex_array_bgra, GL_ARB_vertex_type_2_10_10_10_rev, GL_KOS_texture_memory_management, GL_ATI_meminfo"; + return (const GLubyte*)"GL_ARB_framebuffer_object, GL_ARB_multitexture, GL_ARB_texture_rg, GL_OES_compressed_paletted_texture, GL_EXT_paletted_texture, GL_EXT_shared_texture_palette, GL_KOS_multiple_shared_palette, GL_ARB_vertex_array_bgra, GL_ARB_vertex_type_2_10_10_10_rev, GL_KOS_texture_memory_management, GL_KOS_texture_non_power_of_two, GL_ATI_meminfo"; } return (const GLubyte*) "GL_KOS_ERROR: ENUM Unsupported\n"; diff --git a/GL/texture.c b/GL/texture.c index e706e53..ca761da 100644 --- a/GL/texture.c +++ b/GL/texture.c @@ -42,6 +42,64 @@ static GLboolean TEXTURE_TWIDDLE_ENABLED = GL_FALSE; static void* ALLOC_BASE = NULL; static size_t ALLOC_SIZE = 0; +#define GL_KOS_MAX_STRIDE_WIDTH 992 + +static GLuint _glNextPowerOfTwo(GLuint v) { + if(v <= 1) { + return 1; + } + + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + return v + 1; +} + +static GLboolean _glIsTwiddledFormat(GLenum internalFormat) { + return ( + internalFormat == GL_RGB565_TWID_KOS || + internalFormat == GL_ARGB4444_TWID_KOS || + internalFormat == GL_ARGB1555_TWID_KOS || + internalFormat == GL_COLOR_INDEX8_TWID_KOS || + internalFormat == GL_COLOR_INDEX4_TWID_KOS || + internalFormat == GL_COMPRESSED_RGB_565_VQ_TWID_KOS || + internalFormat == GL_COMPRESSED_ARGB_1555_VQ_TWID_KOS || + internalFormat == GL_COMPRESSED_ARGB_4444_VQ_TWID_KOS || + internalFormat == GL_COMPRESSED_RGB_565_VQ_MIPMAP_TWID_KOS || + internalFormat == GL_COMPRESSED_ARGB_1555_VQ_MIPMAP_TWID_KOS || + internalFormat == GL_COMPRESSED_ARGB_4444_VQ_MIPMAP_TWID_KOS + ) ? GL_TRUE : GL_FALSE; +} + +static GLboolean _glIsCompressedFormat(GLenum internalFormat) { + return ( + internalFormat == GL_COMPRESSED_RGB_565_VQ_KOS || + internalFormat == GL_COMPRESSED_ARGB_1555_VQ_KOS || + internalFormat == GL_COMPRESSED_ARGB_4444_VQ_KOS || + internalFormat == GL_COMPRESSED_RGB_565_VQ_TWID_KOS || + internalFormat == GL_COMPRESSED_ARGB_1555_VQ_TWID_KOS || + internalFormat == GL_COMPRESSED_ARGB_4444_VQ_TWID_KOS || + internalFormat == GL_COMPRESSED_RGB_565_VQ_MIPMAP_KOS || + internalFormat == GL_COMPRESSED_ARGB_1555_VQ_MIPMAP_KOS || + internalFormat == GL_COMPRESSED_ARGB_4444_VQ_MIPMAP_KOS || + internalFormat == GL_COMPRESSED_RGB_565_VQ_MIPMAP_TWID_KOS || + internalFormat == GL_COMPRESSED_ARGB_1555_VQ_MIPMAP_TWID_KOS || + internalFormat == GL_COMPRESSED_ARGB_4444_VQ_MIPMAP_TWID_KOS + ) ? GL_TRUE : GL_FALSE; +} + +static GLboolean _glIsPalettedFormat(GLenum internalFormat) { + return ( + internalFormat == GL_COLOR_INDEX8_EXT || + internalFormat == GL_COLOR_INDEX4_EXT || + internalFormat == GL_COLOR_INDEX8_TWID_KOS || + internalFormat == GL_COLOR_INDEX4_TWID_KOS + ) ? GL_TRUE : GL_FALSE; +} + static void calc_twiddle_factors(uint32_t w, uint32_t h, uint32_t* maskX, uint32_t* maskY) { *maskX = 0; *maskY = 0; @@ -495,6 +553,10 @@ void _glResetSharedPalettes() static void _glInitializeTextureObject(TextureObject* txr, unsigned int id) { txr->index = id; txr->width = txr->height = 0; + txr->logicalWidth = txr->logicalHeight = 0; + txr->pvrWidth = txr->pvrHeight = 0; + txr->strideWidth = 0; + txr->isStrided = GL_FALSE; txr->mipmap = 0; txr->uv_wrap = 0; txr->env = GPU_TXRENV_MODULATEALPHA; @@ -868,16 +930,23 @@ void APIENTRY glCompressedTexImage2DARB(GLenum target, gl_assert(ACTIVE_TEXTURE < MAX_GLDC_TEXTURE_UNITS); TextureObject* active = TEXTURE_UNITS[ACTIVE_TEXTURE]; - GLuint original_id = active->index; if(!active) { _glKosThrowError(GL_INVALID_OPERATION, __func__); return; } + GLuint original_id = active->index; + /* Set the required mipmap count */ active->width = width; active->height = height; + active->logicalWidth = width; + active->logicalHeight = height; + active->pvrWidth = width; + active->pvrHeight = height; + active->strideWidth = 0; + active->isStrided = GL_FALSE; active->internalFormat = internalFormat; active->color = _determinePVRFormat(internalFormat); active->mipmapCount = _glGetMipmapLevelCount(active); @@ -1059,6 +1128,18 @@ static GLint _cleanInternalFormat(GLint internalFormat) { } } +static GLint _cleanInternalFormatForTexture(GLint internalFormat, GLboolean forceNontwiddled) { + if(!forceNontwiddled) { + return _cleanInternalFormat(internalFormat); + } + + GLboolean oldTwiddle = TEXTURE_TWIDDLE_ENABLED; + TEXTURE_TWIDDLE_ENABLED = GL_FALSE; + GLint ret = _cleanInternalFormat(internalFormat); + TEXTURE_TWIDDLE_ENABLED = oldTwiddle; + return ret; +} + static GLuint _determinePVRFormat(GLint internalFormat) { /* Given a cleaned internalFormat, return the Dreamcast format * that can hold it @@ -1448,7 +1529,20 @@ static bool _glValidTextureSize(GLuint size) { } } -static bool _glTexImage2DValidate(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type) { +static GLboolean _glTextureSizeIsNPOT(GLsizei width, GLsizei height) { + return (!_glValidTextureSize((GLuint) width) || !_glValidTextureSize((GLuint) height)) ? GL_TRUE : GL_FALSE; +} + +static GLboolean _glTextureWrapIsClamped(const TextureObject* txr) { + return ( + (txr->uv_wrap & CLAMP_U) && + (txr->uv_wrap & CLAMP_V) && + !(txr->uv_wrap & MIRROR_U) && + !(txr->uv_wrap & MIRROR_V) + ) ? GL_TRUE : GL_FALSE; +} + +static bool _glTexImage2DValidate(const TextureObject* txr, GLenum target, GLint level, GLint internalFormat, GLint cleanInternalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type) { if(target != GL_TEXTURE_2D) { INFO_MSG("Target unsupported"); _glKosThrowError(GL_INVALID_ENUM, __func__); @@ -1493,16 +1587,62 @@ static bool _glTexImage2DValidate(GLenum target, GLint level, GLint internalForm } } - if(_cleanInternalFormat(internalFormat) == -1) { + if(cleanInternalFormat == -1) { INFO_MSG("Unsupported internal format"); _glKosThrowError(GL_INVALID_VALUE, __func__); return false; } + GLboolean isStrided = _glTextureSizeIsNPOT(width, height); + + if(isStrided) { + if(level != 0) { + INFO_MSG("GL_KOS_texture_non_power_of_two does not support mipmaps"); + _glKosThrowError(GL_INVALID_VALUE, __func__); + return false; + } + + if(width <= 0 || (width % 32) != 0 || width > GL_KOS_MAX_STRIDE_WIDTH) { + INFO_MSG("Invalid GL_KOS_texture_non_power_of_two width"); + _glKosThrowError(GL_INVALID_VALUE, __func__); + return false; + } + + if(height <= 0) { + INFO_MSG("Invalid GL_KOS_texture_non_power_of_two height"); + _glKosThrowError(GL_INVALID_VALUE, __func__); + return false; + } + + if(!_glTextureWrapIsClamped(txr)) { + INFO_MSG("GL_KOS_texture_non_power_of_two textures require clamp wrap modes"); + _glKosThrowError(GL_INVALID_OPERATION, __func__); + return false; + } + + if(_glIsTwiddledFormat(cleanInternalFormat)) { + INFO_MSG("GL_KOS_texture_non_power_of_two textures must use non-twiddled storage"); + _glKosThrowError(GL_INVALID_OPERATION, __func__); + return false; + } + + if(_glIsPalettedFormat(cleanInternalFormat)) { + INFO_MSG("GL_KOS_texture_non_power_of_two paletted textures are unsupported"); + _glKosThrowError(GL_INVALID_OPERATION, __func__); + return false; + } + + if(_glIsCompressedFormat(cleanInternalFormat)) { + INFO_MSG("GL_KOS_texture_non_power_of_two compressed textures are unsupported"); + _glKosThrowError(GL_INVALID_OPERATION, __func__); + return false; + } + } + GLuint w = width; GLuint h = height; if(level == 0){ - if(!_glValidTextureSize(w)) { + if(!isStrided && !_glValidTextureSize(w)) { /* Width is not a power of two. Must be!*/ INFO_MSG("Unsupported width"); _glKosThrowError(GL_INVALID_VALUE, __func__); @@ -1510,7 +1650,7 @@ static bool _glTexImage2DValidate(GLenum target, GLint level, GLint internalForm } - if(!_glValidTextureSize(h)) { + if(!isStrided && !_glValidTextureSize(h)) { /* height is not a power of two. Must be!*/ INFO_MSG("Unsupported height"); _glKosThrowError(GL_INVALID_VALUE, __func__); @@ -1639,10 +1779,6 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, GLenum format, GLenum type, const GLvoid *data) { TRACE(); - if(!_glTexImage2DValidate(target, level, internalFormat, width, height, border, format, type)) { - return; - } - gl_assert(ACTIVE_TEXTURE < MAX_GLDC_TEXTURE_UNITS); TextureObject* active = TEXTURE_UNITS[ACTIVE_TEXTURE]; @@ -1652,21 +1788,34 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, return; } + GLboolean useStridedNpot = _glTextureSizeIsNPOT(width, height); + GLenum cleanInternalFormat = _cleanInternalFormatForTexture(internalFormat, useStridedNpot); + + if(!_glTexImage2DValidate(active, target, level, internalFormat, cleanInternalFormat, width, height, border, format, type)) { + return; + } + GLboolean isPaletted = ( internalFormat == GL_COLOR_INDEX8_EXT || internalFormat == GL_COLOR_INDEX4_EXT || internalFormat == GL_COLOR_INDEX4_TWID_KOS || internalFormat == GL_COLOR_INDEX8_TWID_KOS ) ? GL_TRUE : GL_FALSE; - GLenum cleanInternalFormat = _cleanInternalFormat(internalFormat); + GLuint pvrFormat = _determinePVRFormat(cleanInternalFormat); GLuint originalId = active->index; + GLboolean isStrided = useStridedNpot; + GLuint texturePitch = isStrided ? (GLuint) width : (GLuint) width; + GLuint pvrWidth = isStrided ? _glNextPowerOfTwo((GLuint) width) : (GLuint) width; + GLuint pvrHeight = isStrided ? _glNextPowerOfTwo((GLuint) height) : (GLuint) height; if(active->data && (level == 0)) { /* pre-existing texture - check if changed */ if(active->width != width || active->height != height || - active->internalFormat != cleanInternalFormat) { + active->internalFormat != cleanInternalFormat || + active->isStrided != isStrided || + active->strideWidth != (isStrided ? texturePitch : 0)) { /* changed - free old texture memory */ alloc_free(ALLOC_BASE, active->data); active->data = NULL; @@ -1684,8 +1833,11 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, */ GLint destStride = _determineStrideInternal(cleanInternalFormat); GLint sourceStride = _determineStride(format, type); - GLuint srcBytes = ((GLuint)width * (GLuint)height * (GLuint)sourceStride); - GLuint destBytes = ((GLuint)width * (GLuint)height * (GLuint)destStride); + GLuint sourceRowWidth = is4BPPFormat(format) ? (((GLuint) width + 1) / 2) : ((GLuint) width * (GLuint) sourceStride); + GLuint sourcePitch = _glGetUnpackRowPitch(width, sourceStride, format); + GLuint srcBytes = height ? (sourcePitch * ((GLuint) height - 1) + sourceRowWidth) : 0; + GLuint texelCount = texturePitch * (GLuint)height; + GLuint destBytes = texelCount * (GLuint)destStride; TextureConversionFunc conversion = NULL; int needs_conversion = _determineConversion(cleanInternalFormat, format, type, &conversion); @@ -1712,6 +1864,12 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, /* need texture memory */ active->width = width; active->height = height; + active->logicalWidth = width; + active->logicalHeight = height; + active->pvrWidth = pvrWidth; + active->pvrHeight = pvrHeight; + active->isStrided = isStrided; + active->strideWidth = isStrided ? (GLushort) texturePitch : 0; active->color = pvrFormat; active->internalFormat = cleanInternalFormat; /* Set the required mipmap count */ @@ -1755,6 +1913,7 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, gl_assert(targetData); if(!data) { + MEMSET4(targetData, 0x0, destBytes); gl_assert(active->index == originalId); _glGPUStateMarkDirty(); return; @@ -1832,24 +1991,24 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, calc_twiddle_factors(width, height, &maskX, &maskY); } - for(uint32_t i = 0; i < (width * height); ++i) { - const GLubyte* src = data + (sourceStride * i); - GLubyte* dst; + for(uint32_t y = 0; y < (uint32_t) height; ++y) { + for(uint32_t x = 0; x < (uint32_t) width; ++x) { + const GLubyte* src = ((const GLubyte*) data) + (sourcePitch * y) + (sourceStride * x); + GLubyte* dst; - if(twiddle) { - uint32_t x = i % width; - uint32_t y = i / width; - uint32_t newLocation = twid_compute_index(x, y, maskX, maskY); - dst = targetData + (destStride * newLocation); - } else { - dst = targetData + (destStride * i); - } + if(twiddle) { + uint32_t newLocation = twid_compute_index(x, y, maskX, maskY); + dst = targetData + (destStride * newLocation); + } else { + dst = targetData + ((texturePitch * y + x) * destStride); + } - if(convert) { - conversion(src, dst); - } else { - for(int j = 0; j < destStride; ++j) { - dst[j] = src[j]; + if(convert) { + conversion(src, dst); + } else { + for(int j = 0; j < destStride; ++j) { + dst[j] = src[j]; + } } } } @@ -1857,11 +2016,18 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, } else { /* No conversion necessary, we can just upload data directly */ gl_assert(targetData); - gl_assert(data); gl_assert(destBytes); - /* No conversion? Just copy the data, and the pvr_format is correct */ - FASTCPY(targetData, data, destBytes); + if(sourcePitch == ((GLuint) width * (GLuint) destStride) && texturePitch == (GLuint) width) { + /* No conversion? Just copy the data, and the pvr_format is correct */ + FASTCPY(targetData, data, destBytes); + } else { + for(GLsizei y = 0; y < height; ++y) { + GLubyte* destRow = targetData + ((GLuint) y * texturePitch * (GLuint) destStride); + const GLubyte* srcRow = ((const GLubyte*) data) + ((GLuint) y * sourcePitch); + FASTCPY(destRow, srcRow, sourceRowWidth); + } + } gl_assert(active->index == originalId); } @@ -1913,13 +2079,24 @@ void APIENTRY glTexParameteri(GLenum target, GLenum pname, GLint param) { case GL_CLAMP_TO_EDGE: case GL_CLAMP: active->uv_wrap |= CLAMP_U; + active->uv_wrap &= ~MIRROR_U; break; case GL_REPEAT: + if(active->isStrided) { + _glKosThrowError(GL_INVALID_OPERATION, __func__); + return; + } active->uv_wrap &= ~CLAMP_U; + active->uv_wrap &= ~MIRROR_U; break; case GL_MIRRORED_REPEAT: + if(active->isStrided) { + _glKosThrowError(GL_INVALID_OPERATION, __func__); + return; + } + active->uv_wrap &= ~CLAMP_U; active->uv_wrap |= MIRROR_U; break; } @@ -1931,13 +2108,24 @@ void APIENTRY glTexParameteri(GLenum target, GLenum pname, GLint param) { case GL_CLAMP_TO_EDGE: case GL_CLAMP: active->uv_wrap |= CLAMP_V; + active->uv_wrap &= ~MIRROR_V; break; case GL_REPEAT: + if(active->isStrided) { + _glKosThrowError(GL_INVALID_OPERATION, __func__); + return; + } active->uv_wrap &= ~CLAMP_V; + active->uv_wrap &= ~MIRROR_V; break; case GL_MIRRORED_REPEAT: + if(active->isStrided) { + _glKosThrowError(GL_INVALID_OPERATION, __func__); + return; + } + active->uv_wrap &= ~CLAMP_V; active->uv_wrap |= MIRROR_V; break; } @@ -2170,9 +2358,15 @@ void APIENTRY glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint y return; } + if(active->isStrided && level != 0) { + _glKosThrowError(GL_INVALID_VALUE, __func__); + return; + } + // Retrieve the dimensions of the currently bound texture - GLsizei textureWidth = active->width; - GLsizei textureHeight = active->height; + GLsizei textureWidth = active->logicalWidth ? active->logicalWidth : active->width; + GLsizei textureHeight = active->logicalHeight ? active->logicalHeight : active->height; + GLsizei texturePitch = active->isStrided ? active->strideWidth : textureWidth; if (!_glTexSubImage2DValidate(target, level, xoffset, yoffset, width, height, format, type, textureWidth, textureHeight)) { INFO_MSG("Error: _glTexSubImage2DValidate failed\n"); @@ -2197,7 +2391,7 @@ void APIENTRY glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint y GLint destStride = _determineStrideInternal(cleanInternalFormat); // Calculate destBytes using the texture's full dimensions - GLuint destBytes = (textureWidth * textureHeight * destStride); + GLuint destBytes = (texturePitch * textureHeight * destStride); TextureConversionFunc conversion = NULL; int needs_conversion = _determineConversion(cleanInternalFormat, format, type, &conversion); @@ -2237,7 +2431,7 @@ void APIENTRY glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint y if (needs_conversion == CONVERSION_TYPE_CONVERT) { for (uint32_t y = 0; y < height; ++y) { src = (const GLubyte*) data + (y * sourcePitch); - dst = conversionBuffer + ((y + yoffset) * textureWidth + xoffset) * destStride; + dst = conversionBuffer + ((y + yoffset) * texturePitch + xoffset) * destStride; for (uint32_t x = 0; x < width; ++x) { conversion(src, dst); dst += destStride; @@ -2298,7 +2492,7 @@ void APIENTRY glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint y } for (GLsizei y = 0; y < height; ++y) { - GLubyte* destRow = targetData + ((y + yoffset) * textureWidth + xoffset) * destStride; + GLubyte* destRow = targetData + ((y + yoffset) * texturePitch + xoffset) * destStride; FASTCPY(destRow, (GLubyte*)data + y * sourcePitch, sourceRowWidth); } } diff --git a/include/GL/glkos.h b/include/GL/glkos.h index 524212d..47f8f4c 100644 --- a/include/GL/glkos.h +++ b/include/GL/glkos.h @@ -214,4 +214,15 @@ GLAPI GLvoid APIENTRY glDefragmentTextureMemory_KOS(void); /* If enabled, will twiddle texture uploads where possible */ #define GL_TEXTURE_TWIDDLE_KOS 0xEF51 +/* + * CUSTOM EXTENSION GL_KOS_texture_non_power_of_two + * + * Exposes limited GL_TEXTURE_2D non-power-of-two texture support backed by + * the Dreamcast PVR's non-twiddled x32 strided texture mode. Texture + * coordinates remain normalized. This extension is clamp-only: PVR wrapping + * still works for normal POT textures, but strided NPOT textures upload only + * logical width * logical height data while the PVR header uses padded POT + * dimensions. + */ + __END_DECLS diff --git a/samples/nehe06_strided/main.c b/samples/nehe06_strided/main.c new file mode 100644 index 0000000..1e992f9 --- /dev/null +++ b/samples/nehe06_strided/main.c @@ -0,0 +1,193 @@ +#include +#include +#include +#include + +#ifdef _arch_dreamcast +#include +#endif + +#include "GL/gl.h" +#include "GL/glu.h" +#include "GL/glext.h" +#include "GL/glkos.h" + +#ifdef _arch_dreamcast +extern uint8_t romdisk[]; +KOS_INIT_ROMDISK(romdisk); +#define IMAGE_FILENAME "/rd/NeHe.bmp" +#else +#define IMAGE_FILENAME "../samples/nehe06_strided/romdisk/NeHe.bmp" +#endif + +#include "../loadbmp.h" + +/* floats for x rotation, y rotation, z rotation */ +float xrot, yrot, zrot; + +/* storage for one texture */ +GLuint texture[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(IMAGE_FILENAME, image1)) { + exit(1); + } + + printf("Loaded strided texture %dx%d\n", image1->sizeX, image1->sizeY); + + // 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); // scale linearly when image smaller than texture + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // 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_RGB, image1->sizeX, image1->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, image1->data); + free(image1); +} + +/* 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); +} + +int check_start() { +#ifdef _arch_dreamcast + maple_device_t *cont; + cont_state_t *state; + + cont = maple_enum_type(0, MAPLE_FUNC_CONTROLLER); + + if(cont) { + state = (cont_state_t *)maple_dev_status(cont); + + if(state) + return state->buttons & CONT_START; + } +#endif + + return 0; +} + +/* The main drawing function. */ +void DrawGLScene() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer + glLoadIdentity(); // Reset The View + + glTranslatef(0.0f,0.0f,-5.0f); // move 5 units into the screen. + + glRotatef(xrot,1.0f,0.0f,0.0f); // Rotate On The X Axis + glRotatef(yrot,0.0f,1.0f,0.0f); // Rotate On The Y Axis + glRotatef(zrot,0.0f,0.0f,1.0f); // Rotate On The Z Axis + + glBindTexture(GL_TEXTURE_2D, texture[0]); // choose the texture to use. + + glBegin(GL_QUADS); // begin drawing a cube + + // Front Face (note that the texture's corners have to match the quad's corners) + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Top Left Of The Texture and Quad + + // Back Face + glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Bottom Left Of The Texture and Quad + + // Top Face + glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Top Right Of The Texture and Quad + + // Bottom Face + glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Right Of The Texture and Quad + + // Right face + glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Bottom Left Of The Texture and Quad + + // Left Face + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // Top Left Of The Texture and Quad + + glEnd(); // done with the polygon. + + xrot+=1.5f; // X Axis Rotation + yrot+=1.5f; // Y Axis Rotation + zrot+=1.5f; // Z Axis Rotation + // + // swap buffers to display, since we're double buffered. + glKosSwapBuffers(); +} + +int main(int argc, char **argv) +{ + glKosInit(); + + InitGL(640, 480); + ReSizeGLScene(640, 480); + + while(1) { + if(check_start()) + break; + + DrawGLScene(); + } + + return 0; +} diff --git a/samples/nehe06_strided/romdisk/NeHe.bmp b/samples/nehe06_strided/romdisk/NeHe.bmp new file mode 100644 index 0000000..e1c652e Binary files /dev/null and b/samples/nehe06_strided/romdisk/NeHe.bmp differ diff --git a/samples/nehe06_strided/romdisk/PLACEHOLDER b/samples/nehe06_strided/romdisk/PLACEHOLDER new file mode 100644 index 0000000..e69de29 diff --git a/samples/nehe20_strided/main.c b/samples/nehe20_strided/main.c new file mode 100644 index 0000000..5a62468 --- /dev/null +++ b/samples/nehe20_strided/main.c @@ -0,0 +1,280 @@ +/* DREAMCAST + *IAN MICHEAL Ported SDL+OPENGL USING SDL[DREAMHAL][GLDC][KOS2.0]2021 + * Cleaned and tested on dreamcast hardware by Ianmicheal + * This Code Was Created By Pet & Commented/Cleaned Up By Jeff Molofee + * If You've Found This Code Useful, Please Let Me Know. + * Visit NeHe Productions At http://nehe.gamedev.net + */ + +#include // Header File For Windows Math Library +#include // Header File For Standard Input/Output +#include +#include + + +#ifdef _arch_dreamcast +#include +#endif + +#define FPS 60 +uint32_t waittime = 1000.0f/FPS; +uint32_t framestarttime = 0; +int32_t delaytime; + +#ifdef _arch_dreamcast +extern uint8_t romdisk[]; +KOS_INIT_ROMDISK(romdisk); +#define IMG_LOGO_PATH "/rd/logo.bmp" +#define IMG_MASK1_PATH "/rd/mask1.bmp" +#define IMG_IMAGE1_PATH "/rd/image1.bmp" +#define IMG_MASK2_PATH "/rd/mask2.bmp" +#define IMG_IMAGE2_PATH "/rd/image2.bmp" +#else +#define IMG_LOGO_PATH "../samples/nehe20_strided/romdisk/logo.bmp" +#define IMG_MASK1_PATH "../samples/nehe20_strided/romdisk/mask1.bmp" +#define IMG_IMAGE1_PATH "../samples/nehe20_strided/romdisk/image1.bmp" +#define IMG_MASK2_PATH "../samples/nehe20_strided/romdisk/mask2.bmp" +#define IMG_IMAGE2_PATH "../samples/nehe20_strided/romdisk/image2.bmp" +#endif + +#include "../loadbmp.h" + +/* + * This Code Was Created By Jeff Molofee 2000 + * And Modified By Giuseppe D'Agata (waveform@tiscalinet.it) + * If You've Found This Code Useful, Please Let Me Know. + * Visit My Site At nehe.gamedev.net + */ + +#include // Header File For Windows Math Library +#include // Header File For Standard Input/Output +#include + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif +#if defined(__APPLE__) && defined(__MACH__) +#include // Header File For The OpenGL32 Library +#include // Header File For The GLu32 Library +#elif defined(_arch_dreamcast) +#include +#include // Header File For The OpenGL32 Library +#include // Header File For The GLu32 Library +#include +#else +#include // Header File For The OpenGL32 Library +#include +#include // Header File For The GLu32 Library +#endif + +#define BOOL int +#define FALSE 0 +#define TRUE 1 + + +uint8_t* keys; // Array Used For The Keyboard Routine +BOOL active=TRUE; // Window Active Flag Set To TRUE By Default +BOOL fullscreen=FALSE; // Fullscreen Flag Set To Fullscreen Mode By Default +BOOL masking=TRUE; // Masking On/Off +BOOL mp; // M Pressed? +BOOL sp; // Space Pressed? +BOOL scene; // Which Scene To Draw + +GLuint texture[5]; // Storage For Our Five Textures +GLuint loop; // Generic Loop Variable + +GLfloat roll; // Rolling Texture + +int LoadGLTextures() // Load Bitmaps And Convert To Textures +{ + int Status=FALSE; // Status Indicator + + Image TextureImage[5]; + + if ((ImageLoad(IMG_LOGO_PATH, &TextureImage[0])) && // Logo Texture + (ImageLoad(IMG_MASK1_PATH, &TextureImage[1])) && // First Mask + (ImageLoad(IMG_IMAGE1_PATH, &TextureImage[2])) && // First Image + (ImageLoad(IMG_MASK2_PATH, &TextureImage[3])) && // Second Mask + (ImageLoad(IMG_IMAGE2_PATH, &TextureImage[4]))) // Second Image + { + Status=TRUE; // Set The Status To TRUE + glGenTextures(5, &texture[0]); // Create Five Textures + + for (loop=0; loop<5; loop++) // Loop Through All 5 Textures + { + glBindTexture(GL_TEXTURE_2D, texture[loop]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + if (loop == 0) + { + printf("Uploading NPOT logo texture %dx%d\n", + TextureImage[loop].sizeX, TextureImage[loop].sizeY); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + } + glTexImage2D( + GL_TEXTURE_2D, 0, 3, + TextureImage[loop].sizeX, + TextureImage[loop].sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, + TextureImage[loop].data + ); + } + } + + return Status; // Return The Status +} + +GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // Resize And Initialize The GL Window +{ + if (height==0) // Prevent A Divide By Zero By + { + height=1; // Making Height Equal One + } + + glViewport(0,0,width,height); // Reset The Current Viewport + glMatrixMode(GL_PROJECTION); // Select The Projection Matrix + glLoadIdentity(); // Reset The Projection Matrix + gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f); // Calculate Window Aspect Ratio + glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix + glLoadIdentity(); // Reset The Modelview Matrix +} + +int InitGL(GLvoid) // All Setup For OpenGL Goes Here +{ + if (!LoadGLTextures()) // Jump To Texture Loading Routine + { + return FALSE; // If Texture Didn't Load Return FALSE + } + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Clear The Background Color To Black + glClearDepth(1.0); // Enables Clearing Of The Depth Buffer + glEnable(GL_DEPTH_TEST); // Enable Depth Testing + glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading + glEnable(GL_TEXTURE_2D); // Enable 2D Texture Mapping + return TRUE; // Initialization Went OK +} + +int DrawGLScene(GLvoid) // Here's Where We Do All The Drawing +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer + glLoadIdentity(); // Reset The Modelview Matrix + glTranslatef(0.0f,0.0f,-2.0f); // Move Into The Screen 5 Units + + glBindTexture(GL_TEXTURE_2D, texture[0]); // Select Our Logo Texture + glBegin(GL_QUADS); // Start Drawing A Textured Quad + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.1f, -1.1f, 0.0f); // Bottom Left + glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.1f, -1.1f, 0.0f); // Bottom Right + glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.1f, 1.1f, 0.0f); // Top Right + glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.1f, 1.1f, 0.0f); // Top Left + glEnd(); // Done Drawing The Quad + + glEnable(GL_BLEND); // Enable Blending + glDisable(GL_DEPTH_TEST); // Disable Depth Testing + + if (masking) // Is Masking Enabled? + { + glBlendFunc(GL_DST_COLOR,GL_ZERO); // Blend Screen Color With Zero (Black) + } + + if (scene) // Are We Drawing The Second Scene? + { + glTranslatef(0.0f,0.0f,-1.0f); // Translate Into The Screen One Unit + glRotatef(roll*360,0.0f,0.0f,1.0f); // Rotate On The Z Axis 360 Degrees. + if (masking) // Is Masking On? + { + glBindTexture(GL_TEXTURE_2D, texture[3]); // Select The Second Mask Texture + glBegin(GL_QUADS); // Start Drawing A Textured Quad + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.1f, -1.1f, 0.0f); // Bottom Left + glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.1f, -1.1f, 0.0f); // Bottom Right + glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.1f, 1.1f, 0.0f); // Top Right + glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.1f, 1.1f, 0.0f); // Top Left + glEnd(); // Done Drawing The Quad + } + + glBlendFunc(GL_ONE, GL_ONE); // Copy Image 2 Color To The Screen + glBindTexture(GL_TEXTURE_2D, texture[4]); // Select The Second Image Texture + glBegin(GL_QUADS); // Start Drawing A Textured Quad + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.1f, -1.1f, 0.0f); // Bottom Left + glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.1f, -1.1f, 0.0f); // Bottom Right + glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.1f, 1.1f, 0.0f); // Top Right + glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.1f, 1.1f, 0.0f); // Top Left + glEnd(); // Done Drawing The Quad + } + else // Otherwise + { + if (masking) // Is Masking On? + { + glBindTexture(GL_TEXTURE_2D, texture[1]); // Select The First Mask Texture + glBegin(GL_QUADS); // Start Drawing A Textured Quad + glTexCoord2f(roll+0.0f, 0.0f); glVertex3f(-1.1f, -1.1f, 0.0f); // Bottom Left + glTexCoord2f(roll+4.0f, 0.0f); glVertex3f( 1.1f, -1.1f, 0.0f); // Bottom Right + glTexCoord2f(roll+4.0f, 4.0f); glVertex3f( 1.1f, 1.1f, 0.0f); // Top Right + glTexCoord2f(roll+0.0f, 4.0f); glVertex3f(-1.1f, 1.1f, 0.0f); // Top Left + glEnd(); // Done Drawing The Quad + } + + glBlendFunc(GL_ONE, GL_ONE); // Copy Image 1 Color To The Screen + glBindTexture(GL_TEXTURE_2D, texture[2]); // Select The First Image Texture + glBegin(GL_QUADS); // Start Drawing A Textured Quad + glTexCoord2f(roll+0.0f, 0.0f); glVertex3f(-1.1f, -1.1f, 0.0f); // Bottom Left + glTexCoord2f(roll+4.0f, 0.0f); glVertex3f( 1.1f, -1.1f, 0.0f); // Bottom Right + glTexCoord2f(roll+4.0f, 4.0f); glVertex3f( 1.1f, 1.1f, 0.0f); // Top Right + glTexCoord2f(roll+0.0f, 4.0f); glVertex3f(-1.1f, 1.1f, 0.0f); // Top Left + glEnd(); // Done Drawing The Quad + } + + glEnable(GL_DEPTH_TEST); // Enable Depth Testing + glDisable(GL_BLEND); // Disable Blending + + roll+=0.002f; // Increase Our Texture Roll Variable + if (roll>1.0f) // Is Roll Greater Than One + { + roll-=1.0f; // Subtract 1 From Roll + } + + glKosSwapBuffers(); + + return TRUE; // Everything Went OK +} + +int main(int argc, char *argv[]) +{ + glKosInit(); + + InitGL(); + ReSizeGLScene(640, 480); + +#ifdef _arch_dreamcast + maple_device_t* cont = maple_enum_type(0, MAPLE_FUNC_CONTROLLER); + assert(cont); +#endif + + while(1) { + DrawGLScene(); + +#ifdef _arch_dreamcast + cont_state_t* state = (cont_state_t *)maple_dev_status(cont); + + if((state->buttons & CONT_A) && !sp) { + sp = TRUE; + scene = !scene; + } else { + sp = FALSE; + } + + if((state->buttons & CONT_B) && !mp) { + mp = TRUE; + masking = !masking; + } else { + mp = FALSE; + } + + if(state->buttons & CONT_START) { + break; + } +#endif + } + + return 0; +} diff --git a/samples/nehe20_strided/romdisk/image1.bmp b/samples/nehe20_strided/romdisk/image1.bmp new file mode 100644 index 0000000..a89fdd2 Binary files /dev/null and b/samples/nehe20_strided/romdisk/image1.bmp differ diff --git a/samples/nehe20_strided/romdisk/image2.bmp b/samples/nehe20_strided/romdisk/image2.bmp new file mode 100644 index 0000000..9536a2b Binary files /dev/null and b/samples/nehe20_strided/romdisk/image2.bmp differ diff --git a/samples/nehe20_strided/romdisk/logo.bmp b/samples/nehe20_strided/romdisk/logo.bmp new file mode 100644 index 0000000..e7ad8fc Binary files /dev/null and b/samples/nehe20_strided/romdisk/logo.bmp differ diff --git a/samples/nehe20_strided/romdisk/mask1.bmp b/samples/nehe20_strided/romdisk/mask1.bmp new file mode 100644 index 0000000..6b65870 Binary files /dev/null and b/samples/nehe20_strided/romdisk/mask1.bmp differ diff --git a/samples/nehe20_strided/romdisk/mask2.bmp b/samples/nehe20_strided/romdisk/mask2.bmp new file mode 100644 index 0000000..9903d93 Binary files /dev/null and b/samples/nehe20_strided/romdisk/mask2.bmp differ diff --git a/tests/test_glteximage2d.h b/tests/test_glteximage2d.h index bdae539..eff4e1b 100644 --- a/tests/test_glteximage2d.h +++ b/tests/test_glteximage2d.h @@ -3,14 +3,18 @@ #include "tools/test.h" #include "tools/gl_test.h" +#include #include #include +#include #include class TexImage2DTests : public GLTestCase { public: uint8_t image_data[8 * 8 * 4] = {0}; + uint8_t stride_image_data[96 * 48 * 4] = {0}; + uint8_t unpack_row_image_data[128 * 48 * 4] = {0}; void set_up() { GLTestCase::set_up(); @@ -25,6 +29,13 @@ public: } } + void set_clamp_wrap() { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + assert_equal(glGetError(), GL_NO_ERROR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + assert_equal(glGetError(), GL_NO_ERROR); + } + void test_rgb_to_rgb565() { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, image_data); assert_equal(glGetError(), GL_NO_ERROR); @@ -70,4 +81,99 @@ public: assert_equal(internalFormat, GL_ARGB4444_TWID_KOS); } + + void test_extension_string_advertises_limited_kos_npot() { + const char* extensions = (const char*) glGetString(GL_EXTENSIONS); + + assert_true(std::strstr(extensions, "GL_KOS_texture_non_power_of_two") != nullptr); + assert_true(std::strstr(extensions, "GL_KOS_texture_stride") == nullptr); + assert_true(std::strstr(extensions, "GL_ARB_texture_non_power_of_two") == nullptr); + } + + void test_npot_rejected_with_default_repeat_wrap() { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 96, 48, 0, GL_RGB, GL_UNSIGNED_BYTE, stride_image_data); + assert_equal(glGetError(), GL_INVALID_OPERATION); + } + + void test_npot_upload_allowed_with_clamp_wrap() { + set_clamp_wrap(); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 96, 48, 0, GL_RGB, GL_UNSIGNED_BYTE, stride_image_data); + assert_equal(glGetError(), GL_NO_ERROR); + + GLint internalFormat; + glGetIntegerv(GL_TEXTURE_INTERNAL_FORMAT_KOS, &internalFormat); + + assert_equal(internalFormat, GL_RGB565_KOS); + } + + void test_npot_upload_honors_unpack_row_length_without_backend_opt_in() { + set_clamp_wrap(); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, 128); + assert_equal(glGetError(), GL_NO_ERROR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 96, 48, 0, GL_RGB, GL_UNSIGNED_BYTE, unpack_row_image_data); + assert_equal(glGetError(), GL_NO_ERROR); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + assert_equal(glGetError(), GL_NO_ERROR); + } + + void test_npot_texture_width_must_be_multiple_of_32() { + set_clamp_wrap(); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 65, 48, 0, GL_RGB, GL_UNSIGNED_BYTE, stride_image_data); + assert_equal(glGetError(), GL_INVALID_VALUE); + } + + void test_npot_texture_width_must_not_exceed_stride_limit() { + set_clamp_wrap(); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1024, 48, 0, GL_RGB, GL_UNSIGNED_BYTE, stride_image_data); + assert_equal(glGetError(), GL_INVALID_VALUE); + } + + void test_npot_texture_rejects_repeat_after_upload() { + set_clamp_wrap(); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 96, 48, 0, GL_RGB, GL_UNSIGNED_BYTE, stride_image_data); + assert_equal(glGetError(), GL_NO_ERROR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + assert_equal(glGetError(), GL_INVALID_OPERATION); + } + + void test_pot_texture_repeat_wrap_unchanged() { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, + GL_RGB, GL_UNSIGNED_BYTE, image_data); + assert_equal(glGetError(), GL_NO_ERROR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + assert_equal(glGetError(), GL_NO_ERROR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + assert_equal(glGetError(), GL_NO_ERROR); + } + + void test_npot_texture_rejects_twiddled_storage() { + set_clamp_wrap(); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB_TWID_KOS, 96, 48, 0, GL_RGB, GL_UNSIGNED_BYTE, stride_image_data); + assert_equal(glGetError(), GL_INVALID_OPERATION); + } + + void test_npot_texture_rejects_paletted_storage() { + set_clamp_wrap(); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, 96, 48, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, stride_image_data); + assert_equal(glGetError(), GL_INVALID_OPERATION); + } + + void test_npot_texture_rejects_mipmap_level() { + set_clamp_wrap(); + + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGB, 96, 48, 0, GL_RGB, GL_UNSIGNED_BYTE, stride_image_data); + assert_equal(glGetError(), GL_INVALID_VALUE); + } };