mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 08:10:30 -10:00
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:
1 parent
0e7a56f171
commit
9ed0db58c3
4 files changed
+61
-20
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
Reference in new issue
Block a user