mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
font: keep the cells frames still on the GPU draw from
A glyph cell looked up in the current frame is not handed to another codepoint until the frame is over. Where the CPU writes cells into memory the GPU samples - the RSX's atlas in texture memory, the textures GXM and GX2 copy cells into - the frames before may still be drawing from them, and a cell reused for a new glyph shows up in their place. A consumer now says how many earlier frames that can be (font_atlas.inflight): the RSX one, GXM as many as swaps may be pending, GX2 the frame swapped last, and a cell any of them looked up waits until they are over. The default, zero, is the rule as it was. font_frame_test fills the cache in one frame with two in flight: the next two frames take none of its cells, and the third finds every cell free and the first frame's glyph where it was.
This commit is contained in:
1 parent
61c298b7fb
commit
688cc53eda
6 files changed
+76
-12
No files matched your search
@@ -745,6 +745,9 @@ static void* gx2_font_init(void* data, const char* font_path,
|
||||
font->atlas = font->font_driver->get_atlas(font->font_data);
|
||||
/* The atlas may grow, kept modest for the 32 MB of MEM1 */
|
||||
font->atlas->max_dims = VIDEO_SCALE_PACK(1024, 1024);
|
||||
/* Cells are copied into the texture the GPU samples, and the frame
|
||||
* swapped last may still be drawing from it */
|
||||
font->atlas->inflight = 1;
|
||||
if (!gx2_font_make_texture(font))
|
||||
{
|
||||
font->font_driver->free(font->font_data);
|
||||
|
||||
@@ -1756,6 +1756,9 @@ static void *gxm_font_init(void *data,
|
||||
|
||||
/* The atlas may grow; the texture follows it */
|
||||
atlas->max_dims = VIDEO_SCALE_PACK(2048, 2048);
|
||||
/* Cells are copied into the texture the GPU samples, and as many
|
||||
* frames as swaps may be pending can still be drawing from it */
|
||||
atlas->inflight = DISPLAY_MAX_PENDING_SWAPS;
|
||||
|
||||
if (!(font->texture = gxm_font_make_texture(atlas)))
|
||||
goto error;
|
||||
|
||||
@@ -830,6 +830,9 @@ static void *rsx_font_init(void *data,
|
||||
|
||||
/* The atlas may grow; the texture follows it */
|
||||
font->atlas->max_dims = VIDEO_SCALE_PACK(2048, 2048);
|
||||
/* Its cells are written where the RSX samples them, and the frame
|
||||
* before may still be drawing: one per buffer the flip waits on */
|
||||
font->atlas->inflight = RSX_MAX_BUFFERS - 1;
|
||||
font->tex_dims = VIDEO_SCALE_PACK(font->atlas->width,
|
||||
font->atlas->height);
|
||||
/* The atlas kept in texture memory, or copied into it */
|
||||
|
||||
+21
-12
@@ -449,7 +449,7 @@ typedef struct font_cache
|
||||
unsigned cand_count;
|
||||
unsigned cand_next;
|
||||
unsigned cand_frame;
|
||||
/* The frame a search found every cell looked up in, when full_set:
|
||||
/* The frame a search found every cell busy in, when full_set:
|
||||
* no cell is free again before the next one */
|
||||
unsigned full_frame;
|
||||
bool full_set;
|
||||
@@ -640,9 +640,10 @@ static void *font_cache_fallback_face(font_cache_t *c, int id,
|
||||
/* Advanced once a video frame by font_driver_frame_begin(). A cell
|
||||
* looked up in the current frame may already have glyphs queued from
|
||||
* it, so it is not handed to another codepoint until the frame is over:
|
||||
* reusing it would draw the new glyph in the earlier one's place. 0
|
||||
* until the first frame, which leaves plain LRU in place for a cache
|
||||
* used outside the video loop. It is only ever compared, never used
|
||||
* reusing it would draw the new glyph in the earlier one's place. The
|
||||
* same holds for the atlas's inflight frames before it, which the GPU
|
||||
* may still be drawing. 0 until the first frame, which leaves plain LRU
|
||||
* in place for a cache used outside the video loop. It is only ever compared, never used
|
||||
* to publish anything, so it is read relaxed: a plain load on every
|
||||
* backend, where the glyph lookup reads it once a glyph. */
|
||||
static retro_atomic_int_t font_frame_epoch;
|
||||
@@ -652,8 +653,14 @@ void font_driver_frame_begin(void)
|
||||
retro_atomic_fetch_add_int(&font_frame_epoch, 1);
|
||||
}
|
||||
|
||||
/* Searches every cell for the least recently used one not looked up
|
||||
* in @frame, keeping the oldest few in c->cand for the misses after */
|
||||
/* Whether @s was looked up in @frame or in one of the atlas's inflight
|
||||
* frames before it; 0 for a cell never looked up in a frame */
|
||||
#define FONT_CACHE_BUSY(c, s, frame) \
|
||||
( (frame) && (s)->last_frame \
|
||||
&& (unsigned)((frame) - (s)->last_frame) <= (c)->atlas.inflight)
|
||||
|
||||
/* Searches every cell for the least recently used one not busy in
|
||||
* @frame, keeping the oldest few in c->cand for the misses after */
|
||||
static font_cache_slot_t *font_cache_search(font_cache_t *c, unsigned frame)
|
||||
{
|
||||
unsigned i, b, k;
|
||||
@@ -667,7 +674,7 @@ static font_cache_slot_t *font_cache_search(font_cache_t *c, unsigned frame)
|
||||
{
|
||||
/* Unsigned subtraction handles the counter wrapping */
|
||||
unsigned age = c->usage_counter - blk[i].last_used;
|
||||
if (frame && blk[i].last_frame == frame)
|
||||
if (FONT_CACHE_BUSY(c, &blk[i], frame))
|
||||
continue;
|
||||
if ( n == FONT_CACHE_CANDIDATES
|
||||
&& age <= c->usage_counter - c->cand_used[n - 1])
|
||||
@@ -690,8 +697,8 @@ static font_cache_slot_t *font_cache_search(font_cache_t *c, unsigned frame)
|
||||
return n ? c->cand[0] : NULL;
|
||||
}
|
||||
|
||||
/* The least recently used cell not looked up in this frame, taken out
|
||||
* of the hash; NULL when every cell is in use this frame */
|
||||
/* The least recently used cell not busy in this frame, taken out of
|
||||
* the hash; NULL when every cell is */
|
||||
static font_cache_slot_t *font_cache_take_slot(font_cache_t *c)
|
||||
{
|
||||
unsigned frame = (unsigned)retro_atomic_load_relaxed_int(
|
||||
@@ -708,7 +715,7 @@ static font_cache_slot_t *font_cache_take_slot(font_cache_t *c)
|
||||
{
|
||||
font_cache_slot_t *s = c->cand[c->cand_next];
|
||||
unsigned used = c->cand_used[c->cand_next++];
|
||||
if (s->last_used == used && !(frame && s->last_frame == frame))
|
||||
if (s->last_used == used && !FONT_CACHE_BUSY(c, s, frame))
|
||||
{
|
||||
victim = s;
|
||||
break;
|
||||
@@ -910,9 +917,11 @@ VIDEO_NOINLINE static const struct font_glyph *font_cache_miss(
|
||||
if (!rast->render_glyph(face, code, gi, dst, c->atlas.width,
|
||||
c->cell_dims, c->atlas.format, &slot->glyph))
|
||||
{
|
||||
/* Nothing to draw: the cell goes back as the oldest there is */
|
||||
/* Nothing to draw: the cell goes back as the oldest there is,
|
||||
* free again at once - no frame has drawn from it since it was
|
||||
* taken */
|
||||
slot->last_used = c->usage_counter - 0x80000000u;
|
||||
slot->last_frame--;
|
||||
slot->last_frame = 0;
|
||||
c->cand_count = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -541,6 +541,13 @@ struct font_atlas
|
||||
* keeps the atlas at its first size. Cells only ever get added: a
|
||||
* glyph's offsets never change. */
|
||||
unsigned max_dims;
|
||||
/* Set by the consumer whose GPU may still be drawing earlier frames
|
||||
* from memory the CPU writes glyphs into - a texture sampled in
|
||||
* place, or one the CPU copies cells into: how many frames before
|
||||
* the current one may still be reading it. A cell looked up in any
|
||||
* of them is not given to another codepoint. Zero, the default:
|
||||
* only the current frame's cells are kept. */
|
||||
unsigned inflight;
|
||||
enum font_atlas_format format;
|
||||
bool dirty;
|
||||
};
|
||||
|
||||
@@ -125,6 +125,45 @@ int main(void)
|
||||
|
||||
drv->free(h);
|
||||
|
||||
/* A consumer whose GPU may still draw the two frames before: the
|
||||
* cells those looked up are not given to other codepoints until both
|
||||
* are over, and a glyph drawn three frames back is still where it
|
||||
* was */
|
||||
if (!font_renderer_create_default(&drv, &h, dejavu, 16,
|
||||
FONT_ATLAS_FORMAT_A8))
|
||||
return 1;
|
||||
{
|
||||
struct font_atlas *atlas = drv->get_atlas(h);
|
||||
const struct font_glyph *g;
|
||||
unsigned pos, i, missing = 0;
|
||||
atlas->inflight = 2;
|
||||
/* The glyphs drawn as the font was made count as looked up in
|
||||
* the frame it was made in: past them */
|
||||
for (i = 0; i < 3; i++)
|
||||
font_driver_frame_begin();
|
||||
g = drv->get_glyph(h, 0x0100);
|
||||
pos = g ? g->atlas_pos : 0;
|
||||
for (i = 1; i < 256; i++)
|
||||
if (!drv->get_glyph(h, 0x0100 + i))
|
||||
missing++;
|
||||
CHECK(g && missing == 0, "inflight: a frame fills every cell");
|
||||
font_driver_frame_begin();
|
||||
undrawn = frame(drv, h, 0x0400, &moved);
|
||||
CHECK(undrawn == 299, "inflight: the frame after takes none of its cells");
|
||||
font_driver_frame_begin();
|
||||
undrawn = frame(drv, h, 0x0400, &moved);
|
||||
CHECK(undrawn == 299, "inflight: nor the one after that");
|
||||
font_driver_frame_begin();
|
||||
g = drv->get_glyph(h, 0x0100);
|
||||
CHECK(g && g->atlas_pos == pos, "inflight: its glyph is where it was");
|
||||
undrawn = frame(drv, h, 0x0400, &moved);
|
||||
CHECK(undrawn == 300 - 255,
|
||||
"inflight: three frames on, every cell but the one in use");
|
||||
printf(" inflight 2: %u of 300 undrawn once the frames are over\n",
|
||||
undrawn);
|
||||
}
|
||||
drv->free(h);
|
||||
|
||||
/* A consumer that can take a bigger texture: the frame after one
|
||||
* that ran out finds the atlas grown, every earlier glyph where it
|
||||
* was, and draws all it asks for */
|
||||
|
||||
Reference in new issue
Block a user