From f11f7c966aab78b0f13ae6ff5132ddf28d24ef92 Mon Sep 17 00:00:00 2001 From: GPF Date: Fri, 3 Jul 2026 09:37:12 -0700 Subject: [PATCH] texture: fix Dreamcast hardware texture and scissor regressions Fix several Dreamcast hardware regressions found while testing real PVR behavior. - Upload the default white texture through the platform texture loader instead of memcpy directly into PVR texture memory. - Add GPUTextureLoad so SH4 uses pvr_txr_load while the software backend keeps memcpy behavior. - Track scissor/userclip application per polygon list and invalidate scissor state at buffer swap. - Submit queued geometry before USERCLIP/header transitions in the SH4 submit path. - Remove the stale packed 4bpp source-size double-halving in glTexImage2D. - Add texture regression coverage for packed 4bpp, unpack row length, compressed palette4 forwarding, and 8bpp-to-4bpp packing. - Update the scissor sample and add polygon_compare as a small hardware comparison sample. Verified: - make -C dcbuild gldc_tests scissor polygon_compare - scissor sample on Dreamcast hardware - polygon_compare sample on Dreamcast hardware - existing strided texture sample on Dreamcast hardware --- CMakeLists.txt | 1 + GL/draw.c | 3 +- GL/flush.c | 2 +- GL/platform.h | 12 ++- GL/platforms/sh4.c | 12 +++ GL/platforms/sh4.h | 1 + GL/platforms/software.c | 4 + GL/platforms/software.h | 2 +- GL/private.h | 1 + GL/state.c | 100 +++++++++++++++------ GL/texture.c | 6 -- samples/polygon_compare/main.c | 159 +++++++++++++++++++++++++++++++++ samples/scissor/main.c | 141 +++++++++++++++++++++-------- tests/test_texture_formats.h | 57 ++++++++++++ 14 files changed, 424 insertions(+), 77 deletions(-) create mode 100644 samples/polygon_compare/main.c diff --git a/CMakeLists.txt b/CMakeLists.txt index e01dded..257207d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,6 +181,7 @@ if(BUILD_SAMPLES) gen_sample(zclip_triangle samples/zclip_triangle/main.c) gen_sample(zclip_trianglestrip samples/zclip_trianglestrip/main.c) gen_sample(scissor samples/scissor/main.c) + gen_sample(polygon_compare samples/polygon_compare/main.c) gen_sample(polymark samples/polymark/main.c) gen_sample(cubes samples/cubes/main.cpp) gen_sample(zclip_test tests/zclip/main.cpp) diff --git a/GL/draw.c b/GL/draw.c index a3065b3..4466bd4 100644 --- a/GL/draw.c +++ b/GL/draw.c @@ -867,8 +867,9 @@ GL_FORCE_INLINE void submitVertices(GLenum mode, GLsizei first, GLuint count, GL target->output = _glActivePolyList(); gl_assert(target->output); - uint32_t vector_size = aligned_vector_size(&target->output->vector); + _glApplyScissor(false); + uint32_t vector_size = aligned_vector_size(&target->output->vector); GLboolean header_required = (vector_size == 0) || _glGPUStateIsDirty(); target->count = calcFinalVertices(mode, count); diff --git a/GL/flush.c b/GL/flush.c index b6393a2..708164d 100644 --- a/GL/flush.c +++ b/GL/flush.c @@ -141,5 +141,5 @@ void APIENTRY glKosSwapBuffers() { aligned_vector_clear(&PT_LIST.vector); aligned_vector_clear(&TR_LIST.vector); - _glApplyScissor(true); + _glInvalidateScissor(); } diff --git a/GL/platform.h b/GL/platform.h index 683bc32..0ecf0e7 100644 --- a/GL/platform.h +++ b/GL/platform.h @@ -390,6 +390,7 @@ static inline int DimensionFlag(const int w) { static uint8_t* DEFAULT_TEXTURE = NULL; extern void* GPUMemoryAlloc(size_t size); +extern void GPUTextureLoad(const void* src, void* dst, size_t size); /* Compile a polygon context into a polygon header */ static inline void CompilePolyHeader(PolyHeader *dst, const PolyContext *src) { @@ -437,18 +438,23 @@ static inline void CompilePolyHeader(PolyHeader *dst, const PolyContext *src) { dst->mode2 |= (src->gen.color_clamp << GPU_TA_PM2_CLAMP_SHIFT) & GPU_TA_PM2_CLAMP_MASK; dst->mode2 |= (src->gen.alpha << GPU_TA_PM2_ALPHA_SHIFT) & GPU_TA_PM2_ALPHA_MASK; + dst->mode3 = 0; + if(src->txr.enable == GPU_TEXTURE_DISABLE) { /* Texturing was disabled so we simulate it with a white texture */ if(!DEFAULT_TEXTURE) { + uint16_t texture[8 * 8]; + DEFAULT_TEXTURE = (uint8_t*) GPUMemoryAlloc(8 * 8 * 2); - for(int i = 0; i < 8 * 8 * 2; ++i) { - DEFAULT_TEXTURE[i] = 0xff; + for(int i = 0; i < 8 * 8; ++i) { + texture[i] = 0xffff; } + GPUTextureLoad(texture, DEFAULT_TEXTURE, sizeof(texture)); } dst->mode2 |= (DimensionFlag(8) << GPU_TA_PM2_USIZE_SHIFT) & GPU_TA_PM2_USIZE_MASK; dst->mode2 |= (DimensionFlag(8) << GPU_TA_PM2_VSIZE_SHIFT) & GPU_TA_PM2_VSIZE_MASK; dst->mode2 |= (GPU_TXRENV_MODULATE << GPU_TA_PM2_TXRENV_SHIFT) & GPU_TA_PM2_TXRENV_MASK; - dst->mode3 |= (GPU_TXRFMT_RGB565 << GPU_TA_PM3_TXRFMT_SHIFT) & GPU_TA_PM3_TXRFMT_MASK; + dst->mode3 |= ((GPU_TXRFMT_RGB565 | GPU_TXRFMT_NONTWIDDLED) << GPU_TA_PM3_TXRFMT_SHIFT) & GPU_TA_PM3_TXRFMT_MASK; /* Convert the texture address */ txr_base = (uint32_t) DEFAULT_TEXTURE; txr_base = (txr_base & 0x00fffff8) >> 3; diff --git a/GL/platforms/sh4.c b/GL/platforms/sh4.c index c5c6edd..866881c 100644 --- a/GL/platforms/sh4.c +++ b/GL/platforms/sh4.c @@ -20,6 +20,10 @@ void* GPUMemoryAlloc(size_t size) { return pvr_mem_malloc(size); } +void GPUTextureLoad(const void* src, void* dst, size_t size) { + pvr_txr_load(src, dst, size); +} + GL_FORCE_INLINE bool glIsVertex(const float flags) { return flags == GPU_CMD_VERTEX_EOL || flags == GPU_CMD_VERTEX; } @@ -199,7 +203,15 @@ void SceneListSubmit(Vertex* vertices, int n) { Vertex* v0 = vertices; for(int i = 0; i < n - 1; ++i, ++v0) { + if(v0->flags == GPU_CMD_USERCLIP) { + SUBMIT_QUEUED_VERTEX(GPU_CMD_VERTEX_EOL); + _glPushHeader(v0, 1); + visible_mask = 0; + continue; + } + if(is_header(v0)) { + SUBMIT_QUEUED_VERTEX(GPU_CMD_VERTEX_EOL); PolyHeader* header = (PolyHeader*) v0; if(header->meta.texture_is_strided && header->meta.texture_stride != CURRENT_TEXTURE_STRIDE) { _glPVRSetTextureStride(header->meta.texture_stride); diff --git a/GL/platforms/sh4.h b/GL/platforms/sh4.h index 3c90c26..6f0d413 100644 --- a/GL/platforms/sh4.h +++ b/GL/platforms/sh4.h @@ -167,6 +167,7 @@ static inline size_t GPUMemoryAvailable() { } void* GPUMemoryAlloc(size_t size); +void GPUTextureLoad(const void* src, void* dst, size_t size); static inline void GPUSetPaletteFormat(GPUPaletteFormat format) { pvr_set_pal_format((pvr_palfmt_t) format); } diff --git a/GL/platforms/software.c b/GL/platforms/software.c index 7550c45..c38cd5d 100644 --- a/GL/platforms/software.c +++ b/GL/platforms/software.c @@ -569,6 +569,10 @@ void* GPUMemoryAlloc(size_t size) { } } +void GPUTextureLoad(const void* src, void* dst, size_t size) { + memcpy(dst, src, size); +} + void GPUSetPaletteFormat(GPUPaletteFormat format) { } diff --git a/GL/platforms/software.h b/GL/platforms/software.h index ee14b47..111947a 100644 --- a/GL/platforms/software.h +++ b/GL/platforms/software.h @@ -62,6 +62,7 @@ enum GPUPaletteFormat; size_t GPUMemoryAvailable(); void* GPUMemoryAlloc(size_t size); +void GPUTextureLoad(const void* src, void* dst, size_t size); void GPUSetPaletteFormat(GPUPaletteFormat format); void GPUSetPaletteEntry(uint32_t idx, uint32_t value); @@ -73,4 +74,3 @@ void GPUSetFogLinear(float start, float end); void GPUSetFogExp(float density); void GPUSetFogExp2(float density); void GPUSetFogColor(float r, float g, float b, float a); - diff --git a/GL/private.h b/GL/private.h index 3a30237..27cabb2 100644 --- a/GL/private.h +++ b/GL/private.h @@ -441,6 +441,7 @@ GLuint _glUsedTextureMemory(); GLuint _glFreeContiguousTextureMemory(); void _glApplyScissor(bool force); +void _glInvalidateScissor(void); void _glSetColorMaterialMask(GLenum mask); void _glSetColorMaterialMode(GLenum mode); GLenum _glColorMaterialMode(); diff --git a/GL/state.c b/GL/state.c index 257b58a..9fe36b8 100644 --- a/GL/state.c +++ b/GL/state.c @@ -5,6 +5,10 @@ #include "../include/GL/glext.h" #include "private.h" +#define SCISSOR_APPLIED_OP 0x1u +#define SCISSOR_APPLIED_PT 0x2u +#define SCISSOR_APPLIED_TR 0x4u + static struct { GLboolean is_dirty; @@ -33,7 +37,7 @@ static struct { GLint y; GLsizei width; GLsizei height; - GLboolean applied; + unsigned int applied_lists; } scissor_rect; GLenum blend_sfactor; @@ -87,7 +91,7 @@ static struct { .scissor_test_enabled = GL_FALSE, .fog_enabled = GL_FALSE, .depth_mask_enabled = GL_FALSE, - .scissor_rect = {0, 0, 640, 480, false}, + .scissor_rect = {0, 0, 640, 480, 0}, .blend_sfactor = GL_ONE, .blend_dfactor = GL_ZERO, .blend_enabled = GL_FALSE, @@ -553,6 +557,7 @@ GLAPI void APIENTRY glEnable(GLenum cap) { case GL_SCISSOR_TEST: { if(GPUState.scissor_test_enabled != GL_TRUE) { GPUState.scissor_test_enabled = GL_TRUE; + GPUState.scissor_rect.applied_lists = 0; GPUState.is_dirty = GL_TRUE; } } break; @@ -670,6 +675,7 @@ GLAPI void APIENTRY glDisable(GLenum cap) { case GL_SCISSOR_TEST: { if(GPUState.scissor_test_enabled != GL_FALSE) { GPUState.scissor_test_enabled = GL_FALSE; + GPUState.scissor_rect.applied_lists = 0; GPUState.is_dirty = GL_TRUE; } } break; @@ -953,10 +959,8 @@ void APIENTRY glScissor(GLint x, GLint y, GLsizei width, GLsizei height) { GPUState.scissor_rect.y = y; GPUState.scissor_rect.width = width; GPUState.scissor_rect.height = height; - GPUState.scissor_rect.applied = false; + GPUState.scissor_rect.applied_lists = 0; GPUState.is_dirty = GL_TRUE; // FIXME: do we need this? - - _glApplyScissor(false); } /* Setup the hardware user clip rectangle. @@ -982,48 +986,88 @@ void APIENTRY glScissor(GLint x, GLint y, GLsizei width, GLsizei height) { at the right place, either when enabling the scissor test, or when the scissor test changes. */ +static unsigned int _glActiveScissorListBit(const PolyList* list) { + if(list == _glOpaquePolyList()) { + return SCISSOR_APPLIED_OP; + } + + if(list == _glPunchThruPolyList()) { + return SCISSOR_APPLIED_PT; + } + + return SCISSOR_APPLIED_TR; +} + +void _glInvalidateScissor(void) { + GPUState.scissor_rect.applied_lists = 0; +} + void _glApplyScissor(bool force) { - /* Don't do anyting if clipping is disabled */ + /* Don't do anything if clipping is disabled */ if(!GPUState.scissor_test_enabled) { return; } - /* Don't apply if we already applied - nothing changed */ - if(GPUState.scissor_rect.applied && !force) { + PolyList* target = _glActivePolyList(); + unsigned int active_list_bit = _glActiveScissorListBit(target); + + /* Don't apply if this list already has the current clip rectangle. */ + if((GPUState.scissor_rect.applied_lists & active_list_bit) && !force) { return; } - PVRTileClipCommand c; - - GLint miny, maxx, maxy; + typedef union { + Vertex vertex; + PVRTileClipCommand clip; + } ScissorCommand; + ScissorCommand c = {0}; const VideoMode* vid_mode = GetVideoMode(); - GLsizei scissor_width = MAX(MIN(GPUState.scissor_rect.width, vid_mode->width), 0); - GLsizei scissor_height = MAX(MIN(GPUState.scissor_rect.height, vid_mode->height), 0); + GLint x0 = GPUState.scissor_rect.x; + GLint y0 = GPUState.scissor_rect.y; + GLint x1 = x0 + GPUState.scissor_rect.width; + GLint y1 = y0 + GPUState.scissor_rect.height; - /* force the origin to the lower left-hand corner of the screen */ - miny = (vid_mode->height - scissor_height) - GPUState.scissor_rect.y; - maxx = (scissor_width + GPUState.scissor_rect.x); - maxy = (scissor_height + miny); + /* + * glScissor uses OpenGL window coordinates: lower-left origin, + * x/y inclusive start, width/height extent. Clip to the framebuffer + * before converting to PVR tile coordinates. + */ + x0 = CLAMP(x0, 0, vid_mode->width); + y0 = CLAMP(y0, 0, vid_mode->height); + x1 = CLAMP(x1, 0, vid_mode->width); + y1 = CLAMP(y1, 0, vid_mode->height); - /* load command structure while mapping screen coords to TA tiles */ - c.flags = GPU_CMD_USERCLIP; - c.d1 = c.d2 = c.d3 = 0; uint16_t vw = vid_mode->width >> 5; uint16_t vh = vid_mode->height >> 5; + uint16_t max_tx = vw - 1; + uint16_t max_ty = vh - 1; - c.sx = CLAMP(GPUState.scissor_rect.x >> 5, 0, vw); - c.sy = CLAMP(miny >> 5, 0, vh); - c.ex = CLAMP((maxx >> 5) - 1, 0, vw); - c.ey = CLAMP((maxy >> 5) - 1, 0, vh); + /* load command structure while mapping screen coords to TA tiles */ + c.clip.flags = GPU_CMD_USERCLIP; + c.clip.d1 = c.clip.d2 = c.clip.d3 = 0; - aligned_vector_push_back(&_glOpaquePolyList()->vector, &c, 1); - aligned_vector_push_back(&_glPunchThruPolyList()->vector, &c, 1); - aligned_vector_push_back(&_glTransparentPolyList()->vector, &c, 1); + if(x1 <= x0 || y1 <= y0) { + c.clip.sx = 0; + c.clip.sy = 0; + c.clip.ex = 0; + c.clip.ey = 0; + } else { + GLint pvr_y0 = vid_mode->height - y1; + GLint pvr_y1 = vid_mode->height - y0; - GPUState.scissor_rect.applied = true; + c.clip.sx = CLAMP(x0 >> 5, 0, max_tx); + c.clip.sy = CLAMP(pvr_y0 >> 5, 0, max_ty); + c.clip.ex = CLAMP((x1 - 1) >> 5, 0, max_tx); + c.clip.ey = CLAMP((pvr_y1 - 1) >> 5, 0, max_ty); + } + + aligned_vector_push_back(&target->vector, &c.vertex, 1); + + GPUState.scissor_rect.applied_lists |= active_list_bit; + GPUState.is_dirty = GL_TRUE; } void glStencilFunc(GLenum func, GLint ref, GLuint mask) { diff --git a/GL/texture.c b/GL/texture.c index ca761da..e0d74d4 100644 --- a/GL/texture.c +++ b/GL/texture.c @@ -1842,12 +1842,6 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat, TextureConversionFunc conversion = NULL; int needs_conversion = _determineConversion(cleanInternalFormat, format, type, &conversion); - // Hack: If we're doing a 4bpp source (via glCompressedTexture...) - // halve the srcBytes - if(format == GL_COLOR_INDEX4_EXT || format == GL_COLOR_INDEX4_TWID_KOS) { - srcBytes /= 2; - } - /* If we're packing stuff, then the dest size is half what it would be */ if((needs_conversion & CONVERSION_TYPE_PACK) == CONVERSION_TYPE_PACK) { destBytes /= 2; diff --git a/samples/polygon_compare/main.c b/samples/polygon_compare/main.c new file mode 100644 index 0000000..1c665ed --- /dev/null +++ b/samples/polygon_compare/main.c @@ -0,0 +1,159 @@ +#ifdef _arch_dreamcast +#include +#endif + +#include + +#include "GL/gl.h" +#include "GL/glkos.h" + +#define SCREEN_W 640 +#define SCREEN_H 480 + +static int check_start(void) +{ +#ifdef _arch_dreamcast + static maple_device_t *cont; + cont_state_t *state; + + if(!cont || !cont->valid) + 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; +} + +static void setup(void) +{ + glKosInit(); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + glShadeModel(GL_SMOOTH); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + glViewport(0, 0, SCREEN_W, SCREEN_H); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, SCREEN_W, SCREEN_H, 0.0, -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glDisable(GL_SCISSOR_TEST); +} + +static void rect_quad(float x0, float y0, float x1, float y1) +{ + glBegin(GL_QUADS); + glVertex2f(x0, y0); + glVertex2f(x1, y0); + glVertex2f(x1, y1); + glVertex2f(x0, y1); + glEnd(); +} + +static void rect_triangles(float x0, float y0, float x1, float y1) +{ + glBegin(GL_TRIANGLES); + glVertex2f(x0, y0); + glVertex2f(x1, y0); + glVertex2f(x1, y1); + + glVertex2f(x0, y0); + glVertex2f(x1, y1); + glVertex2f(x0, y1); + glEnd(); +} + +static void rect_polygon4(float x0, float y0, float x1, float y1) +{ + glBegin(GL_POLYGON); + glVertex2f(x0, y0); + glVertex2f(x1, y0); + glVertex2f(x1, y1); + glVertex2f(x0, y1); + glEnd(); +} + +static void octagon_points(float cx, float cy, float r) +{ + glVertex2f(cx + 0.00f * r, cy - 1.00f * r); + glVertex2f(cx + 0.71f * r, cy - 0.71f * r); + glVertex2f(cx + 1.00f * r, cy + 0.00f * r); + glVertex2f(cx + 0.71f * r, cy + 0.71f * r); + glVertex2f(cx + 0.00f * r, cy + 1.00f * r); + glVertex2f(cx - 0.71f * r, cy + 0.71f * r); + glVertex2f(cx - 1.00f * r, cy + 0.00f * r); + glVertex2f(cx - 0.71f * r, cy - 0.71f * r); +} + +static void fan8(float cx, float cy, float r) +{ + glBegin(GL_TRIANGLE_FAN); + octagon_points(cx, cy, r); + glEnd(); +} + +static void polygon8(float cx, float cy, float r) +{ + glBegin(GL_POLYGON); + octagon_points(cx, cy, r); + glEnd(); +} + +static void draw_case(float y, void (*draw)(float, float, float, float), + float x0, float y0, float x1, float y1, + GLubyte r, GLubyte g, GLubyte b) +{ + glColor4ub(r, g, b, 255); + draw(x0, y0 + y, x1, y1 + y); +} + +static void draw_scene(void) +{ + static int announced = 0; + + if(!announced) { + printf("case 1: GL_QUADS cyan\n"); + printf("case 2: GL_POLYGON(4) cyan\n"); + printf("case 3: GL_TRIANGLES cyan\n"); + printf("case 4: GL_POLYGON(4) blue\n"); + printf("case 5: GL_POLYGON(4) magenta\n"); + printf("case 6: GL_TRIANGLE_FAN(8) cyan\n"); + printf("case 7: GL_POLYGON(8) cyan\n"); + announced = 1; + } + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + draw_case(0.0f, rect_quad, 40.0f, 40.0f, 140.0f, 140.0f, 0, 255, 255); + draw_case(0.0f, rect_polygon4, 180.0f, 40.0f, 280.0f, 140.0f, 0, 255, 255); + draw_case(0.0f, rect_triangles, 320.0f, 40.0f, 420.0f, 140.0f, 0, 255, 255); + draw_case(150.0f, rect_polygon4, 40.0f, 40.0f, 140.0f, 140.0f, 0, 0, 255); + draw_case(150.0f, rect_polygon4, 180.0f, 40.0f, 280.0f, 140.0f, 255, 0, 255); + glColor4ub(0, 255, 255, 255); + fan8(360.0f, 205.0f, 42.0f); + glColor4ub(0, 255, 255, 255); + polygon8(520.0f, 205.0f, 42.0f); + + glKosSwapBuffers(); +} + +int main(void) +{ + setup(); + + while(!check_start()) { + draw_scene(); + } + + return 0; +} diff --git a/samples/scissor/main.c b/samples/scissor/main.c index 4f9ee23..671b38d 100644 --- a/samples/scissor/main.c +++ b/samples/scissor/main.c @@ -6,8 +6,30 @@ #include "GL/glu.h" #include "GL/glkos.h" +#define SCREEN_W 640 +#define SCREEN_H 480 -void InitGL(int Width, int Height) +#define SCISSOR_X 160 +#define SCISSOR_Y 128 +#define SCISSOR_W 320 +#define SCISSOR_H 224 + +static void setup_projection(int width, int height) +{ + if(height == 0) + height = 1; + + glViewport(0, 0, width, height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(45.0f, (GLfloat)width / (GLfloat)height, 0.1f, 100.0f); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +static void init_gl(int width, int height) { glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClearDepth(1.0); @@ -15,39 +37,20 @@ void InitGL(int Width, int Height) glEnable(GL_DEPTH_TEST); glShadeModel(GL_SMOOTH); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); + setup_projection(width, height); - gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f); - - glMatrixMode(GL_MODELVIEW); - - /* We cut out a square in the middle of the viewport to render to */ glEnable(GL_SCISSOR_TEST); - glScissor(160, 120, 320, 240); + glScissor(SCISSOR_X, SCISSOR_Y, SCISSOR_W, SCISSOR_H); } -/* The function called when our window is resized (which shouldn't happen, because we're fullscreen) */ -void ReSizeGLScene(int Width, int Height) +static int check_start(void) { - if (Height == 0) - Height = 1; - - glViewport(0, 0, Width, Height); - - 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; + static maple_device_t *cont; cont_state_t *state; - cont = maple_enum_type(0, MAPLE_FUNC_CONTROLLER); + if(!cont || !cont->valid) + cont = maple_enum_type(0, MAPLE_FUNC_CONTROLLER); if(cont) { state = (cont_state_t *)maple_dev_status(cont); @@ -60,14 +63,62 @@ int check_start() { return 0; } -/* The main drawing function. */ -void DrawGLScene() +static void draw_scissor_outline(void) +{ + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0.0, SCREEN_W, SCREEN_H, 0.0, -1.0, 1.0); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + { + GLfloat x = (GLfloat)SCISSOR_X; + GLfloat y = (GLfloat)(SCREEN_H - SCISSOR_Y - SCISSOR_H); + GLfloat w = (GLfloat)SCISSOR_W; + GLfloat h = (GLfloat)SCISSOR_H; + + glDisable(GL_TEXTURE_2D); + glColor4ub(255, 255, 0, 255); + + glBegin(GL_LINES); + glVertex2f(x, y); + glVertex2f(x + w, y); + + glVertex2f(x + w, y); + glVertex2f(x + w, y + h); + + glVertex2f(x + w, y + h); + glVertex2f(x, y + h); + + glVertex2f(x, y + h); + glVertex2f(x, y); + glEnd(); + } + + glPopMatrix(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + + glEnable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); +} + +static void draw_scene_geometry(void) { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef(-3.0f, 1.5f, -10.0f); + glColor4ub(255, 0, 0, 255); glBegin(GL_TRIANGLES); glVertex3f( 0.0f, 1.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 0.0f); @@ -76,7 +127,7 @@ void DrawGLScene() glTranslatef(3.0f, 0.0f, 0.0f); - + glColor4ub(0, 255, 0, 255); glBegin(GL_QUADS); glVertex3f(-1.0f, 1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 0.0f); @@ -86,6 +137,7 @@ void DrawGLScene() glTranslatef(3.0f, 0.0f, 0.0f); + glColor4ub(0, 0, 255, 255); glBegin(GL_POLYGON); glVertex3f(-0.0f, 1.0f, 0.0f); glVertex3f(-0.75f, 0.75f, 0.0f); @@ -99,8 +151,8 @@ void DrawGLScene() glTranslatef(-6.0f, -3.0f, 0.0f); - - glBegin(GL_POLYGON); + glColor4ub(255, 128, 0, 255); + glBegin(GL_TRIANGLES); glVertex3f( 0.0f, 1.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 0.0f); @@ -108,7 +160,7 @@ void DrawGLScene() glTranslatef(3.0f, 0.0f, 0.0f); - + glColor4ub(0, 255, 255, 255); glBegin(GL_POLYGON); glVertex3f(-1.0f, 1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 0.0f); @@ -118,6 +170,7 @@ void DrawGLScene() glTranslatef(3.0f, 0.0f, 0.0f); + glColor4ub(255, 0, 255, 255); glBegin(GL_POLYGON); glVertex3f(-0.0f, 1.0f, 0.0f); glVertex3f(-0.75f, 0.75f, 0.0f); @@ -128,22 +181,36 @@ void DrawGLScene() glVertex3f( 1.0f, 0.0f, 0.0f); glVertex3f( 0.75f, 0.75f, 0.0f); glEnd(); +} + +static void draw_gl_scene(void) +{ + glDisable(GL_SCISSOR_TEST); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + draw_scissor_outline(); + + glEnable(GL_SCISSOR_TEST); + glScissor(SCISSOR_X, SCISSOR_Y, SCISSOR_W, SCISSOR_H); + + draw_scene_geometry(); glKosSwapBuffers(); } int main(int argc, char **argv) { - glKosInit(); + (void)argc; + (void)argv; - InitGL(640, 480); - ReSizeGLScene(640, 480); + glKosInit(); + init_gl(SCREEN_W, SCREEN_H); while(1) { if(check_start()) break; - DrawGLScene(); + draw_gl_scene(); } return 0; diff --git a/tests/test_texture_formats.h b/tests/test_texture_formats.h index 0e9c0e0..cf789b8 100644 --- a/tests/test_texture_formats.h +++ b/tests/test_texture_formats.h @@ -251,6 +251,63 @@ public: assert_is_not_null(t->palette); } + void test_packed_color_index4_upload_uses_packed_source_size() { + std::vector img((8 * 8) / 2, 0xaa); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_COLOR_INDEX4_EXT, 8, 8, 0, + GL_COLOR_INDEX4_EXT, GL_UNSIGNED_BYTE, img.data()); + assert_equal(glGetError(), GL_NO_ERROR); + + TextureObject* t = _glGetBoundTexture(); + assert_true(t->isPaletted); + assert_equal(t->internalFormat, GL_COLOR_INDEX4_TWID_KOS); + assert_equal(t->baseDataSize, (GLuint)((8 * 8) / 2)); + } + + void test_packed_color_index4_upload_respects_unpack_row_length() { + std::vector img(((12 * 8) + 1) / 2, 0xaa); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 12); + glTexImage2D(GL_TEXTURE_2D, 0, GL_COLOR_INDEX4_EXT, 8, 8, 0, + GL_COLOR_INDEX4_EXT, GL_UNSIGNED_BYTE, img.data()); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + assert_equal(glGetError(), GL_NO_ERROR); + + TextureObject* t = _glGetBoundTexture(); + assert_true(t->isPaletted); + assert_equal(t->internalFormat, GL_COLOR_INDEX4_TWID_KOS); + assert_equal(t->baseDataSize, (GLuint)((8 * 8) / 2)); + } + + void test_compressed_palette4_upload_uses_packed_source_size() { + std::vector img((16 * 3) + ((8 * 8) / 2), 0xaa); + + glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, GL_PALETTE4_RGB8_OES, + 8, 8, 0, (GLsizei)img.size(), img.data()); + assert_equal(glGetError(), GL_NO_ERROR); + + TextureObject* t = _glGetBoundTexture(); + assert_true(t->isPaletted); + assert_equal(t->internalFormat, GL_COLOR_INDEX4_TWID_KOS); + assert_equal(t->baseDataSize, (GLuint)((8 * 8) / 2)); + assert_is_not_null(t->palette); + } + + void test_color_index8_source_can_pack_to_color_index4() { + std::vector img(8 * 8, 0x0a); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_COLOR_INDEX4_EXT, 8, 8, 0, + GL_COLOR_INDEX, GL_UNSIGNED_BYTE, img.data()); + assert_equal(glGetError(), GL_NO_ERROR); + + TextureObject* t = _glGetBoundTexture(); + assert_true(t->isPaletted); + assert_equal(t->internalFormat, GL_COLOR_INDEX4_TWID_KOS); + assert_equal(t->baseDataSize, (GLuint)((8 * 8) / 2)); + } + /* ------------------------------------------------- Error handling */ void test_non_power_of_two_width_raises_invalid_value() {