From bf1f5a4a704937dc0a2220280633c8d95877eeb3 Mon Sep 17 00:00:00 2001 From: LibretroAdmin Date: Wed, 7 Oct 2026 09:36:18 +0200 Subject: [PATCH] gfx_surface: stills decode through the loader's request and set A still's decode asks the loader for what the driver takes - 10-bit where it samples it, the compressed payload as it lies in the file - through image_texture_load_request and its buffer twin, rather than an image_texture_load that only ever produced 8-bit. A set of files decodes through image_texture_load_set, the parallel decode moved to libretro-common as formats/image_texture_set.c: dispatch_apply on Apple, threads of its own elsewhere, in order on one, and image_texture_set_run for any other per-index work. The loader sample gains the set lanes: files decoded together with missing, empty and NULL paths left empty, and every index of a run called exactly once. --- Makefile.common | 3 +- gfx/gfx_surface.c | 122 +++------------ griffin/griffin.c | 1 + libretro-common/formats/image_texture.c | 147 ++++++++++-------- libretro-common/formats/image_texture_set.c | 138 ++++++++++++++++ libretro-common/include/formats/image.h | 28 ++++ .../samples/formats/image_loader/Makefile | 9 +- .../formats/image_loader/image_loader_test.c | 53 +++++++ samples/gfx/gfx_surface_still/Makefile | 8 +- .../gfx_surface_still_test.c | 14 +- samples/gfx/gfx_thumbnail_anim/Makefile | 1 + samples/gfx/gfx_thumbnail_preview/Makefile | 1 + samples/gfx/overlay_apng/Makefile | 1 + samples/input/overlay_textures/Makefile | 3 +- 14 files changed, 351 insertions(+), 178 deletions(-) create mode 100644 libretro-common/formats/image_texture_set.c diff --git a/Makefile.common b/Makefile.common index 0cebd9ee42..234254d18d 100644 --- a/Makefile.common +++ b/Makefile.common @@ -516,7 +516,8 @@ ifneq ($(HAVE_STRL), 1) endif OBJ += $(LIBRETRO_COMM_DIR)/formats/image_texture.o \ - $(LIBRETRO_COMM_DIR)/formats/image_texture_scale.o + $(LIBRETRO_COMM_DIR)/formats/image_texture_scale.o \ + $(LIBRETRO_COMM_DIR)/formats/image_texture_set.o ifeq ($(HAVE_IMAGEVIEWER), 1) DEFINES += -DHAVE_IMAGEVIEWER diff --git a/gfx/gfx_surface.c b/gfx/gfx_surface.c index f429d8c4bb..127b538802 100644 --- a/gfx/gfx_surface.c +++ b/gfx/gfx_surface.c @@ -17,13 +17,6 @@ #include #include -#include -#include -#ifdef HAVE_GCD -#include -#elif defined(HAVE_THREADS) -#include -#endif #include "gfx_surface.h" #include "gfx_instrument.h" @@ -689,17 +682,33 @@ gfx_surface_t *gfx_surface_still(gfx_surface_t **slot, return *slot; } +/* What a still's decode is asked for: the order the caller named, + * 10-bit where the driver samples it, a compressed payload as it lies + * in the file */ +static void gfx_surface_request(image_texture_request_t *req, + bool supports_rgba) +{ + gfx_surface_requirements_t want; + req->rgba = supports_rgba; + req->want_10bit = gfx_surface_query_requirements(0, &want) + && (want.formats & GFX_SURFACE_PIXFMT_2101010); + req->want_fp16 = false; + req->want_compressed = true; +} + bool gfx_surface_submit_buffer(gfx_surface_t *s, enum image_type_enum type, const void *buf, size_t len, bool supports_rgba) { + image_texture_request_t req; struct texture_image *img; if (!s || !buf || !len) return false; if (!(img = (struct texture_image*)calloc(1, sizeof(*img)))) return false; - img->supports_rgba = supports_rgba; - if (!image_texture_load_buffer(img, type, (void*)buf, len)) + gfx_surface_request(&req, supports_rgba); + if (!image_texture_load_buffer_request(img, type, buf, len, &req, + NULL, NULL)) { free(img); return false; @@ -723,13 +732,14 @@ bool gfx_surface_take_image(gfx_surface_t *s, struct texture_image *img) bool gfx_surface_submit_file(gfx_surface_t *s, const char *path, bool supports_rgba) { + image_texture_request_t req; struct texture_image *img; if (!s || !path || !*path) return false; if (!(img = (struct texture_image*)calloc(1, sizeof(*img)))) return false; - img->supports_rgba = supports_rgba; - if (!image_texture_load(img, path)) + gfx_surface_request(&req, supports_rgba); + if (!image_texture_load_request(img, path, &req, NULL, NULL)) { free(img); return false; @@ -737,85 +747,6 @@ bool gfx_surface_submit_file(gfx_surface_t *s, const char *path, return gfx_surface_submit_image(s, img); } -/* A set of files decoded together: the next index a worker takes, - * and the images they come back as */ -typedef struct -{ - const char *const *paths; - struct texture_image *imgs; - retro_atomic_int_t next; - unsigned n; - bool supports_rgba; -} gfx_surface_decode_set_t; - -static void gfx_surface_decode_one(gfx_surface_decode_set_t *set, - unsigned i) -{ - struct texture_image *img = &set->imgs[i]; - img->supports_rgba = set->supports_rgba; - if (!set->paths[i] || !*set->paths[i] - || !image_texture_load(img, set->paths[i])) - img->pixels = NULL; -} - -#if !defined(HAVE_GCD) && defined(HAVE_THREADS) -static void gfx_surface_decode_set_run(void *data) -{ - gfx_surface_decode_set_t *set = (gfx_surface_decode_set_t*)data; - for (;;) - { - unsigned i = (unsigned)retro_atomic_fetch_add_int(&set->next, 1); - if (i >= set->n) - return; - gfx_surface_decode_one(set, i); - } -} -#endif - -/* The set, decoded: on Apple over the dispatch pool the task queue's - * decodes already run on, elsewhere on threads of its own, and on one - * thread where there is only one */ -static void gfx_surface_decode_set(gfx_surface_decode_set_t *set) -{ -#ifdef HAVE_GCD - if (set->n > 1) - { - gfx_surface_decode_set_t *at = set; - dispatch_apply(set->n, - dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), - ^(size_t i) { gfx_surface_decode_one(at, (unsigned)i); }); - return; - } -#elif defined(HAVE_THREADS) - { - unsigned workers = cpu_features_get_core_amount(); - if (workers > 8) - workers = 8; - if (workers > 1 && set->n > 1) - { - /* This thread decodes alongside the others; a worker that - * could not be made costs nothing but its share */ - sthread_t *thread[8]; - unsigned t, spawned = workers - 1; - if (spawned > set->n - 1) - spawned = set->n - 1; - for (t = 0; t < spawned; t++) - thread[t] = sthread_create(gfx_surface_decode_set_run, set); - gfx_surface_decode_set_run(set); - for (t = 0; t < spawned; t++) - if (thread[t]) - sthread_join(thread[t]); - return; - } - } -#endif - { - unsigned i; - for (i = 0; i < set->n; i++) - gfx_surface_decode_one(set, i); - } -} - /* At most this many decodes in hand before they go up, whatever * the set's size: a theme's icons are small, but there are many */ #define GFX_SURFACE_DECODE_BATCH 32 @@ -824,14 +755,13 @@ unsigned gfx_surface_submit_files(gfx_surface_t *const *slots, const char *const *paths, unsigned n, bool supports_rgba) { struct texture_image imgs[GFX_SURFACE_DECODE_BATCH]; - gfx_surface_decode_set_t set; + image_texture_request_t req; unsigned done = 0, first; if (!slots || !paths) return 0; - set.imgs = imgs; - set.supports_rgba = supports_rgba; + gfx_surface_request(&req, supports_rgba); for (first = 0; first < n; first += GFX_SURFACE_DECODE_BATCH) { @@ -839,11 +769,7 @@ unsigned gfx_surface_submit_files(gfx_surface_t *const *slots, unsigned count = n - first; if (count > GFX_SURFACE_DECODE_BATCH) count = GFX_SURFACE_DECODE_BATCH; - memset(imgs, 0, count * sizeof(*imgs)); - set.paths = paths + first; - set.n = count; - retro_atomic_int_init(&set.next, 0); - gfx_surface_decode_set(&set); + image_texture_load_set(paths + first, imgs, count, &req); /* Up, in order, on this thread */ for (i = 0; i < count; i++) diff --git a/griffin/griffin.c b/griffin/griffin.c index c94506af3f..0ba1e7c42f 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -451,6 +451,7 @@ VIDEO IMAGE #include "../libretro-common/formats/image_texture.c" #include "../libretro-common/formats/image_texture_scale.c" +#include "../libretro-common/formats/image_texture_set.c" #ifdef HAVE_RTGA #include "../libretro-common/formats/tga/rtga.c" diff --git a/libretro-common/formats/image_texture.c b/libretro-common/formats/image_texture.c index 4f88651b90..4af71cb612 100644 --- a/libretro-common/formats/image_texture.c +++ b/libretro-common/formats/image_texture.c @@ -438,26 +438,33 @@ void image_loader_free(image_loader_t *l) free(l); } -/* The loader run to completion over a whole buffer: the request from - * the caller's image, whose ->pix10 asks for 10-bit on the way in. */ -static bool image_texture_load_internal( - enum image_type_enum type, - void *ptr, - size_t len, - struct texture_image *out_img, +static void image_texture_clear(struct texture_image *img) +{ + img->supports_rgba = false; + img->pixels = NULL; + img->width = 0; + img->height = 0; + img->compressed = NULL; + img->pix10 = false; + img->fp16 = false; +} + +bool image_texture_load_buffer_request(struct texture_image *out_img, + enum image_type_enum type, const void *ptr, size_t len, + const image_texture_request_t *req, bool (*should_abort)(void *ud), void *abort_ud) { - image_texture_request_t req; image_loader_t *l; enum image_loader_state st; - req.rgba = out_img->supports_rgba; - req.want_10bit = out_img->pix10; - req.want_fp16 = false; - req.want_compressed = true; - out_img->compressed = NULL; - if (!(l = image_loader_new(type, &req))) + if (!out_img) return false; + out_img->compressed = NULL; + if (!ptr || !len || !(l = image_loader_new(type, req))) + { + image_texture_clear(out_img); + return false; + } image_loader_set_abort(l, should_abort, abort_ud); if (!image_loader_start(l, ptr, len, len)) { @@ -471,9 +478,59 @@ static bool image_texture_load_internal( return true; } image_loader_free(l); + image_texture_clear(out_img); return false; } +bool image_texture_load_request(struct texture_image *out_img, + const char *path, const image_texture_request_t *req, + bool (*should_abort)(void *ud), void *ud) +{ + enum image_type_enum type = image_texture_get_type(path); + + if (type != IMAGE_TYPE_NONE) + { + /* The synchronous read rides the data_transfer prefix spine + * like every other loader: filestream/VFS routing (overlays + * and driver assets from archive members or content:// + * documents), 64-bit lengths, the hardware guard behind the + * read, and honest short-read detection. A zero budget fills + * to completion in one blocking call, which is this API's + * contract. */ + struct data_transfer *dt = data_transfer_open_prefix(path, 0); + if (dt) + { + size_t file_len = 0; + const uint8_t *ptr = NULL; + + data_transfer_iterate(dt, 0); + ptr = data_transfer_ptr(dt, &file_len); + if (data_transfer_complete(dt) && ptr && file_len + && image_texture_load_buffer_request(out_img, type, + ptr, file_len, req, should_abort, ud)) + { + data_transfer_free(dt); + return true; + } + data_transfer_free(dt); + } + } + if (out_img) + image_texture_clear(out_img); + return false; +} + +/* The request the plain loads carry: the caller's image, whose + * ->pix10 asks for 10-bit on the way in */ +static void image_texture_request_of(const struct texture_image *img, + image_texture_request_t *req) +{ + req->rgba = img->supports_rgba; + req->want_10bit = img->pix10; + req->want_fp16 = false; + req->want_compressed = true; +} + bool image_texture_load(struct texture_image *out_img, const char *path) { return image_texture_load_ex(out_img, path, NULL, NULL); @@ -648,63 +705,17 @@ bool image_texture_tile_gx(struct texture_image *img) bool image_texture_load_buffer(struct texture_image *out_img, enum image_type_enum type, void *buffer, size_t buffer_len) { - if (type != IMAGE_TYPE_NONE) - { - if (image_texture_load_internal( - type, buffer, buffer_len, out_img, NULL, NULL)) - return true; - } - - out_img->supports_rgba = false; - out_img->pixels = NULL; - out_img->width = 0; - out_img->height = 0; - out_img->compressed = NULL; - - return false; + image_texture_request_t req; + image_texture_request_of(out_img, &req); + return image_texture_load_buffer_request(out_img, type, buffer, + buffer_len, &req, NULL, NULL); } bool image_texture_load_ex(struct texture_image *out_img, const char *path, bool (*should_abort)(void *ud), void *ud) { - enum image_type_enum type = image_texture_get_type(path); - - if (type != IMAGE_TYPE_NONE) - { - /* The synchronous read rides the data_transfer prefix spine - * like every other loader: filestream/VFS routing (overlays - * and driver assets from archive members or content:// - * documents), 64-bit lengths, the hardware guard behind the - * read, and honest short-read detection. A zero budget fills - * to completion in one blocking call, which is this API's - * contract. */ - struct data_transfer *dt = data_transfer_open_prefix(path, 0); - if (dt) - { - size_t file_len = 0; - const uint8_t *ptr = NULL; - - data_transfer_iterate(dt, 0); - ptr = data_transfer_ptr(dt, &file_len); - if (data_transfer_complete(dt) && ptr && file_len - && image_texture_load_internal( - type, - (void*)ptr, file_len, out_img, should_abort, ud)) - { - data_transfer_free(dt); - return true; - } - data_transfer_free(dt); - } - } - - out_img->supports_rgba = false; - out_img->pixels = NULL; - out_img->width = 0; - out_img->height = 0; - out_img->compressed = NULL; - out_img->pix10 = false; - out_img->fp16 = false; - - return false; + image_texture_request_t req; + image_texture_request_of(out_img, &req); + return image_texture_load_request(out_img, path, &req, + should_abort, ud); } diff --git a/libretro-common/formats/image_texture_set.c b/libretro-common/formats/image_texture_set.c new file mode 100644 index 0000000000..a0efa4c162 --- /dev/null +++ b/libretro-common/formats/image_texture_set.c @@ -0,0 +1,138 @@ +/* Copyright (C) 2010-2026 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (image_texture_set.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include +#include +#ifdef HAVE_GCD +#include +#elif defined(HAVE_THREADS) +#include +#include +#include +#endif + +#if !defined(HAVE_GCD) && defined(HAVE_THREADS) +/* The next index a worker takes */ +typedef struct +{ + void (*fn)(unsigned i, void *ud); + void *ud; + retro_atomic_int_t next; + unsigned n; +} image_texture_set_t; + +static void image_texture_set_worker(void *data) +{ + image_texture_set_t *set = (image_texture_set_t*)data; + for (;;) + { + unsigned i = (unsigned)retro_atomic_fetch_add_int(&set->next, 1); + if (i >= set->n) + return; + set->fn(i, set->ud); + } +} +#endif + +void image_texture_set_run(unsigned n, + void (*fn)(unsigned i, void *ud), void *ud) +{ + unsigned i; + if (!n || !fn) + return; +#ifdef HAVE_GCD + if (n > 1) + { + dispatch_apply(n, + dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), + ^(size_t at) { fn((unsigned)at, ud); }); + return; + } +#elif defined(HAVE_THREADS) + { + unsigned workers = cpu_features_get_core_amount(); + if (workers > 8) + workers = 8; + if (workers > 1 && n > 1) + { + /* This thread works alongside the others; a worker that + * could not be made costs nothing but its share */ + image_texture_set_t set; + sthread_t *thread[8]; + unsigned t, spawned = workers - 1; + set.fn = fn; + set.ud = ud; + set.n = n; + retro_atomic_int_init(&set.next, 0); + if (spawned > n - 1) + spawned = n - 1; + for (t = 0; t < spawned; t++) + thread[t] = sthread_create(image_texture_set_worker, &set); + image_texture_set_worker(&set); + for (t = 0; t < spawned; t++) + if (thread[t]) + sthread_join(thread[t]); + return; + } + } +#endif + for (i = 0; i < n; i++) + fn(i, ud); +} + +typedef struct +{ + const char *const *paths; + struct texture_image *imgs; + const image_texture_request_t *req; +} image_texture_load_set_t; + +static void image_texture_load_one(unsigned i, void *ud) +{ + image_texture_load_set_t *set = (image_texture_load_set_t*)ud; + struct texture_image *img = &set->imgs[i]; + if ( !set->paths[i] || !*set->paths[i] + || !image_texture_load_request(img, set->paths[i], set->req, + NULL, NULL)) + img->pixels = NULL; +} + +unsigned image_texture_load_set(const char *const *paths, + struct texture_image *imgs, unsigned n, + const image_texture_request_t *req) +{ + image_texture_load_set_t set; + unsigned i, done = 0; + if (!paths || !imgs || !n) + return 0; + memset(imgs, 0, (size_t)n * sizeof(*imgs)); + set.paths = paths; + set.imgs = imgs; + set.req = req; + image_texture_set_run(n, image_texture_load_one, &set); + for (i = 0; i < n; i++) + if (imgs[i].pixels || imgs[i].compressed) + done++; + return done; +} diff --git a/libretro-common/include/formats/image.h b/libretro-common/include/formats/image.h index 5f1099d0f0..16060da0f9 100644 --- a/libretro-common/include/formats/image.h +++ b/libretro-common/include/formats/image.h @@ -260,6 +260,34 @@ bool image_texture_scale(struct texture_image *img, bool image_texture_load_buffer(struct texture_image *img, enum image_type_enum type, void *s, size_t len); +/* The loader run to completion over a whole buffer or a file, with + * @req naming what the caller takes (NULL: the ordinary image). The + * abort hook is asked between steps; it may be NULL. False leaves + * @img empty. */ +bool image_texture_load_buffer_request(struct texture_image *img, + enum image_type_enum type, const void *buf, size_t len, + const image_texture_request_t *req, + bool (*should_abort)(void *ud), void *ud); +bool image_texture_load_request(struct texture_image *img, + const char *path, const image_texture_request_t *req, + bool (*should_abort)(void *ud), void *ud); + +/* Run @fn for every index below @n, across the cores at once where + * there are threads: on Apple over the dispatch pool, elsewhere on + * threads of its own with the caller working alongside, and in order + * on one thread where there is only one. Returns once every call has. + * In formats/image_texture_set.c. */ +void image_texture_set_run(unsigned n, + void (*fn)(unsigned i, void *ud), void *ud); + +/* Decode the @n files @paths name into @imgs together, each as + * image_texture_load_request does with @req; a path that is NULL, + * empty or unreadable leaves its image empty. Returns how many + * decoded. In formats/image_texture_set.c. */ +unsigned image_texture_load_set(const char *const *paths, + struct texture_image *imgs, unsigned n, + const image_texture_request_t *req); + /* ->pix10 is an ask on the way in and an answer on the way out: set * it before the call to have a decoder that can emit XRGB2101010 do * so, and read it after to find out whether it did. Clear on entry diff --git a/libretro-common/samples/formats/image_loader/Makefile b/libretro-common/samples/formats/image_loader/Makefile index f9ef29b762..a9159beea5 100644 --- a/libretro-common/samples/formats/image_loader/Makefile +++ b/libretro-common/samples/formats/image_loader/Makefile @@ -3,7 +3,8 @@ TARGET := image_loader_test # The still loader (formats/image_texture.c) and the scale unit # (formats/image_texture_scale.c), driven as a frontend task or a core # would: whole buffer, timed steps against a fake clock, a prefix that -# grows, an abort, the APNG verdict, the request's answers. +# grows, an abort, the APNG verdict, the request's answers, and the +# set decode (formats/image_texture_set.c) across threads. LIBRETRO_COMM_DIR := ../../.. SOURCES := image_loader_test.c \ @@ -18,6 +19,7 @@ SOURCES := image_loader_test.c \ $(LIBRETRO_COMM_DIR)/formats/data_transfer.c \ $(LIBRETRO_COMM_DIR)/formats/image_texture.c \ $(LIBRETRO_COMM_DIR)/formats/image_texture_scale.c \ + $(LIBRETRO_COMM_DIR)/formats/image_texture_set.c \ $(LIBRETRO_COMM_DIR)/formats/image_transfer.c \ $(LIBRETRO_COMM_DIR)/formats/png/rpng.c \ $(LIBRETRO_COMM_DIR)/formats/png/rpng_apng.c \ @@ -26,6 +28,7 @@ SOURCES := image_loader_test.c \ $(LIBRETRO_COMM_DIR)/gfx/scaler/scaler_filter.c \ $(LIBRETRO_COMM_DIR)/gfx/scaler/scaler_int.c \ $(LIBRETRO_COMM_DIR)/memmap/memmap.c \ + $(LIBRETRO_COMM_DIR)/rthreads/rthreads.c \ $(LIBRETRO_COMM_DIR)/streams/file_stream.c \ $(LIBRETRO_COMM_DIR)/streams/trans_stream.c \ $(LIBRETRO_COMM_DIR)/streams/trans_stream_deflate.c \ @@ -37,7 +40,7 @@ SOURCES := image_loader_test.c \ OBJS := $(SOURCES:.c=.o) -CFLAGS += -Wall -pedantic -std=gnu99 -g -O0 -DHAVE_RPNG \ +CFLAGS += -Wall -pedantic -std=gnu99 -g -O0 -DHAVE_RPNG -DHAVE_THREADS \ -I$(LIBRETRO_COMM_DIR)/include ifneq ($(SANITIZER),) @@ -51,7 +54,7 @@ all: $(TARGET) $(CC) -c -o $@ $< $(CFLAGS) $(TARGET): $(OBJS) - $(CC) -o $@ $^ $(LDFLAGS) -lm + $(CC) -o $@ $^ $(LDFLAGS) -lm -lpthread clean: rm -f $(TARGET) $(TARGET).exe $(OBJS) diff --git a/libretro-common/samples/formats/image_loader/image_loader_test.c b/libretro-common/samples/formats/image_loader/image_loader_test.c index 2ae3978e33..1b4ff61872 100644 --- a/libretro-common/samples/formats/image_loader/image_loader_test.c +++ b/libretro-common/samples/formats/image_loader/image_loader_test.c @@ -12,6 +12,8 @@ * 6 the request's channel order is answered and honoured * 7 image_texture_scale: the cap, the whole-factor upscale, and * a half-float image left alone + * 8 image_texture_load_set: a set of files decoded together, an + * unreadable one left empty, every index run exactly once * * The PNG is built here - RGB, every row filter 0, the zlib stream * stored and split across many small IDATs so the transfer has many @@ -187,6 +189,11 @@ static int64_t fake_now(void) return fake_now_v; } +static void set_hit(unsigned i, void *ud) +{ + ((unsigned*)ud)[i]++; +} + static int aborts; static bool abort_now(void *ud) { @@ -339,6 +346,52 @@ int main(void) free(s.pixels); } + /* 8: a set */ + { + static const char *names[5] = { + "image_loader_set_0.png", "image_loader_set_1.png", + "image_loader_none.png", "", "image_loader_set_2.png" }; + const char *paths[6]; + struct texture_image imgs[6]; + unsigned i, ok = 1, got; + for (i = 0; i < 5; i++) + { + FILE *f; + paths[i] = names[i]; + if (i == 2 || i == 3) + continue; + if ((f = fopen(names[i], "wb"))) + { + fwrite(png, 1, len, f); + fclose(f); + } + } + paths[5] = NULL; + req.rgba = false; + got = image_texture_load_set(paths, imgs, 6, &req); + for (i = 0; i < 6; i++) + { + bool want = (i == 0 || i == 1 || i == 4); + if (want ? !pixels_match(&imgs[i], false) : (imgs[i].pixels != NULL)) + ok = 0; + image_texture_free(&imgs[i]); + } + CHECK(got == 3 && ok, "a set decodes its files; missing, empty and NULL paths stay empty"); + for (i = 0; i < 5; i++) + if (i != 2 && i != 3) + remove(names[i]); + } + { + static unsigned hits[64]; + unsigned i, ok = 1; + memset(hits, 0, sizeof(hits)); + image_texture_set_run(64, set_hit, hits); + for (i = 0; i < 64; i++) + if (hits[i] != 1) + ok = 0; + CHECK(ok, "image_texture_set_run calls every index exactly once"); + } + image_texture_free(&ref); free(png); free(apng); diff --git a/samples/gfx/gfx_surface_still/Makefile b/samples/gfx/gfx_surface_still/Makefile index f9d24ee57e..ea25486b5f 100644 --- a/samples/gfx/gfx_surface_still/Makefile +++ b/samples/gfx/gfx_surface_still/Makefile @@ -1,13 +1,15 @@ # gfx_surface stills against a stub driver, a simulated threaded -# wrapper, a held task queue and a stub image decoder: a still owns -# its texture across uploads, decodes and a free in flight, and a set -# of files is decoded across threads and goes up in order. +# wrapper, a held task queue and a stub image decoder under the real +# set decode (image_texture_set.c): a still owns its texture across +# uploads, decodes and a free in flight, and a set of files is decoded +# across threads and goes up in order. RA := ../../.. CC ?= gcc CFLAGS += -Wall -Wextra -g -O1 -std=gnu99 -I$(RA) -I$(RA)/libretro-common/include \ -I$(RA)/deps -DHAVE_THREADS -DRARCH_INTERNAL $(EXTRA_CFLAGS) SRC := gfx_surface_still_test.c \ $(RA)/gfx/gfx_surface.c $(RA)/gfx/gfx_instrument.c \ + $(RA)/libretro-common/formats/image_texture_set.c \ $(RA)/libretro-common/compat/compat_strl.c \ $(RA)/libretro-common/rthreads/rthreads.c \ $(RA)/libretro-common/features/features_cpu.c \ diff --git a/samples/gfx/gfx_surface_still/gfx_surface_still_test.c b/samples/gfx/gfx_surface_still/gfx_surface_still_test.c index b2e5cf1e31..0d1410ea4c 100644 --- a/samples/gfx/gfx_surface_still/gfx_surface_still_test.c +++ b/samples/gfx/gfx_surface_still/gfx_surface_still_test.c @@ -131,9 +131,12 @@ static uintptr_t st_decode_thread[ST_DECODE_THREADS]; static unsigned st_decode_threads; static slock_t *st_decode_lock; -bool image_texture_load(struct texture_image *img, const char *path) +bool image_texture_load_request(struct texture_image *img, + const char *path, const image_texture_request_t *req, + bool (*should_abort)(void *ud), void *ud) { unsigned w = (unsigned)atoi(path); + (void)req; (void)should_abort; (void)ud; if (!w) return false; retro_sleep(2); @@ -153,10 +156,13 @@ bool image_texture_load(struct texture_image *img, const char *path) return true; } -bool image_texture_load_buffer(struct texture_image *img, - enum image_type_enum type, void *s, size_t len) +bool image_texture_load_buffer_request(struct texture_image *img, + enum image_type_enum type, const void *s, size_t len, + const image_texture_request_t *req, + bool (*should_abort)(void *ud), void *ud) { - (void)img; (void)type; (void)s; (void)len; + (void)img; (void)type; (void)s; (void)len; (void)req; + (void)should_abort; (void)ud; return false; } diff --git a/samples/gfx/gfx_thumbnail_anim/Makefile b/samples/gfx/gfx_thumbnail_anim/Makefile index 402e94a86d..176c60f9ae 100644 --- a/samples/gfx/gfx_thumbnail_anim/Makefile +++ b/samples/gfx/gfx_thumbnail_anim/Makefile @@ -36,6 +36,7 @@ SOURCES := gfx_thumbnail_anim_test.c \ $(LIBRETRO_COMM_DIR)/file/file_path_io.c \ $(LIBRETRO_COMM_DIR)/formats/data_transfer.c \ $(LIBRETRO_COMM_DIR)/formats/image_texture.c \ + $(LIBRETRO_COMM_DIR)/formats/image_texture_set.c \ $(LIBRETRO_COMM_DIR)/formats/image_transfer.c \ $(LIBRETRO_COMM_DIR)/formats/vp8/rvp8.c \ $(LIBRETRO_COMM_DIR)/formats/webp/rwebp.c \ diff --git a/samples/gfx/gfx_thumbnail_preview/Makefile b/samples/gfx/gfx_thumbnail_preview/Makefile index eb19abef02..cff6b94f07 100644 --- a/samples/gfx/gfx_thumbnail_preview/Makefile +++ b/samples/gfx/gfx_thumbnail_preview/Makefile @@ -52,6 +52,7 @@ SOURCES := gfx_thumbnail_preview_test.c \ $(LIBRETRO_COMM_DIR)/formats/audio_transfer.c \ $(LIBRETRO_COMM_DIR)/formats/data_transfer.c \ $(LIBRETRO_COMM_DIR)/formats/image_texture.c \ + $(LIBRETRO_COMM_DIR)/formats/image_texture_set.c \ $(LIBRETRO_COMM_DIR)/formats/image_transfer.c \ $(LIBRETRO_COMM_DIR)/formats/image/image_hdr_blit.c \ $(LIBRETRO_COMM_DIR)/formats/image/image_blit_bands.c \ diff --git a/samples/gfx/overlay_apng/Makefile b/samples/gfx/overlay_apng/Makefile index 32bbd54a28..1ba4c3f61c 100644 --- a/samples/gfx/overlay_apng/Makefile +++ b/samples/gfx/overlay_apng/Makefile @@ -18,6 +18,7 @@ SOURCES := \ $(LIBRETRO_COMM_DIR)/file/file_path.c \ $(LIBRETRO_COMM_DIR)/file/file_path_io.c \ $(LIBRETRO_COMM_DIR)/formats/image_texture.c \ + $(LIBRETRO_COMM_DIR)/formats/image_texture_set.c \ $(LIBRETRO_COMM_DIR)/formats/png/rpng.c \ $(LIBRETRO_COMM_DIR)/formats/png/rpng_apng.c \ $(LIBRETRO_COMM_DIR)/lists/string_list.c \ diff --git a/samples/input/overlay_textures/Makefile b/samples/input/overlay_textures/Makefile index 7c61d58525..71898be90a 100644 --- a/samples/input/overlay_textures/Makefile +++ b/samples/input/overlay_textures/Makefile @@ -12,7 +12,8 @@ SOURCES := \ $(REPO_ROOT)/input/input_overlay_textures.c \ $(REPO_ROOT)/input/input_overlay_alpha.c \ $(REPO_ROOT)/gfx/gfx_surface.c \ - $(LIBRETRO_COMM_DIR)/formats/image_texture.c + $(LIBRETRO_COMM_DIR)/formats/image_texture.c \ + $(LIBRETRO_COMM_DIR)/formats/image_texture_set.c OBJS := $(SOURCES:.c=.o)