diff --git a/src/renderer/api.c b/src/renderer/api.c index 8bdf4a0d..ad86c747 100644 --- a/src/renderer/api.c +++ b/src/renderer/api.c @@ -252,8 +252,21 @@ SDL_Window* r_create_window(const char *title, int x, int y, int w, int h, uint3 return B.create_window(title, x, y, w, h, flags); } -bool r_supports(RendererFeature feature) { - return B.supports(feature); +r_feature_bits_t r_features(void) { + return B.features(); +} + +r_capability_bits_t r_capabilities_current(void) { + return B.capabilities_current(); +} + +void r_capabilities(r_capability_bits_t newcaps) { + r_capability_bits_t caps = B.capabilities_current(); + + if(caps != newcaps) { + _r_state_touch_capabilities(); + B.capabilities(newcaps); + } } void r_capability(RendererCapability cap, bool value) { diff --git a/src/renderer/api.h b/src/renderer/api.h index a3d9de45..4e6a2715 100644 --- a/src/renderer/api.h +++ b/src/renderer/api.h @@ -409,7 +409,10 @@ void r_init(void); void r_post_init(void); void r_shutdown(void); -bool r_supports(RendererFeature feature); +r_feature_bits_t r_features(void); + +r_capability_bits_t r_capabilities_current(void); +void r_capabilities(r_capability_bits_t newcaps); void r_capability(RendererCapability cap, bool value); bool r_capability_current(RendererCapability cap); @@ -838,4 +841,9 @@ r_feature_bits_t r_feature_bit(RendererFeature feat) { return (1 << idx); } +static inline attr_must_inline +bool r_supports(RendererFeature feature) { + return r_features() & r_feature_bit(feature); +} + #endif // IGUARD_renderer_api_h diff --git a/src/renderer/common/backend.h b/src/renderer/common/backend.h index 14d81f13..9baa1246 100644 --- a/src/renderer/common/backend.h +++ b/src/renderer/common/backend.h @@ -20,7 +20,7 @@ typedef struct RendererFuncs { SDL_Window* (*create_window)(const char *title, int x, int y, int w, int h, uint32_t flags); - bool (*supports)(RendererFeature feature); + r_feature_bits_t (*features)(void); void (*capabilities)(r_capability_bits_t capbits); r_capability_bits_t (*capabilities_current)(void); diff --git a/src/renderer/common/sprite_batch.c b/src/renderer/common/sprite_batch.c index 3ac711dd..7c395174 100644 --- a/src/renderer/common/sprite_batch.c +++ b/src/renderer/common/sprite_batch.c @@ -14,6 +14,14 @@ #include "resource/sprite.h" #include "resource/model.h" +#ifndef SPRITE_BATCH_STATS +#ifdef DEBUG + #define SPRITE_BATCH_STATS 1 +#else + #define SPRITE_BATCH_STATS 0 +#endif +#endif + typedef struct SpriteAttribs { mat4 transform; mat4 tex_transform; @@ -29,30 +37,33 @@ typedef struct SpriteAttribs { #define SIZEOF_SPRITE_ATTRIBS (offsetof(SpriteAttribs, end_of_fields)) static struct SpriteBatchState { + // constants (set once on init and not expected to change) VertexArray *varr; VertexBuffer *vbuf; - uint base_instance; + Model quad; + r_feature_bits_t renderer_features; + + // varying state + mat4 projection; Texture *primary_texture; Texture *aux_textures[R_NUM_SPRITE_AUX_TEXTURES]; ShaderProgram *shader; - BlendMode blend; Framebuffer *framebuffer; + uint base_instance; + BlendMode blend; CullFaceMode cull_mode; DepthTestFunc depth_func; - mat4 projection CGLM_ALIGN(32); - uint cull_enabled : 1; - uint depth_test_enabled : 1; - uint depth_write_enabled : 1; uint num_pending; + r_capability_bits_t capbits; - Model quad; - +#if SPRITE_BATCH_STATS struct { uint flushes; uint sprites; uint best_batch; uint worst_batch; } frame_stats; +#endif } _r_sprite_batch; void _r_sprite_batch_init(void) { @@ -113,6 +124,8 @@ void _r_sprite_batch_init(void) { _r_sprite_batch.quad.offset = 0; _r_sprite_batch.quad.primitive = PRIM_TRIANGLE_STRIP; _r_sprite_batch.quad.vertex_array = _r_sprite_batch.varr; + + _r_sprite_batch.renderer_features = r_features(); } void _r_sprite_batch_shutdown(void) { @@ -128,21 +141,23 @@ void r_flush_sprites(void) { uint pending = _r_sprite_batch.num_pending; // needs to be done early to thwart recursive callss + _r_sprite_batch.num_pending = 0; +#if SPRITE_BATCH_STATS if(_r_sprite_batch.frame_stats.flushes) { - if(_r_sprite_batch.num_pending > _r_sprite_batch.frame_stats.best_batch) { - _r_sprite_batch.frame_stats.best_batch = _r_sprite_batch.num_pending; + if(pending > _r_sprite_batch.frame_stats.best_batch) { + _r_sprite_batch.frame_stats.best_batch = pending; } - if(_r_sprite_batch.num_pending < _r_sprite_batch.frame_stats.worst_batch) { - _r_sprite_batch.frame_stats.worst_batch = _r_sprite_batch.num_pending; + if(pending < _r_sprite_batch.frame_stats.worst_batch) { + _r_sprite_batch.frame_stats.worst_batch = pending; } } else { - _r_sprite_batch.frame_stats.worst_batch = _r_sprite_batch.frame_stats.best_batch = _r_sprite_batch.num_pending; + _r_sprite_batch.frame_stats.worst_batch = _r_sprite_batch.frame_stats.best_batch = pending; } - _r_sprite_batch.num_pending = 0; _r_sprite_batch.frame_stats.flushes++; +#endif r_state_push(); @@ -155,13 +170,17 @@ void r_flush_sprites(void) { r_uniform_sampler_array("tex_aux[0]", 0, R_NUM_SPRITE_AUX_TEXTURES, _r_sprite_batch.aux_textures); r_framebuffer(_r_sprite_batch.framebuffer); r_blend(_r_sprite_batch.blend); - r_capability(RCAP_DEPTH_TEST, _r_sprite_batch.depth_test_enabled); - r_capability(RCAP_DEPTH_WRITE, _r_sprite_batch.depth_write_enabled); - r_capability(RCAP_CULL_FACE, _r_sprite_batch.cull_enabled); - r_depth_func(_r_sprite_batch.depth_func); - r_cull(_r_sprite_batch.cull_mode); + r_capabilities(_r_sprite_batch.capbits); - if(r_supports(RFEAT_DRAW_INSTANCED_BASE_INSTANCE)) { + if(_r_sprite_batch.capbits & r_capability_bit(RCAP_DEPTH_TEST)) { + r_depth_func(_r_sprite_batch.depth_func); + } + + if(_r_sprite_batch.capbits & r_capability_bit(RCAP_CULL_FACE)) { + r_cull(_r_sprite_batch.cull_mode); + } + + if(_r_sprite_batch.renderer_features & r_feature_bit(RFEAT_DRAW_INSTANCED_BASE_INSTANCE)) { r_draw_model_ptr(&_r_sprite_batch.quad, pending, _r_sprite_batch.base_instance); _r_sprite_batch.base_instance += pending; @@ -256,7 +275,10 @@ static void _r_sprite_batch_add(Sprite *spr, const SpriteParams *params, SDL_RWo } SDL_RWwrite(stream, &attribs, SIZEOF_SPRITE_ATTRIBS, 1); + +#if SPRITE_BATCH_STATS _r_sprite_batch.frame_stats.sprites++; +#endif } void r_draw_sprite(const SpriteParams *params) { @@ -319,33 +341,21 @@ void r_draw_sprite(const SpriteParams *params) { _r_sprite_batch.blend = blend; } - bool depth_test_enabled = r_capability_current(RCAP_DEPTH_TEST); - bool depth_write_enabled = r_capability_current(RCAP_DEPTH_WRITE); - bool cull_enabled = r_capability_current(RCAP_CULL_FACE); + r_capability_bits_t caps = r_capabilities_current(); DepthTestFunc depth_func = r_depth_func_current(); CullFaceMode cull_mode = r_cull_current(); - if(_r_sprite_batch.depth_test_enabled != depth_test_enabled) { + if(_r_sprite_batch.capbits != caps) { r_flush_sprites(); - _r_sprite_batch.depth_test_enabled = depth_test_enabled; + _r_sprite_batch.capbits = caps; } - if(_r_sprite_batch.depth_write_enabled != depth_write_enabled) { - r_flush_sprites(); - _r_sprite_batch.depth_write_enabled = depth_write_enabled; - } - - if(_r_sprite_batch.cull_enabled != cull_enabled) { - r_flush_sprites(); - _r_sprite_batch.cull_enabled = cull_enabled; - } - - if(_r_sprite_batch.depth_func != depth_func) { + if((caps & r_capability_bit(RCAP_DEPTH_TEST)) && _r_sprite_batch.depth_func != depth_func) { r_flush_sprites(); _r_sprite_batch.depth_func = depth_func; } - if(_r_sprite_batch.cull_mode != cull_mode) { + if((caps & r_capability_bit(RCAP_CULL_FACE)) && _r_sprite_batch.cull_mode != cull_mode) { r_flush_sprites(); _r_sprite_batch.cull_mode = cull_mode; } @@ -361,6 +371,8 @@ void r_draw_sprite(const SpriteParams *params) { size_t remaining = SDL_RWsize(stream) - SDL_RWtell(stream); if(remaining < SIZEOF_SPRITE_ATTRIBS) { + // TODO: maybe it is better to grow the buffer instead? + if(!r_supports(RFEAT_DRAW_INSTANCED_BASE_INSTANCE)) { log_warn("Vertex buffer exhausted (%zu needed for next sprite, %zu remaining), flush forced", SIZEOF_SPRITE_ATTRIBS, remaining); } @@ -375,13 +387,13 @@ void r_draw_sprite(const SpriteParams *params) { #include "resource/font.h" void _r_sprite_batch_end_frame(void) { -#ifdef DEBUG + r_flush_sprites(); + +#if SPRITE_BATCH_STATS if(!_r_sprite_batch.frame_stats.flushes) { return; } - r_flush_sprites(); - static char buf[512]; snprintf(buf, sizeof(buf), "%6i sprites %6i flushes %9.02f spr/flush %6i best %6i worst", _r_sprite_batch.frame_stats.sprites, @@ -400,7 +412,6 @@ void _r_sprite_batch_end_frame(void) { }); memset(&_r_sprite_batch.frame_stats, 0, sizeof(_r_sprite_batch.frame_stats)); - #endif } diff --git a/src/renderer/gl33/gl33.c b/src/renderer/gl33/gl33.c index 4bb27e65..232d782f 100644 --- a/src/renderer/gl33/gl33.c +++ b/src/renderer/gl33/gl33.c @@ -855,8 +855,8 @@ static SDL_Window* gl33_create_window(const char *title, int x, int y, int w, in return window; } -static bool gl33_supports(RendererFeature feature) { - return R.features & r_feature_bit(feature); +static r_feature_bits_t gl33_features(void) { + return R.features; } static void gl33_capabilities(r_capability_bits_t capbits) { @@ -1079,7 +1079,7 @@ RendererBackend _r_backend_gl33 = { .post_init = gl33_post_init, .shutdown = gl33_shutdown, .create_window = gl33_create_window, - .supports = gl33_supports, + .features = gl33_features, .capabilities = gl33_capabilities, .capabilities_current = gl33_capabilities_current, .draw = gl33_draw, diff --git a/src/renderer/null/null.c b/src/renderer/null/null.c index 55d0a480..746cc8c6 100644 --- a/src/renderer/null/null.c +++ b/src/renderer/null/null.c @@ -23,9 +23,7 @@ static void null_init(void) { } static void null_post_init(void) { } static void null_shutdown(void) { } -static bool null_supports(RendererFeature feature) { - return true; -} +static r_feature_bits_t null_features(void) { return ~0; } static void null_capabilities(r_capability_bits_t capbits) { } static r_capability_bits_t null_capabilities_current(void) { return (r_capability_bits_t)-1; } @@ -169,7 +167,7 @@ RendererBackend _r_backend_null = { .post_init = null_post_init, .shutdown = null_shutdown, .create_window = null_create_window, - .supports = null_supports, + .features = null_features, .capabilities = null_capabilities, .capabilities_current = null_capabilities_current, .draw = null_draw,