mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
gfx_thumbnail: the viewer reuses the still the file browser shows
Pressing OK on an image in the file browser read and decoded the file a second time even though its thumbnail was already on screen. menu_media_open_selected_file only reuses a thumbnail when gfx_thumbnail_has_file says it holds the file, and that answered yes only for a live animation session. Every still (PNG, JPEG, BMP, TGA, single-frame GIF/WEBP) has no session, and neither does an animation the preview budget refuses - large ones land as a still - nor one that finished its last loop. All of those were reset and loaded again. Each thumbnail now records a 64-bit hash of the path it was requested from (no per-thumbnail path buffer), cleared on reset. gfx_thumbnail_has_file matches it while the still is shown, or while its load is in flight and not superseded; a failed or cancelled load still gets requested again. samples/gfx/gfx_thumbnail_anim lane 16 drives the real gfx_thumbnail.c: a still is recognised in flight and once shown, and not for another path, after a reset, when superseded or when the load failed. It fails on the old code.
This commit is contained in:
1 parent
5c6b2a3867
commit
b93738dd4a
3 files changed
+153
-3
No files matched your search
+44
-3
@@ -992,11 +992,46 @@ static bool gfx_thumb_frame_budget(void *ud, size_t avail, size_t len)
|
||||
return cpu_features_get_time_usec() - *(int64_t*)ud < 2000;
|
||||
}
|
||||
|
||||
/* 64-bit FNV-1a; never 0 for a non-empty path, so 0 means "none". */
|
||||
static uint64_t gfx_thumbnail_file_hash(const char *path)
|
||||
{
|
||||
uint64_t h = 0xcbf29ce484222325ULL;
|
||||
if (!path || !*path)
|
||||
return 0;
|
||||
while (*path)
|
||||
h = (h ^ (unsigned char)*path++) * 0x100000001b3ULL;
|
||||
return h ? h : 1;
|
||||
}
|
||||
|
||||
/* Whether @thumbnail already holds @path, shown or on its way.
|
||||
*
|
||||
* Asking only the animation session left out every still - and every
|
||||
* animation the preview budget refused, which lands as a still - so
|
||||
* opening the viewer on the image the file browser was showing reset
|
||||
* the thumbnail and read and decoded the file all over again. */
|
||||
bool gfx_thumbnail_has_file(const gfx_thumbnail_t *thumbnail, const char *path)
|
||||
{
|
||||
const gfx_anim_preview_t *session = thumbnail
|
||||
? (const gfx_anim_preview_t*)thumbnail->anim_sess : NULL;
|
||||
return session && string_is_equal(session->path, path);
|
||||
const gfx_anim_preview_t *session;
|
||||
if (!thumbnail || !path || !*path)
|
||||
return false;
|
||||
if ((session = (const gfx_anim_preview_t*)thumbnail->anim_sess))
|
||||
return string_is_equal(session->path, path);
|
||||
if (!thumbnail->file_hash)
|
||||
return false;
|
||||
switch (GFX_THUMB_STATUS_LOAD(
|
||||
(retro_atomic_int_t*)&thumbnail->status))
|
||||
{
|
||||
case GFX_THUMBNAIL_STATUS_AVAILABLE:
|
||||
break;
|
||||
case GFX_THUMBNAIL_STATUS_PENDING:
|
||||
/* superseded: nothing will ever deliver it */
|
||||
if (thumbnail->list_id != gfx_thumb_st.list_id)
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return thumbnail->file_hash == gfx_thumbnail_file_hash(path);
|
||||
}
|
||||
|
||||
/* Main-thread transport command; the audio mixer applies its request
|
||||
@@ -1717,6 +1752,9 @@ void gfx_thumbnail_request(
|
||||
{
|
||||
gfx_thumbnail_tag_t *thumbnail_tag;
|
||||
|
||||
thumbnail->file_hash = gfx_thumbnail_file_hash(
|
||||
thumbnail_path);
|
||||
|
||||
/* A WebM or MP4 that serves as its own thumbnail takes
|
||||
* the sliding window rather than the whole-file still
|
||||
* decode; see gfx_thumbnail_try_video_open. */
|
||||
@@ -1839,6 +1877,8 @@ void gfx_thumbnail_request_file(
|
||||
|| !path_is_valid(file_path))
|
||||
return;
|
||||
|
||||
thumbnail->file_hash = gfx_thumbnail_file_hash(file_path);
|
||||
|
||||
/* Video containers take the sliding window before the still task
|
||||
* is ever queued; see gfx_thumbnail_try_video_open. */
|
||||
if (gfx_thumbnail_try_video_open(thumbnail, file_path))
|
||||
@@ -1941,6 +1981,7 @@ void gfx_thumbnail_reset(gfx_thumbnail_t *thumbnail)
|
||||
* thread learn on delivery that this reset happened. */
|
||||
GFX_THUMB_STATUS_STORE(&thumbnail->status, GFX_THUMBNAIL_STATUS_UNKNOWN);
|
||||
thumbnail->texture = 0;
|
||||
thumbnail->file_hash = 0;
|
||||
thumbnail->dims = 0;
|
||||
thumbnail->alpha = 0.0f;
|
||||
thumbnail->delay_timer = 0.0f;
|
||||
|
||||
@@ -270,6 +270,12 @@ typedef struct
|
||||
* ever deliver it, so the slot must be re-requested rather than
|
||||
* waited on. */
|
||||
uint64_t list_id;
|
||||
/* 64-bit FNV-1a of the file this thumbnail was last requested
|
||||
* from, 0 when none; cleared by gfx_thumbnail_reset. Lets
|
||||
* gfx_thumbnail_has_file recognise a still (or an animation that
|
||||
* fell back to one) without keeping a path buffer per thumbnail,
|
||||
* so the viewer takes what the browser already decoded. */
|
||||
uint64_t file_hash;
|
||||
/* Both axes in one word, VIDEO_SCALE_PACK's layout. */
|
||||
unsigned dims;
|
||||
float alpha;
|
||||
|
||||
@@ -1088,6 +1088,109 @@ int main(void)
|
||||
bad = 1;
|
||||
}
|
||||
|
||||
/* 16. already loaded: the file browser shows an image as the
|
||||
* selected entry's thumbnail, and OK opens the viewer on that
|
||||
* same file. menu_media_open_selected_file reuses a thumbnail
|
||||
* only when gfx_thumbnail_has_file says it holds the file, and
|
||||
* that answered yes only for a live animation session - so
|
||||
* every still (PNG, JPEG, BMP, TGA, single-frame GIF/WEBP), and
|
||||
* every animation too large for the preview budget (it falls
|
||||
* back to a still), was reset and decoded a second time on OK.
|
||||
* A still requested from a path must be recognised as holding
|
||||
* it once shown and while its load is in flight; not for
|
||||
* another path, not after a reset, not when its load was
|
||||
* superseded, and not when the load failed. */
|
||||
{
|
||||
char still[256], other[256];
|
||||
int ok16 = 1;
|
||||
snprintf(still, sizeof(still), "/tmp/gfx_thumb_has_%d.bin",
|
||||
(int)getpid());
|
||||
snprintf(other, sizeof(other), "/tmp/gfx_thumb_has_%d_b.bin",
|
||||
(int)getpid());
|
||||
{
|
||||
FILE *f = fopen(still, "wb");
|
||||
if (f)
|
||||
{
|
||||
fwrite("still", 1, 5, f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
task_queue_init(false, NULL);
|
||||
gt_still_capture = 1;
|
||||
gt_async_mode = 0;
|
||||
memset(&th, 0, sizeof(th));
|
||||
|
||||
/* in flight: OK pressed before the decode came back */
|
||||
gt_still_cb = NULL;
|
||||
gfx_thumbnail_request_file(still, &th, 0);
|
||||
if (!gt_still_cb || !gfx_thumbnail_has_file(&th, still))
|
||||
{
|
||||
printf("[FAIL] has_file: a still in flight not recognised\n");
|
||||
ok16 = 0;
|
||||
}
|
||||
if (gt_still_cb)
|
||||
{
|
||||
struct texture_image *img = gt_still_image(0xA000u, 0);
|
||||
if (img)
|
||||
gt_still_cb(NULL, img, gt_still_ud, NULL);
|
||||
}
|
||||
/* shown: the viewer must take it as is */
|
||||
if ( (int)th.status != GFX_THUMBNAIL_STATUS_AVAILABLE
|
||||
|| !gfx_thumbnail_has_file(&th, still))
|
||||
{
|
||||
printf("[FAIL] has_file: a shown still not recognised "
|
||||
"(status=%d)\n", (int)th.status);
|
||||
ok16 = 0;
|
||||
}
|
||||
if ( gfx_thumbnail_has_file(&th, other)
|
||||
|| gfx_thumbnail_has_file(&th, NULL)
|
||||
|| gfx_thumbnail_has_file(&th, ""))
|
||||
{
|
||||
printf("[FAIL] has_file: a still claimed another path\n");
|
||||
ok16 = 0;
|
||||
}
|
||||
gfx_thumbnail_reset(&th);
|
||||
if (gfx_thumbnail_has_file(&th, still))
|
||||
{
|
||||
printf("[FAIL] has_file: still recognised after a reset\n");
|
||||
ok16 = 0;
|
||||
}
|
||||
/* superseded: the request's list was cancelled, nothing will
|
||||
* ever deliver it */
|
||||
gt_still_cb = NULL;
|
||||
gfx_thumbnail_request_file(still, &th, 0);
|
||||
gfx_thumbnail_cancel_pending_requests();
|
||||
if (gfx_thumbnail_has_file(&th, still))
|
||||
{
|
||||
printf("[FAIL] has_file: a superseded load recognised\n");
|
||||
ok16 = 0;
|
||||
}
|
||||
if (gt_still_cb)
|
||||
gt_still_cb(NULL, NULL, gt_still_ud, NULL);
|
||||
gfx_thumbnail_reset(&th);
|
||||
/* failed: no image came back, the viewer must try again */
|
||||
gt_still_cb = NULL;
|
||||
gfx_thumbnail_request_file(still, &th, 0);
|
||||
if (gt_still_cb)
|
||||
gt_still_cb(NULL, NULL, gt_still_ud, "decode failed");
|
||||
if (gfx_thumbnail_has_file(&th, still))
|
||||
{
|
||||
printf("[FAIL] has_file: a failed load recognised "
|
||||
"(status=%d)\n", (int)th.status);
|
||||
ok16 = 0;
|
||||
}
|
||||
gfx_thumbnail_reset(&th);
|
||||
gt_still_capture = 0;
|
||||
task_queue_deinit();
|
||||
remove(still);
|
||||
if (ok16)
|
||||
printf("[ok] has_file: a still is recognised in flight and "
|
||||
"shown, not for another path, after a reset, when "
|
||||
"superseded or when it failed\n");
|
||||
else
|
||||
bad = 1;
|
||||
}
|
||||
|
||||
remove(path);
|
||||
printf("%s\n", bad ? "FAILED" : "PASS");
|
||||
return bad;
|
||||
|
||||
Reference in new issue
Block a user