mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
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.
This commit is contained in:
1 parent
9773b6956b
commit
bf1f5a4a70
14 files changed
+351
-178
No files matched your search
+2
-1
@@ -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
|
||||
|
||||
+24
-98
@@ -17,13 +17,6 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <features/features_cpu.h>
|
||||
#include <retro_atomic.h>
|
||||
#ifdef HAVE_GCD
|
||||
#include <dispatch/dispatch.h>
|
||||
#elif defined(HAVE_THREADS)
|
||||
#include <rthreads/rthreads.h>
|
||||
#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++)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <boolean.h>
|
||||
#include <formats/image.h>
|
||||
#ifdef HAVE_GCD
|
||||
#include <dispatch/dispatch.h>
|
||||
#elif defined(HAVE_THREADS)
|
||||
#include <retro_atomic.h>
|
||||
#include <rthreads/rthreads.h>
|
||||
#include <features/features_cpu.h>
|
||||
#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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in new issue
Block a user