video_thread: accept a lent frame at any offset and pitch in the slot

The zero-copy lend only counted when the core pushed exactly the
pointer it was given. A core that renders its whole surface into the
loan and then crops by pointer offset - beetle-psx trims overscan that
way, with its own stride - pushed a pointer past the slot's start,
the loan lapsed, and every frame was copied as if it had never asked.

A pushed frame that lies anywhere inside the lent slot is now
published from there, with the pitch the core pushed; the slot
records the offset and the worker presents from it. Rows past the
slot are clamped as before, now counting from the offset. A push
outside the slot still lapses the loan.

threaded_video_test: the harness core gains a mode that renders the
loan and pushes a cropped window of it (8 pixels in, 4 rows down, the
loan's pitch); the zero-copy lane runs in both modes. The cropped
lane publishes 0 of 59 grants zero-copy on the old wrapper and 39,
the same as the plain lane, on this one.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-09-26 23:03:35 +02:00
1 parent 0e7a56f171
commit 9ed0db58c3
4 files changed
+61 -20

No files matched your search

+18 -9
View File
@@ -1983,7 +1983,8 @@ static void video_thread_loop(void *data)
/* A dupe goes to the driver as the NULL the core
* sent, exactly as it does without the wrapper. */
const void *fdata = thr->frame.slot[slot].dupe
? NULL : thr->frame.slot[slot].buffer;
? NULL : thr->frame.slot[slot].buffer
+ thr->frame.slot[slot].offset;
unsigned fdims = thr->frame.slot[slot].dims;
unsigned fpitch = thr->frame.slot[slot].pitch;
if (fdata && thr->frame.slot[slot].convert)
@@ -2433,6 +2434,7 @@ static bool video_thread_frame(void *data, const void *frame_,
bool dropped = false;
bool zero_copy = false;
bool waited = false;
size_t lent_off = 0;
#ifdef HAVE_VIDEO_FILTER
unsigned filter_bpp = 0;
#endif
@@ -2577,16 +2579,22 @@ static bool video_thread_frame(void *data, const void *frame_,
/* A frame rendered straight into the lent slot: publish that slot,
* no copy. The loan kept it free, so it is still neither pending nor
* being rendered. Any other push means the core rendered elsewhere;
* the loan lapses and the slot is picked as usual. */
* being rendered. The frame may start anywhere inside the slot and
* carry any pitch: a core that renders a whole surface into the loan
* and crops by pointer offset, or keeps its own stride, is still in
* place. Any other push means the core rendered elsewhere; the loan
* lapses and the slot is picked as usual. */
if (thr->frame.lent >= 0)
{
unsigned l = (unsigned)thr->frame.lent;
const uint8_t *f = (const uint8_t*)frame_;
const uint8_t *b = thr->frame.slot[l].buffer;
thr->frame.lent = -1;
if (frame_ && frame_ == thr->frame.slot[l].buffer)
if (f && f >= b && f < b + thr->frame.buffer_size)
{
zero_copy = true;
slot = l;
lent_off = (size_t)(f - b);
}
else
thr->handoff.lapsed++;
@@ -2652,14 +2660,14 @@ static bool video_thread_frame(void *data, const void *frame_,
if (zero_copy)
{
/* Already in place; the slot's pitch is the one the core was
* given, which is what it rendered with. Rows past the slot
* are cropped as for a copied frame. */
/* Already in place, at lent_off into the slot and with the
* pitch the core pushed (the one it was given, or its own).
* Rows past the slot are cropped as for a copied frame. */
thr->frame.zero_copy_count++;
if (pitch)
copy_stride = (unsigned)pitch;
if ((size_t)height * copy_stride > thr->frame.buffer_size)
height = (unsigned)(thr->frame.buffer_size / copy_stride);
if (lent_off + (size_t)height * copy_stride > thr->frame.buffer_size)
height = (unsigned)((thr->frame.buffer_size - lent_off) / copy_stride);
}
else if (src)
{
@@ -2678,6 +2686,7 @@ static bool video_thread_frame(void *data, const void *frame_,
}
thr->frame.slot[slot].dims = VIDEO_SCALE_PACK(width, height);
thr->frame.slot[slot].offset = zero_copy ? lent_off : 0;
thr->frame.slot[slot].count = frame_count;
thr->frame.slot[slot].pushed_at = now;
thr->frame.slot[slot].hw_slot = hw_slot;
+4
View File
@@ -600,6 +600,10 @@ typedef struct thread_video
* which holds whatever frame was last put in it, an old one. */
bool dupe;
uint8_t *buffer;
/* Where the frame starts inside buffer: 0 for a copied frame,
* and for a lent frame whatever the core pushed, which may sit
* past the start (a core that crops by pointer offset). */
size_t offset;
unsigned dims;
unsigned pitch;
char msg[NAME_MAX_LENGTH];
+20 -3
View File
@@ -59,7 +59,12 @@ void retro_reset(void) { }
/* Set by the harness through the core's own export below: when on,
* retro_run asks the frontend for a framebuffer and renders into it,
* exercising the wrapper's zero-copy lend. Counts how often the ask
* was granted so the harness can check the lend actually happened. */
* was granted so the harness can check the lend actually happened.
* Mode 2 renders the whole loan but pushes a cropped window into it -
* a pointer past the start with the loan's pitch, the way a core that
* crops overscan by offset does - which must still be a lend. */
#define CROP_X 8
#define CROP_Y 4
static int harness_use_fb;
static unsigned harness_fb_granted;
@@ -76,6 +81,9 @@ void retro_run(void)
size_t pitch = W * 2;
runs++;
unsigned out_w = W, out_h = h;
const uint16_t *push = NULL;
if (harness_use_fb && h == H)
{
struct retro_framebuffer fb;
@@ -97,10 +105,19 @@ void retro_run(void)
for (i = 0; i < W * h; i++)
dst[i] = (uint16_t)(runs + i);
push = dst;
if (harness_use_fb == 2 && h == H)
{
/* Cropped window: CROP_Y rows down and CROP_X pixels in, at the
* full pitch. Same for a loan and for the core's own buffer. */
push = dst + CROP_Y * (pitch / 2) + CROP_X;
out_w = W - 2 * CROP_X;
out_h = H - 2 * CROP_Y;
}
if (runs % 3 == 0)
video_cb(NULL, W, h, pitch);
video_cb(NULL, out_w, out_h, pitch);
else
video_cb(dst, W, h, pitch);
video_cb(push, out_w, out_h, pitch);
}
size_t retro_serialize_size(void) { return 0; }
bool retro_serialize(void *data, size_t size) { (void)data; (void)size; return false; }
@@ -2663,7 +2663,10 @@ static void lane_waiter_call(void)
printf(" waiter-call lane: call ran on the waiting thread, before the reply\n");
}
static void lane_zero_copy(void)
/* Shared by the two zero-copy lanes: mode 1 pushes the loan's start,
* mode 2 a cropped window inside it (an offset pointer, the loan's
* pitch). Both must be published from the lent slot, no copy. */
static void lane_zero_copy_mode(int mode, const char *name)
{
unsigned had = failures;
dylib_t lib = runloop_state_get_ptr()->lib_handle;
@@ -2682,8 +2685,8 @@ static void lane_zero_copy(void)
if (menu_is_up())
command_event(CMD_EVENT_MENU_TOGGLE, NULL);
run_frames(10);
expect_wrapper(true, "zero-copy lane");
use_fb(1);
expect_wrapper(true, name);
use_fb(mode);
/* Most asks granted, and those frames published from the lent slot.
* Dupes (every third frame) ask and then push NULL, so the loan
@@ -2696,9 +2699,9 @@ static void lane_zero_copy(void)
video_thread_wait_idle();
g1 = granted();
slock_lock(thr->lock); zc1 = (unsigned)thr->frame.zero_copy_count; slock_unlock(thr->lock);
CHECK(g1 - g0 >= 30, "zero-copy on but only %u of 60 asks granted", g1 - g0);
CHECK(zc1 - zc0 >= 20, "only %u frames published zero-copy for %u grants", zc1 - zc0, g1 - g0);
CHECK(zc1 - zc0 <= g1 - g0, "%u zero-copy frames for %u grants", zc1 - zc0, g1 - g0);
CHECK(g1 - g0 >= 30, "%s: zero-copy on but only %u of 60 asks granted", name, g1 - g0);
CHECK(zc1 - zc0 >= 20, "%s: only %u frames published zero-copy for %u grants", name, zc1 - zc0, g1 - g0);
CHECK(zc1 - zc0 <= g1 - g0, "%s: %u zero-copy frames for %u grants", name, zc1 - zc0, g1 - g0);
use_fb(0);
if (!menu_is_up())
@@ -2706,8 +2709,16 @@ static void lane_zero_copy(void)
set_threaded_via_setting(false);
if (failures == had)
fprintf(stderr, "[pass] zero-copy lane (%u grants, %u zero-copy frames)\n",
g1 - g0, zc1 - zc0);
fprintf(stderr, "[pass] %s (%u grants, %u zero-copy frames)\n",
name, g1 - g0, zc1 - zc0);
}
static void lane_zero_copy(void)
{
lane_zero_copy_mode(1, "zero-copy lane");
/* The offset push used to lapse the loan (the wrapper matched the
* pointer to the slot's start) and copy every frame. */
lane_zero_copy_mode(2, "zero-copy lane, cropped window");
}
/* ------------------------------------------------------------------ */