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
This commit is contained in:
GPF 2026-07-03 09:37:12 -07:00
parent 862bcf70d1
commit f11f7c966a
14 changed files with 424 additions and 77 deletions

View File

@ -181,6 +181,7 @@ if(BUILD_SAMPLES)
gen_sample(zclip_triangle samples/zclip_triangle/main.c) gen_sample(zclip_triangle samples/zclip_triangle/main.c)
gen_sample(zclip_trianglestrip samples/zclip_trianglestrip/main.c) gen_sample(zclip_trianglestrip samples/zclip_trianglestrip/main.c)
gen_sample(scissor samples/scissor/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(polymark samples/polymark/main.c)
gen_sample(cubes samples/cubes/main.cpp) gen_sample(cubes samples/cubes/main.cpp)
gen_sample(zclip_test tests/zclip/main.cpp) gen_sample(zclip_test tests/zclip/main.cpp)

View File

@ -867,8 +867,9 @@ GL_FORCE_INLINE void submitVertices(GLenum mode, GLsizei first, GLuint count, GL
target->output = _glActivePolyList(); target->output = _glActivePolyList();
gl_assert(target->output); 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(); GLboolean header_required = (vector_size == 0) || _glGPUStateIsDirty();
target->count = calcFinalVertices(mode, count); target->count = calcFinalVertices(mode, count);

View File

@ -141,5 +141,5 @@ void APIENTRY glKosSwapBuffers() {
aligned_vector_clear(&PT_LIST.vector); aligned_vector_clear(&PT_LIST.vector);
aligned_vector_clear(&TR_LIST.vector); aligned_vector_clear(&TR_LIST.vector);
_glApplyScissor(true); _glInvalidateScissor();
} }

View File

@ -390,6 +390,7 @@ static inline int DimensionFlag(const int w) {
static uint8_t* DEFAULT_TEXTURE = NULL; static uint8_t* DEFAULT_TEXTURE = NULL;
extern void* GPUMemoryAlloc(size_t size); 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 */ /* Compile a polygon context into a polygon header */
static inline void CompilePolyHeader(PolyHeader *dst, const PolyContext *src) { 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.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->mode2 |= (src->gen.alpha << GPU_TA_PM2_ALPHA_SHIFT) & GPU_TA_PM2_ALPHA_MASK;
dst->mode3 = 0;
if(src->txr.enable == GPU_TEXTURE_DISABLE) { if(src->txr.enable == GPU_TEXTURE_DISABLE) {
/* Texturing was disabled so we simulate it with a white texture */ /* Texturing was disabled so we simulate it with a white texture */
if(!DEFAULT_TEXTURE) { if(!DEFAULT_TEXTURE) {
uint16_t texture[8 * 8];
DEFAULT_TEXTURE = (uint8_t*) GPUMemoryAlloc(8 * 8 * 2); DEFAULT_TEXTURE = (uint8_t*) GPUMemoryAlloc(8 * 8 * 2);
for(int i = 0; i < 8 * 8 * 2; ++i) { for(int i = 0; i < 8 * 8; ++i) {
DEFAULT_TEXTURE[i] = 0xff; 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_USIZE_SHIFT) & GPU_TA_PM2_USIZE_MASK;
dst->mode2 |= (DimensionFlag(8) << GPU_TA_PM2_VSIZE_SHIFT) & GPU_TA_PM2_VSIZE_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->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 */ /* Convert the texture address */
txr_base = (uint32_t) DEFAULT_TEXTURE; txr_base = (uint32_t) DEFAULT_TEXTURE;
txr_base = (txr_base & 0x00fffff8) >> 3; txr_base = (txr_base & 0x00fffff8) >> 3;

View File

@ -20,6 +20,10 @@ void* GPUMemoryAlloc(size_t size) {
return pvr_mem_malloc(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) { GL_FORCE_INLINE bool glIsVertex(const float flags) {
return flags == GPU_CMD_VERTEX_EOL || flags == GPU_CMD_VERTEX; return flags == GPU_CMD_VERTEX_EOL || flags == GPU_CMD_VERTEX;
} }
@ -199,7 +203,15 @@ void SceneListSubmit(Vertex* vertices, int n) {
Vertex* v0 = vertices; Vertex* v0 = vertices;
for(int i = 0; i < n - 1; ++i, ++v0) { 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)) { if(is_header(v0)) {
SUBMIT_QUEUED_VERTEX(GPU_CMD_VERTEX_EOL);
PolyHeader* header = (PolyHeader*) v0; PolyHeader* header = (PolyHeader*) v0;
if(header->meta.texture_is_strided && header->meta.texture_stride != CURRENT_TEXTURE_STRIDE) { if(header->meta.texture_is_strided && header->meta.texture_stride != CURRENT_TEXTURE_STRIDE) {
_glPVRSetTextureStride(header->meta.texture_stride); _glPVRSetTextureStride(header->meta.texture_stride);

View File

@ -167,6 +167,7 @@ static inline size_t GPUMemoryAvailable() {
} }
void* GPUMemoryAlloc(size_t size); void* GPUMemoryAlloc(size_t size);
void GPUTextureLoad(const void* src, void* dst, size_t size);
static inline void GPUSetPaletteFormat(GPUPaletteFormat format) { static inline void GPUSetPaletteFormat(GPUPaletteFormat format) {
pvr_set_pal_format((pvr_palfmt_t) format); pvr_set_pal_format((pvr_palfmt_t) format);
} }

View File

@ -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) { void GPUSetPaletteFormat(GPUPaletteFormat format) {
} }

View File

@ -62,6 +62,7 @@ enum GPUPaletteFormat;
size_t GPUMemoryAvailable(); size_t GPUMemoryAvailable();
void* GPUMemoryAlloc(size_t size); void* GPUMemoryAlloc(size_t size);
void GPUTextureLoad(const void* src, void* dst, size_t size);
void GPUSetPaletteFormat(GPUPaletteFormat format); void GPUSetPaletteFormat(GPUPaletteFormat format);
void GPUSetPaletteEntry(uint32_t idx, uint32_t value); void GPUSetPaletteEntry(uint32_t idx, uint32_t value);
@ -73,4 +74,3 @@ void GPUSetFogLinear(float start, float end);
void GPUSetFogExp(float density); void GPUSetFogExp(float density);
void GPUSetFogExp2(float density); void GPUSetFogExp2(float density);
void GPUSetFogColor(float r, float g, float b, float a); void GPUSetFogColor(float r, float g, float b, float a);

View File

@ -441,6 +441,7 @@ GLuint _glUsedTextureMemory();
GLuint _glFreeContiguousTextureMemory(); GLuint _glFreeContiguousTextureMemory();
void _glApplyScissor(bool force); void _glApplyScissor(bool force);
void _glInvalidateScissor(void);
void _glSetColorMaterialMask(GLenum mask); void _glSetColorMaterialMask(GLenum mask);
void _glSetColorMaterialMode(GLenum mode); void _glSetColorMaterialMode(GLenum mode);
GLenum _glColorMaterialMode(); GLenum _glColorMaterialMode();

View File

@ -5,6 +5,10 @@
#include "../include/GL/glext.h" #include "../include/GL/glext.h"
#include "private.h" #include "private.h"
#define SCISSOR_APPLIED_OP 0x1u
#define SCISSOR_APPLIED_PT 0x2u
#define SCISSOR_APPLIED_TR 0x4u
static struct { static struct {
GLboolean is_dirty; GLboolean is_dirty;
@ -33,7 +37,7 @@ static struct {
GLint y; GLint y;
GLsizei width; GLsizei width;
GLsizei height; GLsizei height;
GLboolean applied; unsigned int applied_lists;
} scissor_rect; } scissor_rect;
GLenum blend_sfactor; GLenum blend_sfactor;
@ -87,7 +91,7 @@ static struct {
.scissor_test_enabled = GL_FALSE, .scissor_test_enabled = GL_FALSE,
.fog_enabled = GL_FALSE, .fog_enabled = GL_FALSE,
.depth_mask_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_sfactor = GL_ONE,
.blend_dfactor = GL_ZERO, .blend_dfactor = GL_ZERO,
.blend_enabled = GL_FALSE, .blend_enabled = GL_FALSE,
@ -553,6 +557,7 @@ GLAPI void APIENTRY glEnable(GLenum cap) {
case GL_SCISSOR_TEST: { case GL_SCISSOR_TEST: {
if(GPUState.scissor_test_enabled != GL_TRUE) { if(GPUState.scissor_test_enabled != GL_TRUE) {
GPUState.scissor_test_enabled = GL_TRUE; GPUState.scissor_test_enabled = GL_TRUE;
GPUState.scissor_rect.applied_lists = 0;
GPUState.is_dirty = GL_TRUE; GPUState.is_dirty = GL_TRUE;
} }
} break; } break;
@ -670,6 +675,7 @@ GLAPI void APIENTRY glDisable(GLenum cap) {
case GL_SCISSOR_TEST: { case GL_SCISSOR_TEST: {
if(GPUState.scissor_test_enabled != GL_FALSE) { if(GPUState.scissor_test_enabled != GL_FALSE) {
GPUState.scissor_test_enabled = GL_FALSE; GPUState.scissor_test_enabled = GL_FALSE;
GPUState.scissor_rect.applied_lists = 0;
GPUState.is_dirty = GL_TRUE; GPUState.is_dirty = GL_TRUE;
} }
} break; } break;
@ -953,10 +959,8 @@ void APIENTRY glScissor(GLint x, GLint y, GLsizei width, GLsizei height) {
GPUState.scissor_rect.y = y; GPUState.scissor_rect.y = y;
GPUState.scissor_rect.width = width; GPUState.scissor_rect.width = width;
GPUState.scissor_rect.height = height; 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? GPUState.is_dirty = GL_TRUE; // FIXME: do we need this?
_glApplyScissor(false);
} }
/* Setup the hardware user clip rectangle. /* 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 at the right place, either when enabling the scissor test, or
when the scissor test changes. 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) { 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) { if(!GPUState.scissor_test_enabled) {
return; return;
} }
/* Don't apply if we already applied - nothing changed */ PolyList* target = _glActivePolyList();
if(GPUState.scissor_rect.applied && !force) { 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; return;
} }
PVRTileClipCommand c; typedef union {
Vertex vertex;
GLint miny, maxx, maxy; PVRTileClipCommand clip;
} ScissorCommand;
ScissorCommand c = {0};
const VideoMode* vid_mode = GetVideoMode(); const VideoMode* vid_mode = GetVideoMode();
GLsizei scissor_width = MAX(MIN(GPUState.scissor_rect.width, vid_mode->width), 0); GLint x0 = GPUState.scissor_rect.x;
GLsizei scissor_height = MAX(MIN(GPUState.scissor_rect.height, vid_mode->height), 0); 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; * glScissor uses OpenGL window coordinates: lower-left origin,
maxx = (scissor_width + GPUState.scissor_rect.x); * x/y inclusive start, width/height extent. Clip to the framebuffer
maxy = (scissor_height + miny); * 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 vw = vid_mode->width >> 5;
uint16_t vh = vid_mode->height >> 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); /* load command structure while mapping screen coords to TA tiles */
c.sy = CLAMP(miny >> 5, 0, vh); c.clip.flags = GPU_CMD_USERCLIP;
c.ex = CLAMP((maxx >> 5) - 1, 0, vw); c.clip.d1 = c.clip.d2 = c.clip.d3 = 0;
c.ey = CLAMP((maxy >> 5) - 1, 0, vh);
aligned_vector_push_back(&_glOpaquePolyList()->vector, &c, 1); if(x1 <= x0 || y1 <= y0) {
aligned_vector_push_back(&_glPunchThruPolyList()->vector, &c, 1); c.clip.sx = 0;
aligned_vector_push_back(&_glTransparentPolyList()->vector, &c, 1); 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) { void glStencilFunc(GLenum func, GLint ref, GLuint mask) {

View File

@ -1842,12 +1842,6 @@ void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalFormat,
TextureConversionFunc conversion = NULL; TextureConversionFunc conversion = NULL;
int needs_conversion = _determineConversion(cleanInternalFormat, format, type, &conversion); 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 we're packing stuff, then the dest size is half what it would be */
if((needs_conversion & CONVERSION_TYPE_PACK) == CONVERSION_TYPE_PACK) { if((needs_conversion & CONVERSION_TYPE_PACK) == CONVERSION_TYPE_PACK) {
destBytes /= 2; destBytes /= 2;

View File

@ -0,0 +1,159 @@
#ifdef _arch_dreamcast
#include <kos.h>
#endif
#include <stdio.h>
#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;
}

View File

@ -6,8 +6,30 @@
#include "GL/glu.h" #include "GL/glu.h"
#include "GL/glkos.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); glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0); glClearDepth(1.0);
@ -15,39 +37,20 @@ void InitGL(int Width, int Height)
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH); glShadeModel(GL_SMOOTH);
glMatrixMode(GL_PROJECTION); setup_projection(width, height);
glLoadIdentity();
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); 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) */ static int check_start(void)
void ReSizeGLScene(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);
}
int check_start() {
#ifdef _arch_dreamcast #ifdef _arch_dreamcast
maple_device_t *cont; static maple_device_t *cont;
cont_state_t *state; 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) { if(cont) {
state = (cont_state_t *)maple_dev_status(cont); state = (cont_state_t *)maple_dev_status(cont);
@ -60,14 +63,62 @@ int check_start() {
return 0; return 0;
} }
/* The main drawing function. */ static void draw_scissor_outline(void)
void DrawGLScene() {
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(); glLoadIdentity();
glTranslatef(-3.0f, 1.5f, -10.0f); glTranslatef(-3.0f, 1.5f, -10.0f);
glColor4ub(255, 0, 0, 255);
glBegin(GL_TRIANGLES); glBegin(GL_TRIANGLES);
glVertex3f( 0.0f, 1.0f, 0.0f); glVertex3f( 0.0f, 1.0f, 0.0f);
glVertex3f( 1.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); glTranslatef(3.0f, 0.0f, 0.0f);
glColor4ub(0, 255, 0, 255);
glBegin(GL_QUADS); glBegin(GL_QUADS);
glVertex3f(-1.0f, 1.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 0.0f);
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); glTranslatef(3.0f, 0.0f, 0.0f);
glColor4ub(0, 0, 255, 255);
glBegin(GL_POLYGON); glBegin(GL_POLYGON);
glVertex3f(-0.0f, 1.0f, 0.0f); glVertex3f(-0.0f, 1.0f, 0.0f);
glVertex3f(-0.75f, 0.75f, 0.0f); glVertex3f(-0.75f, 0.75f, 0.0f);
@ -99,8 +151,8 @@ void DrawGLScene()
glTranslatef(-6.0f, -3.0f, 0.0f); glTranslatef(-6.0f, -3.0f, 0.0f);
glColor4ub(255, 128, 0, 255);
glBegin(GL_POLYGON); glBegin(GL_TRIANGLES);
glVertex3f( 0.0f, 1.0f, 0.0f); glVertex3f( 0.0f, 1.0f, 0.0f);
glVertex3f( 1.0f,-1.0f, 0.0f); glVertex3f( 1.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); glTranslatef(3.0f, 0.0f, 0.0f);
glColor4ub(0, 255, 255, 255);
glBegin(GL_POLYGON); glBegin(GL_POLYGON);
glVertex3f(-1.0f, 1.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 0.0f);
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); glTranslatef(3.0f, 0.0f, 0.0f);
glColor4ub(255, 0, 255, 255);
glBegin(GL_POLYGON); glBegin(GL_POLYGON);
glVertex3f(-0.0f, 1.0f, 0.0f); glVertex3f(-0.0f, 1.0f, 0.0f);
glVertex3f(-0.75f, 0.75f, 0.0f); glVertex3f(-0.75f, 0.75f, 0.0f);
@ -128,22 +181,36 @@ void DrawGLScene()
glVertex3f( 1.0f, 0.0f, 0.0f); glVertex3f( 1.0f, 0.0f, 0.0f);
glVertex3f( 0.75f, 0.75f, 0.0f); glVertex3f( 0.75f, 0.75f, 0.0f);
glEnd(); 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(); glKosSwapBuffers();
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
glKosInit(); (void)argc;
(void)argv;
InitGL(640, 480); glKosInit();
ReSizeGLScene(640, 480); init_gl(SCREEN_W, SCREEN_H);
while(1) { while(1) {
if(check_start()) if(check_start())
break; break;
DrawGLScene(); draw_gl_scene();
} }
return 0; return 0;

View File

@ -251,6 +251,63 @@ public:
assert_is_not_null(t->palette); assert_is_not_null(t->palette);
} }
void test_packed_color_index4_upload_uses_packed_source_size() {
std::vector<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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 */ /* ------------------------------------------------- Error handling */
void test_non_power_of_two_width_raises_invalid_value() { void test_non_power_of_two_width_raises_invalid_value() {