diff --git a/src/renderer/api.c b/src/renderer/api.c index 45a7206e..2a1fbce4 100644 --- a/src/renderer/api.c +++ b/src/renderer/api.c @@ -720,6 +720,19 @@ void r_vertex_array_layout(VertexArray *varr, uint nattribs, VertexAttribFormat B.vertex_array_layout(varr, nattribs, attribs); } +void r_scissor(int x, int y, int w, int h) { + _r_state_touch_scissor(); + B.scissor((IntRect) { x, y, w, h }); +} + +void r_scissor_rect(IntRect scissor) { + B.scissor(scissor); +} + +void r_scissor_current(IntRect *scissor) { + B.scissor_current(scissor); +} + void r_vsync(VsyncMode mode) { B.vsync(mode); } diff --git a/src/renderer/api.h b/src/renderer/api.h index 43b60562..77834ce6 100644 --- a/src/renderer/api.h +++ b/src/renderer/api.h @@ -793,6 +793,11 @@ void r_vertex_array_attach_index_buffer(VertexArray *varr, IndexBuffer *ibuf) at IndexBuffer* r_vertex_array_get_index_attachment(VertexArray *varr) attr_nonnull(1); void r_vertex_array_layout(VertexArray *varr, uint nattribs, VertexAttribFormat attribs[nattribs]) attr_nonnull(1, 3); +// NOTE: setting all parameters to 0 disables the scissor test +void r_scissor(int x, int y, int w, int h); +void r_scissor_rect(IntRect scissor); +void r_scissor_current(IntRect *scissor) attr_nonnull(1); + void r_model_add_static(Model *out_mdl, Primitive prim, size_t num_vertices, GenericModelVertex vertices[num_vertices], size_t num_indices, uint indices[num_indices]); const Model *r_model_get_quad(void) attr_returns_nonnull; diff --git a/src/renderer/common/backend.h b/src/renderer/common/backend.h index 9d98acb2..f4e17dca 100644 --- a/src/renderer/common/backend.h +++ b/src/renderer/common/backend.h @@ -121,6 +121,9 @@ typedef struct RendererFuncs { VertexBuffer* (*vertex_array_get_vertex_attachment)(VertexArray *varr, uint attachment); IndexBuffer* (*vertex_array_get_index_attachment)(VertexArray *varr); + void (*scissor)(IntRect scissor); + void (*scissor_current)(IntRect *scissor); + void (*vsync)(VsyncMode mode); VsyncMode (*vsync_current)(void); diff --git a/src/renderer/common/state.c b/src/renderer/common/state.c index b7f4409f..b4eb56aa 100644 --- a/src/renderer/common/state.c +++ b/src/renderer/common/state.c @@ -85,6 +85,10 @@ void r_state_pop(void) { B.framebuffer(S.framebuffer); } + RESTORE(RSTATE_SCISSOR) { + B.scissor(S.scissor); + } + if(_r_state.head == _r_state.stack) { _r_state.head = NULL; } else { @@ -136,3 +140,9 @@ void _r_state_touch_framebuffer(void) { S.framebuffer = B.framebuffer_current(); }); } + +void _r_state_touch_scissor(void) { + TAINT(RSTATE_SCISSOR, { + B.scissor_current(&S.scissor); + }); +} diff --git a/src/renderer/common/state.h b/src/renderer/common/state.h index 330bf0ad..d6f32ca3 100644 --- a/src/renderer/common/state.h +++ b/src/renderer/common/state.h @@ -20,6 +20,7 @@ RSTATE(SHADER) \ RSTATE(SHADER_UNIFORMS) \ RSTATE(RENDERTARGET) \ + RSTATE(SCISSOR) \ typedef enum RendererStateID { #define RSTATE(id) RSTATE_ID_##id, @@ -47,6 +48,7 @@ typedef struct RendererStateRollback { ShaderProgram *shader; // TODO uniforms Framebuffer *framebuffer; + IntRect scissor; } RendererStateRollback; void _r_state_touch_capabilities(void); @@ -57,6 +59,7 @@ void _r_state_touch_depth_func(void); void _r_state_touch_shader(void); void _r_state_touch_uniform(Uniform *uniform); void _r_state_touch_framebuffer(void); +void _r_state_touch_scissor(void); void _r_state_init(void); void _r_state_shutdown(void); diff --git a/src/renderer/gl33/framebuffer.c b/src/renderer/gl33/framebuffer.c index 8c16a7f5..8962d526 100644 --- a/src/renderer/gl33/framebuffer.c +++ b/src/renderer/gl33/framebuffer.c @@ -181,6 +181,7 @@ void gl33_framebuffer_clear(Framebuffer *framebuffer, ClearBufferFlags flags, co Framebuffer *fb_saved = r_framebuffer_current(); r_framebuffer(framebuffer); gl33_sync_framebuffer(); + gl33_sync_scissor(); glClear(glflags); r_framebuffer(fb_saved); } diff --git a/src/renderer/gl33/gl33.c b/src/renderer/gl33/gl33.c index ae1d8de6..7dfffa0f 100644 --- a/src/renderer/gl33/gl33.c +++ b/src/renderer/gl33/gl33.c @@ -106,6 +106,12 @@ static struct { FloatRect default_framebuffer; } viewport; + struct { + IntRect pending; + IntRect active; + bool active_enabled; + } scissor; + Color color; Color clear_color; float clear_depth; @@ -379,7 +385,7 @@ static void gl33_apply_capability(RendererCapability cap, bool value) { } } -static void transform_viewport_origin(Framebuffer *fb, FloatRect *vp) { +static int get_framebuffer_height(Framebuffer *fb) { int fb_height = 0; if(fb == NULL) { @@ -396,6 +402,11 @@ static void transform_viewport_origin(Framebuffer *fb, FloatRect *vp) { } assert(fb_height > 0); + return fb_height; +} + +static void transform_viewport_origin(Framebuffer *fb, FloatRect *vp) { + int fb_height = get_framebuffer_height(fb); vp->y = fb_height - vp->y - vp->h; } @@ -416,6 +427,37 @@ static void gl33_sync_viewport(void) { } } +void gl33_sync_scissor(void) { + bool pending_enabled = + R.scissor.pending.x && + R.scissor.pending.y && + R.scissor.pending.w && + R.scissor.pending.h; + + if(!pending_enabled) { + if(R.scissor.active_enabled) { + glDisable(GL_SCISSOR_TEST); + R.scissor.active_enabled = false; + } + + return; + } + + if(!R.scissor.active_enabled) { + glEnable(GL_SCISSOR_TEST); + R.scissor.active_enabled = true; + } + + IntRect scissor = R.scissor.pending; + int fb_height = get_framebuffer_height(R.framebuffer.pending); + scissor.y = fb_height - scissor.y - scissor.h; + + if(memcmp(&R.scissor.active, &scissor, sizeof(R.scissor.active))) { + glScissor(scissor.x, scissor.y, scissor.w, scissor.h); + R.scissor.active = scissor; + } +} + static IntExtent gl33_get_default_framebuffer_size(void) { IntExtent s; // TODO: cache this at window creation time and refresh on resize events? @@ -500,6 +542,7 @@ static void gl33_sync_state(void) { gl33_sync_framebuffer(); gl33_sync_shader(); gl33_sync_viewport(); + gl33_sync_scissor(); gl33_sync_magic_uniforms(); gl33_sync_uniforms(R.progs.active); gl33_sync_texunits(true); @@ -1380,6 +1423,14 @@ static bool gl33_screenshot(Pixmap *out) { return true; } +static void gl33_scissor(IntRect scissor) { + R.scissor.pending = scissor; +} + +static void gl33_scissor_current(IntRect *scissor) { + *scissor = R.scissor.pending; +} + RendererBackend _r_backend_gl33 = { .name = "gl33", .funcs = { @@ -1467,6 +1518,8 @@ RendererBackend _r_backend_gl33 = { .vertex_array_get_vertex_attachment = gl33_vertex_array_get_vertex_attachment, .vertex_array_attach_index_buffer = gl33_vertex_array_attach_index_buffer, .vertex_array_get_index_attachment = gl33_vertex_array_get_index_attachment, + .scissor = gl33_scissor, + .scissor_current = gl33_scissor_current, .vsync = gl33_vsync, .vsync_current = gl33_vsync_current, .swap = gl33_swap, diff --git a/src/renderer/gl33/gl33.h b/src/renderer/gl33/gl33.h index 08655f5e..47824a20 100644 --- a/src/renderer/gl33/gl33.h +++ b/src/renderer/gl33/gl33.h @@ -75,6 +75,7 @@ void gl33_sync_blend_mode(void); void gl33_sync_cull_face_mode(void); void gl33_sync_depth_test_func(void); void gl33_sync_capabilities(void); +void gl33_sync_scissor(void); void gl33_buffer_deleted(CommonBuffer *cbuf); void gl33_vertex_buffer_deleted(VertexBuffer *vbuf); diff --git a/src/renderer/null/null.c b/src/renderer/null/null.c index b0cc91d6..662e5a21 100644 --- a/src/renderer/null/null.c +++ b/src/renderer/null/null.c @@ -179,6 +179,9 @@ static VertexBuffer* null_vertex_array_get_vertex_attachment(VertexArray *varr, static IndexBuffer* null_vertex_array_get_index_attachment(VertexArray *varr) { return (void*)&placeholder; } static void null_vertex_array_layout(VertexArray *varr, uint nattribs, VertexAttribFormat attribs[nattribs]) { } +static void null_scissor(IntRect scissor) { } +static void null_scissor_current(IntRect *scissor) { *scissor = (IntRect) { 0 }; } + static void null_vsync(VsyncMode mode) { } static VsyncMode null_vsync_current(void) { return VSYNC_NONE; } @@ -273,6 +276,8 @@ RendererBackend _r_backend_null = { .vertex_array_get_vertex_attachment = null_vertex_array_get_vertex_attachment, .vertex_array_attach_index_buffer = null_vertex_array_attach_index_buffer, .vertex_array_get_index_attachment = null_vertex_array_get_index_attachment, + .scissor = null_scissor, + .scissor_current = null_scissor_current, .vsync = null_vsync, .vsync_current = null_vsync_current, .swap = null_swap,