improve sprite batch state management
This commit is contained in:
parent
d2df6fa3f2
commit
0c24da938a
6 changed files with 82 additions and 52 deletions
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue