mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 17:28:49 -10:00
gfx_display: count what ends each quad batch and show it on the statistics overlay
Every flush point now records why the gathered quads had to go out - text, texture change, blend group, scissor, a non-gathering draw, a full batch, or an explicit flush from outside - along with quads, strips, largest strip, and text calls/bytes per menu frame. Counts are plain increments on the drawing thread and are published atomically once per menu frame for the overlay, which adds a MENU UI block while the menu is up.
This commit is contained in:
1 parent
b26625232f
commit
02a1b2c23f
5 files changed
+145
-14
No files matched your search
+67
-12
@@ -335,6 +335,17 @@ float gfx_display_get_dpi_scale(
|
||||
return adjusted_scale;
|
||||
}
|
||||
|
||||
static void gfx_display_flush_impl(gfx_display_t *p_disp);
|
||||
|
||||
/* Sends what is gathered and records why it had to go */
|
||||
static void gfx_display_flush_as(gfx_display_t *p_disp,
|
||||
enum gfx_display_flush_reason reason)
|
||||
{
|
||||
if (p_disp && p_disp->batch_quads)
|
||||
p_disp->stats.v[GFX_DISPLAY_STAT_FLUSH + reason]++;
|
||||
gfx_display_flush_impl(p_disp);
|
||||
}
|
||||
|
||||
/* Begin scissoring operation */
|
||||
void gfx_display_scissor_begin(
|
||||
gfx_display_t *p_disp,
|
||||
@@ -345,7 +356,7 @@ void gfx_display_scissor_begin(
|
||||
{
|
||||
gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
|
||||
/* What is gathered goes out before this draws */
|
||||
gfx_display_flush_batch(disp_get_ptr());
|
||||
gfx_display_flush_as(disp_get_ptr(), GFX_DISPLAY_FLUSH_SCISSOR);
|
||||
if (dispctx && dispctx->scissor_begin)
|
||||
{
|
||||
if (y < 0)
|
||||
@@ -417,10 +428,12 @@ static void gfx_display_draw_text_internal(
|
||||
float scale, bool shadows_enable, float shadow_offset,
|
||||
bool draw_outside)
|
||||
{
|
||||
size_t _len;
|
||||
struct font_params params;
|
||||
gfx_display_t *p_disp = disp_get_ptr();
|
||||
video_driver_state_t *video_st = video_state_get_ptr();
|
||||
/* What is gathered goes out before this draws */
|
||||
gfx_display_flush_batch(disp_get_ptr());
|
||||
gfx_display_flush_as(p_disp, GFX_DISPLAY_FLUSH_TEXT);
|
||||
|
||||
/* NULL text is a no-op: ozone_draw_footer and similar menu code can
|
||||
* legitimately reach here with text==NULL for unset/optional fields,
|
||||
@@ -459,9 +472,13 @@ static void gfx_display_draw_text_internal(
|
||||
params.drop_alpha = GFX_SHADOW_ALPHA;
|
||||
}
|
||||
|
||||
_len = strlen(text);
|
||||
p_disp->stats.v[GFX_DISPLAY_STAT_TEXT_CALLS]++;
|
||||
p_disp->stats.v[GFX_DISPLAY_STAT_TEXT_BYTES] += (unsigned)_len;
|
||||
|
||||
if (video_st->poke && video_st->poke->set_osd_msg)
|
||||
video_st->poke->set_osd_msg(video_st->data,
|
||||
text, strlen(text), ¶ms, (void*)font);
|
||||
text, _len, ¶ms, (void*)font);
|
||||
}
|
||||
|
||||
void gfx_display_draw_text(
|
||||
@@ -572,9 +589,9 @@ static bool gfx_display_batch_add(gfx_display_t *p_disp,
|
||||
|| p_disp->batch_userdata != userdata
|
||||
|| p_disp->batch_video_width != video_width
|
||||
|| p_disp->batch_video_height != video_height))
|
||||
gfx_display_flush_batch(p_disp);
|
||||
gfx_display_flush_as(p_disp, GFX_DISPLAY_FLUSH_TEXTURE);
|
||||
if (p_disp->batch_quads >= GFX_DISPLAY_BATCH_QUADS)
|
||||
gfx_display_flush_batch(p_disp);
|
||||
gfx_display_flush_as(p_disp, GFX_DISPLAY_FLUSH_CAPACITY);
|
||||
|
||||
if (!p_disp->batch_mem)
|
||||
{
|
||||
@@ -633,6 +650,7 @@ static bool gfx_display_batch_add(gfx_display_t *p_disp,
|
||||
p_disp->batch_first_h = ph;
|
||||
}
|
||||
p_disp->batch_quads++;
|
||||
p_disp->stats.v[GFX_DISPLAY_STAT_QUADS]++;
|
||||
p_disp->batch_texture = texture;
|
||||
p_disp->batch_userdata = userdata;
|
||||
p_disp->batch_video_width = video_width;
|
||||
@@ -644,7 +662,7 @@ static bool gfx_display_batch_add(gfx_display_t *p_disp,
|
||||
* batch. Called before anything else draws, so that what was gathered
|
||||
* lands under what comes after it, and at the end of a frame so that
|
||||
* nothing is still waiting when the frame is over. */
|
||||
void gfx_display_flush_batch(gfx_display_t *p_disp)
|
||||
static void gfx_display_flush_impl(gfx_display_t *p_disp)
|
||||
{
|
||||
gfx_display_ctx_driver_t *dispctx;
|
||||
gfx_display_ctx_draw_t draw;
|
||||
@@ -653,6 +671,9 @@ void gfx_display_flush_batch(gfx_display_t *p_disp)
|
||||
if (!p_disp || !p_disp->batch_quads)
|
||||
return;
|
||||
dispctx = p_disp->dispctx;
|
||||
p_disp->stats.v[GFX_DISPLAY_STAT_BATCHES]++;
|
||||
if (p_disp->batch_quads > p_disp->stats.v[GFX_DISPLAY_STAT_BATCH_MAX])
|
||||
p_disp->stats.v[GFX_DISPLAY_STAT_BATCH_MAX] = p_disp->batch_quads;
|
||||
coords.lut_tex_coord = NULL;
|
||||
if (p_disp->batch_quads == 1)
|
||||
{
|
||||
@@ -699,13 +720,42 @@ void gfx_display_flush_batch(gfx_display_t *p_disp)
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_display_flush_batch(gfx_display_t *p_disp)
|
||||
{
|
||||
if (p_disp && p_disp->batch_quads)
|
||||
p_disp->stats.v[GFX_DISPLAY_STAT_FLUSH + GFX_DISPLAY_FLUSH_EXPLICIT]++;
|
||||
gfx_display_flush_impl(p_disp);
|
||||
}
|
||||
|
||||
void gfx_display_stats_latch(gfx_display_t *p_disp)
|
||||
{
|
||||
unsigned i;
|
||||
if (!p_disp)
|
||||
return;
|
||||
for (i = 0; i < GFX_DISPLAY_STAT_LAST; i++)
|
||||
{
|
||||
retro_atomic_store_relaxed_int(&p_disp->stats_pub[i],
|
||||
(int)p_disp->stats.v[i]);
|
||||
p_disp->stats.v[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_display_stats_get(gfx_display_stats_t *out)
|
||||
{
|
||||
unsigned i;
|
||||
gfx_display_t *p_disp = disp_get_ptr();
|
||||
for (i = 0; i < GFX_DISPLAY_STAT_LAST; i++)
|
||||
out->v[i] = (unsigned)retro_atomic_load_relaxed_int(
|
||||
&p_disp->stats_pub[i]);
|
||||
}
|
||||
|
||||
void gfx_display_blend_begin(gfx_display_ctx_driver_t *dispctx,
|
||||
void *userdata)
|
||||
{
|
||||
gfx_display_t *p_disp = disp_get_ptr();
|
||||
/* What was gathered outside this group goes out under the state it
|
||||
* was gathered under */
|
||||
gfx_display_flush_batch(p_disp);
|
||||
gfx_display_flush_as(p_disp, GFX_DISPLAY_FLUSH_BLEND);
|
||||
if (dispctx && dispctx->blend_begin)
|
||||
{
|
||||
dispctx->blend_begin(userdata);
|
||||
@@ -718,7 +768,7 @@ void gfx_display_blend_end(gfx_display_ctx_driver_t *dispctx,
|
||||
{
|
||||
gfx_display_t *p_disp = disp_get_ptr();
|
||||
/* And what was gathered inside it goes out while it is still on */
|
||||
gfx_display_flush_batch(p_disp);
|
||||
gfx_display_flush_as(p_disp, GFX_DISPLAY_FLUSH_BLEND);
|
||||
if (dispctx && dispctx->blend_end)
|
||||
{
|
||||
dispctx->blend_end(userdata);
|
||||
@@ -735,7 +785,7 @@ void gfx_display_draw(gfx_display_ctx_driver_t *dispctx,
|
||||
gfx_display_ctx_draw_t *draw, void *userdata,
|
||||
unsigned video_width, unsigned video_height)
|
||||
{
|
||||
gfx_display_flush_batch(disp_get_ptr());
|
||||
gfx_display_flush_as(disp_get_ptr(), GFX_DISPLAY_FLUSH_DRAW);
|
||||
if (dispctx && dispctx->draw && draw)
|
||||
dispctx->draw(draw, userdata, video_width, video_height);
|
||||
}
|
||||
@@ -792,7 +842,7 @@ void gfx_display_draw_quad(
|
||||
draw.x, draw.y, draw.width, draw.height))
|
||||
return;
|
||||
|
||||
gfx_display_flush_batch(p_disp);
|
||||
gfx_display_flush_as(p_disp, GFX_DISPLAY_FLUSH_DRAW);
|
||||
if (dispctx->blend_begin)
|
||||
dispctx->blend_begin(data);
|
||||
if (dispctx->draw)
|
||||
@@ -841,7 +891,7 @@ void gfx_display_draw_texture_slice(
|
||||
};
|
||||
|
||||
/* What is gathered goes out before this draws */
|
||||
gfx_display_flush_batch(disp_get_ptr());
|
||||
gfx_display_flush_as(disp_get_ptr(), GFX_DISPLAY_FLUSH_DRAW);
|
||||
|
||||
/* Early-out: guard against division by zero from
|
||||
* zero display dimensions or zero texture dimensions */
|
||||
@@ -1074,7 +1124,7 @@ void gfx_display_draw_cursor(
|
||||
struct video_coords coords;
|
||||
gfx_display_ctx_driver_t *dispctx = p_disp->dispctx;
|
||||
/* What is gathered goes out before this draws */
|
||||
gfx_display_flush_batch(disp_get_ptr());
|
||||
gfx_display_flush_as(disp_get_ptr(), GFX_DISPLAY_FLUSH_DRAW);
|
||||
|
||||
if (!dispctx)
|
||||
return;
|
||||
@@ -1447,6 +1497,11 @@ void gfx_display_init(void)
|
||||
else
|
||||
p_disp->flags &= ~GFX_DISP_FLAG_HAS_WINDOWED;
|
||||
p_dispca->allocated = 0;
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < GFX_DISPLAY_STAT_LAST; i++)
|
||||
retro_atomic_int_init(&p_disp->stats_pub[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool gfx_display_init_first_driver(gfx_display_t *p_disp,
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <retro_common_api.h>
|
||||
#include <formats/image.h>
|
||||
#include <gfx/math/matrix_4x4.h>
|
||||
#include <retro_atomic.h>
|
||||
|
||||
#include "../retroarch.h"
|
||||
#include "../gfx/font_driver.h"
|
||||
@@ -187,6 +188,35 @@ typedef struct gfx_display_ctx_powerstate
|
||||
bool charging;
|
||||
} gfx_display_ctx_powerstate_t;
|
||||
|
||||
/* Why a gathered batch of quads had to go out. */
|
||||
enum gfx_display_flush_reason
|
||||
{
|
||||
GFX_DISPLAY_FLUSH_TEXT = 0, /* text drawn */
|
||||
GFX_DISPLAY_FLUSH_TEXTURE, /* quad with another texture or frame */
|
||||
GFX_DISPLAY_FLUSH_BLEND, /* blend group begun or ended */
|
||||
GFX_DISPLAY_FLUSH_SCISSOR, /* scissor begun or ended */
|
||||
GFX_DISPLAY_FLUSH_DRAW, /* a draw that does not gather */
|
||||
GFX_DISPLAY_FLUSH_CAPACITY, /* batch full */
|
||||
GFX_DISPLAY_FLUSH_EXPLICIT, /* gfx_display_flush_batch() from outside */
|
||||
GFX_DISPLAY_FLUSH_LAST
|
||||
};
|
||||
|
||||
enum gfx_display_stat
|
||||
{
|
||||
GFX_DISPLAY_STAT_QUADS = 0, /* quads gathered */
|
||||
GFX_DISPLAY_STAT_BATCHES, /* strips sent out */
|
||||
GFX_DISPLAY_STAT_BATCH_MAX, /* quads in the largest strip */
|
||||
GFX_DISPLAY_STAT_TEXT_CALLS, /* strings handed to the font driver */
|
||||
GFX_DISPLAY_STAT_TEXT_BYTES, /* bytes of text in them */
|
||||
GFX_DISPLAY_STAT_FLUSH, /* one per enum gfx_display_flush_reason */
|
||||
GFX_DISPLAY_STAT_LAST = GFX_DISPLAY_STAT_FLUSH + GFX_DISPLAY_FLUSH_LAST
|
||||
};
|
||||
|
||||
typedef struct gfx_display_stats
|
||||
{
|
||||
unsigned v[GFX_DISPLAY_STAT_LAST];
|
||||
} gfx_display_stats_t;
|
||||
|
||||
struct gfx_display
|
||||
{
|
||||
gfx_display_ctx_driver_t *dispctx;
|
||||
@@ -234,6 +264,14 @@ struct gfx_display
|
||||
bool blend_on;
|
||||
|
||||
uint8_t flags;
|
||||
|
||||
/* What the batch did during the menu frame being drawn, counted
|
||||
* where it happens on the drawing thread and published as a whole
|
||||
* by gfx_display_stats_latch() once the frame is over. The
|
||||
* statistics overlay reads the published copy from the main
|
||||
* thread, so that copy is atomic and the live one is not. */
|
||||
gfx_display_stats_t stats;
|
||||
retro_atomic_int_t stats_pub[GFX_DISPLAY_STAT_LAST];
|
||||
};
|
||||
|
||||
void gfx_display_free(void);
|
||||
@@ -306,6 +344,13 @@ void gfx_display_draw_bg(
|
||||
* that were asked for before it. */
|
||||
void gfx_display_flush_batch(gfx_display_t *p_disp);
|
||||
|
||||
/* Publishes the counts of the menu frame just drawn and starts the
|
||||
* next; called by the drawing thread once the menu frame is over. */
|
||||
void gfx_display_stats_latch(gfx_display_t *p_disp);
|
||||
|
||||
/* The counts of the last published menu frame, safe from any thread. */
|
||||
void gfx_display_stats_get(gfx_display_stats_t *out);
|
||||
|
||||
/* Blending, counted, so that what is gathered knows whether it is
|
||||
* inside a group that has already turned blending on. Every caller
|
||||
* goes through these rather than the driver's own. */
|
||||
|
||||
@@ -6406,6 +6406,35 @@ void video_driver_frame(const void *data, unsigned width,
|
||||
video_st->frame_count,
|
||||
video_st->frame_drop_count);
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
if (menu_is_alive)
|
||||
{
|
||||
/* What the menu's quad batching did in the last menu
|
||||
* frame drawn: how many strips its quads went out in,
|
||||
* and what ended each strip. */
|
||||
gfx_display_stats_t ui;
|
||||
gfx_display_stats_get(&ui);
|
||||
__len += snprintf(video_st->stat_text + __len, sizeof(video_st->stat_text) - __len,
|
||||
"MENU UI\n"
|
||||
" Quads: %5u in %u strips (max %u)\n"
|
||||
" Ended by text %u tex %u blend %u sciss %u draw %u full %u end %u\n"
|
||||
" Text: %5u calls, %u bytes\n"
|
||||
,
|
||||
ui.v[GFX_DISPLAY_STAT_QUADS],
|
||||
ui.v[GFX_DISPLAY_STAT_BATCHES],
|
||||
ui.v[GFX_DISPLAY_STAT_BATCH_MAX],
|
||||
ui.v[GFX_DISPLAY_STAT_FLUSH + GFX_DISPLAY_FLUSH_TEXT],
|
||||
ui.v[GFX_DISPLAY_STAT_FLUSH + GFX_DISPLAY_FLUSH_TEXTURE],
|
||||
ui.v[GFX_DISPLAY_STAT_FLUSH + GFX_DISPLAY_FLUSH_BLEND],
|
||||
ui.v[GFX_DISPLAY_STAT_FLUSH + GFX_DISPLAY_FLUSH_SCISSOR],
|
||||
ui.v[GFX_DISPLAY_STAT_FLUSH + GFX_DISPLAY_FLUSH_DRAW],
|
||||
ui.v[GFX_DISPLAY_STAT_FLUSH + GFX_DISPLAY_FLUSH_CAPACITY],
|
||||
ui.v[GFX_DISPLAY_STAT_FLUSH + GFX_DISPLAY_FLUSH_EXPLICIT],
|
||||
ui.v[GFX_DISPLAY_STAT_TEXT_CALLS],
|
||||
ui.v[GFX_DISPLAY_STAT_TEXT_BYTES]);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Split from the block above: a single concatenated format
|
||||
* literal exceeded the 509-byte minimum ISO C90 guarantees
|
||||
* (-Werror=overlength-strings in the C89 lane). */
|
||||
|
||||
+1
-1
@@ -1181,7 +1181,7 @@ typedef struct
|
||||
retro_atomic_int_t output_size_packed;
|
||||
/* Where the statistics overlay's text is built, for the frame
|
||||
* descriptor to point at (video_frame_info_t::stat_text) */
|
||||
char stat_text[1024];
|
||||
char stat_text[1280];
|
||||
#ifdef HAVE_OVERLAY
|
||||
/* The active overlay's viewport override, published by the main
|
||||
* thread whenever the active overlay changes
|
||||
|
||||
+3
-1
@@ -4548,10 +4548,12 @@ void menu_driver_frame(bool menu_is_alive, video_frame_info_t *video_info)
|
||||
struct menu_state *menu_st = &menu_driver_state;
|
||||
if (menu_is_alive && menu_st->driver_ctx->frame)
|
||||
{
|
||||
gfx_display_t *p_disp = disp_get_ptr();
|
||||
menu_st->driver_ctx->frame(menu_st->userdata, video_info);
|
||||
/* Nothing the menu gathered may still be waiting when the frame
|
||||
* it belongs to is over */
|
||||
gfx_display_flush_batch(disp_get_ptr());
|
||||
gfx_display_flush_batch(p_disp);
|
||||
gfx_display_stats_latch(p_disp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user