From f03f0e8f0464ecce966a9f422e5d11873ee80e39 Mon Sep 17 00:00:00 2001 From: Luke Benstead Date: Wed, 14 Apr 2021 21:38:33 +0100 Subject: [PATCH] Implement glScissor correctly --- CMakeLists.txt | 1 + GL/flush.c | 10 ++++ GL/private.h | 4 ++ GL/state.c | 90 ++++++++++++++++++++++++----- samples/scissor/main.c | 126 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 217 insertions(+), 14 deletions(-) create mode 100644 samples/scissor/main.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 991435f..e3ec63a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,6 +139,7 @@ gen_sample(terrain samples/terrain/main.c) gen_sample(zclip samples/zclip/main.c) 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) if(PLATFORM_DREAMCAST) gen_sample(polymark samples/polymark/main.c) diff --git a/GL/flush.c b/GL/flush.c index 8829533..197dee9 100644 --- a/GL/flush.c +++ b/GL/flush.c @@ -18,6 +18,14 @@ PolyList* _glActivePolyList() { } } +PolyList* _glOpaquePolyList() { + return &OP_LIST; +} + +PolyList* _glPunchThruPolyList() { + return &PT_LIST; +} + PolyList *_glTransparentPolyList() { return &TR_LIST; } @@ -105,6 +113,8 @@ void APIENTRY glKosSwapBuffers() { aligned_vector_clear(&PT_LIST.vector); aligned_vector_clear(&TR_LIST.vector); + _glApplyScissor(true); + profiler_checkpoint("scene"); profiler_pop(); diff --git a/GL/private.h b/GL/private.h index 2908508..48a8cae 100644 --- a/GL/private.h +++ b/GL/private.h @@ -265,6 +265,8 @@ float _glClipLineToNearZ(const Vertex* v1, const Vertex* v2, Vertex* vout); void _glClipTriangleStrip(SubmissionTarget* target, uint8_t fladeShade); PolyList *_glActivePolyList(); +PolyList* _glOpaquePolyList(); +PolyList* _glPunchThruPolyList(); PolyList *_glTransparentPolyList(); void _glInitAttributePointers(); @@ -356,6 +358,8 @@ GLuint _glFreeTextureMemory(); GLuint _glUsedTextureMemory(); GLuint _glFreeContiguousTextureMemory(); +void _glApplyScissor(bool force); + #define MAX_TEXTURE_UNITS 2 #define MAX_LIGHTS 8 diff --git a/GL/state.c b/GL/state.c index 9487e6a..908a282 100644 --- a/GL/state.c +++ b/GL/state.c @@ -29,10 +29,22 @@ static GLboolean POLYGON_OFFSET_ENABLED = GL_FALSE; static GLboolean NORMALIZE_ENABLED = GL_FALSE; +static struct { + GLint x; + GLint y; + GLsizei width; + GLsizei height; + GLboolean applied; +} SCISSOR_RECT = { + 0, 0, 640, 480, false +}; + GLboolean _glIsSharedTexturePaletteEnabled() { return SHARED_PALETTE_ENABLED; } +void _glApplyScissor(bool force); + static int _calc_pvr_face_culling() { if(!CULLING_ENABLED) { return GPU_CULLING_NONE; @@ -261,6 +273,13 @@ void _glInitContext() { GL_CONTEXT.fmt.uv = GPU_UVFMT_32BIT; GL_CONTEXT.gen.color_clamp = GPU_CLRCLAMP_DISABLE; + const VideoMode* mode = GetVideoMode(); + + SCISSOR_RECT.x = 0; + SCISSOR_RECT.y = 0; + SCISSOR_RECT.width = mode->width; + SCISSOR_RECT.height = mode->height; + glClearDepth(1.0f); glDepthFunc(GL_LESS); glDepthMask(GL_TRUE); @@ -302,6 +321,7 @@ GLAPI void APIENTRY glEnable(GLenum cap) { } break; case GL_SCISSOR_TEST: { GL_CONTEXT.gen.clip_mode = GPU_USERCLIP_INSIDE; + _glApplyScissor(false); } break; case GL_LIGHTING: { LIGHTING_ENABLED = GL_TRUE; @@ -537,6 +557,23 @@ void glPixelStorei(GLenum pname, GLint param) { } +void APIENTRY glScissor(GLint x, GLint y, GLsizei width, GLsizei height) { + if(SCISSOR_RECT.x == x && + SCISSOR_RECT.y == y && + SCISSOR_RECT.width == width && + SCISSOR_RECT.height == height) { + return; + } + + SCISSOR_RECT.x = x; + SCISSOR_RECT.y = y; + SCISSOR_RECT.width = width; + SCISSOR_RECT.height = height; + SCISSOR_RECT.applied = false; + + _glApplyScissor(false); +} + /* Setup the hardware user clip rectangle. The minimum clip rectangle is a 32x32 area which is dependent on the tile @@ -549,30 +586,55 @@ void glPixelStorei(GLenum pname, GLint param) { glScissor(0, 0, 32, 32) allows only the 'tile' in the lower left hand corner of the screen to be modified and glScissor(0, 0, 0, 0) disallows modification to all 'tiles' on the screen. + + We call this in the following situations: + + - glEnable(GL_SCISSOR_TEST) is called + - glScissor() is called + - After glKosSwapBuffers() + + This ensures that a clip command is added to every vertex list + at the right place, either when enabling the scissor test, or + when the scissor test changes. */ -void APIENTRY glScissor(GLint x, GLint y, GLsizei width, GLsizei height) { - /*!!! FIXME: Shouldn't this be added to *all* lists? */ - PVRTileClipCommand *c = aligned_vector_extend(&_glActivePolyList()->vector, 1); +void _glApplyScissor(bool force) { + /* Don't do anyting if clipping is disabled */ + if(GL_CONTEXT.gen.clip_mode == GPU_USERCLIP_DISABLE) { + return; + } + + /* Don't apply if we already applied - nothing changed */ + if(SCISSOR_RECT.applied && !force) { + return; + } + + PVRTileClipCommand c; GLint miny, maxx, maxy; const VideoMode* vid_mode = GetVideoMode(); - GLsizei gl_scissor_width = MAX( MIN(width, vid_mode->width), 0 ); - GLsizei gl_scissor_height = MAX( MIN(height, vid_mode->height), 0 ); + GLsizei scissor_width = MAX(MIN(SCISSOR_RECT.width, vid_mode->width), 0); + GLsizei scissor_height = MAX(MIN(SCISSOR_RECT.height, vid_mode->height), 0); /* force the origin to the lower left-hand corner of the screen */ - miny = (vid_mode->height - gl_scissor_height) - y; - maxx = (gl_scissor_width + x); - maxy = (gl_scissor_height + miny); + miny = (vid_mode->height - scissor_height) - SCISSOR_RECT.y; + maxx = (scissor_width + SCISSOR_RECT.x); + maxy = (scissor_height + miny); /* load command structure while mapping screen coords to TA tiles */ - c->flags = GPU_CMD_USERCLIP; - c->d1 = c->d2 = c->d3 = 0; - c->sx = CLAMP(x / 32, 0, vid_mode->width / 32); - c->sy = CLAMP(miny / 32, 0, vid_mode->height / 32); - c->ex = CLAMP((maxx / 32) - 1, 0, vid_mode->width / 32); - c->ey = CLAMP((maxy / 32) - 1, 0, vid_mode->height / 32); + c.flags = GPU_CMD_USERCLIP; + c.d1 = c.d2 = c.d3 = 0; + c.sx = CLAMP(SCISSOR_RECT.x / 32, 0, vid_mode->width / 32); + c.sy = CLAMP(miny / 32, 0, vid_mode->height / 32); + c.ex = CLAMP((maxx / 32) - 1, 0, vid_mode->width / 32); + c.ey = CLAMP((maxy / 32) - 1, 0, vid_mode->height / 32); + + aligned_vector_push_back(&_glOpaquePolyList()->vector, &c, 1); + aligned_vector_push_back(&_glPunchThruPolyList()->vector, &c, 1); + aligned_vector_push_back(&_glTransparentPolyList()->vector, &c, 1); + + SCISSOR_RECT.applied = true; } GLboolean APIENTRY glIsEnabled(GLenum cap) { diff --git a/samples/scissor/main.c b/samples/scissor/main.c new file mode 100644 index 0000000..f5802d4 --- /dev/null +++ b/samples/scissor/main.c @@ -0,0 +1,126 @@ +#include "GL/gl.h" +#include "GL/glu.h" +#include "GL/glkos.h" + + +void InitGL(int Width, int Height) +{ + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClearDepth(1.0); + glDepthFunc(GL_LEQUAL); + glEnable(GL_DEPTH_TEST); + glShadeModel(GL_SMOOTH); + + glMatrixMode(GL_PROJECTION); + 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); + glScissor(160, 120, 320, 240); +} + +/* 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) + 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); +} + + +/* The main drawing function. */ +void DrawGLScene() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + glTranslatef(-3.0f, 1.5f, -10.0f); + + glBegin(GL_TRIANGLES); + glVertex3f( 0.0f, 1.0f, 0.0f); + glVertex3f( 1.0f,-1.0f, 0.0f); + glVertex3f(-1.0f,-1.0f, 0.0f); + glEnd(); + + glTranslatef(3.0f, 0.0f, 0.0f); + + + 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); + glEnd(); + + glTranslatef(3.0f, 0.0f, 0.0f); + + glBegin(GL_POLYGON); + glVertex3f(-0.0f, 1.0f, 0.0f); + glVertex3f(-0.75f, 0.75f, 0.0f); + glVertex3f(-1.0f, 0.0f, 0.0f); + glVertex3f(-0.75f,-0.75f, 0.0f); + glVertex3f(-0.0f,-1.0f, 0.0f); + glVertex3f( 0.75f,-0.75f, 0.0f); + glVertex3f( 1.0f, 0.0f, 0.0f); + glVertex3f( 0.75f, 0.75f, 0.0f); + glEnd(); + + glTranslatef(-6.0f, -3.0f, 0.0f); + + + glBegin(GL_POLYGON); + glVertex3f( 0.0f, 1.0f, 0.0f); + glVertex3f( 1.0f,-1.0f, 0.0f); + glVertex3f(-1.0f,-1.0f, 0.0f); + glEnd(); + + glTranslatef(3.0f, 0.0f, 0.0f); + + + 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); + glEnd(); + + glTranslatef(3.0f, 0.0f, 0.0f); + + glBegin(GL_POLYGON); + glVertex3f(-0.0f, 1.0f, 0.0f); + glVertex3f(-0.75f, 0.75f, 0.0f); + glVertex3f(-1.0f, 0.0f, 0.0f); + glVertex3f(-0.75f,-0.75f, 0.0f); + glVertex3f(-0.0f,-1.0f, 0.0f); + glVertex3f( 0.75f,-0.75f, 0.0f); + glVertex3f( 1.0f, 0.0f, 0.0f); + glVertex3f( 0.75f, 0.75f, 0.0f); + glEnd(); + + glKosSwapBuffers(); +} + +int main(int argc, char **argv) +{ + glKosInit(); + + InitGL(640, 480); + ReSizeGLScene(640, 480); + + while(1) { + DrawGLScene(); + } + + return 0; +}