Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions include/modavd_extras.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ enum {
MODAVD_OK = 0,
MODAVD_E_INVALID = -1, /* bad argument */
MODAVD_E_NOT_FOUND = -2, /* nothing answers on that console port */
MODAVD_E_NOT_READY = -3, /* frame sharing is off, or no frame was composed yet */
MODAVD_E_NOT_READY = -3, /* frame sharing is off, no frame was composed yet, or (capture) new frames
kept arriving during the copy: try again */
MODAVD_E_RESIZED = -4, /* the shared frame no longer fits the mapping: close and reopen */
MODAVD_E_BUFFER = -5, /* buffer too small; the frame info says how big it must be */
MODAVD_E_IO = -6, /* an OS call failed */
Expand Down Expand Up @@ -141,7 +142,9 @@ MODAVD_API int modavd_console_port(modavd_handle h); /* > 0, or MODAVD_E_IN

/* Copies the latest frame of `display` (only 0 for now) into buf.
* With buf == NULL or buf_size == 0 it only fills info (a size probe).
* MODAVD_E_NOT_READY: no frame yet. MODAVD_E_RESIZED: reopen the handle.
* MODAVD_E_NOT_READY: no frame yet, or the emulator published a new frame
* during each of the copies, so buf may mix two frames;
* call again. MODAVD_E_RESIZED: reopen the handle.
* MODAVD_E_BUFFER: buf_size < info->stride * info->height. */
MODAVD_API int modavd_capture(modavd_handle h, int display, uint32_t format, uint32_t flags, void* buf,
size_t buf_size, modavd_frame_info* info);
Expand Down
9 changes: 8 additions & 1 deletion shim/mumu_shim.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,14 @@ EXPORT int nemu_capture_display(int handle, unsigned int display_id, int buffer_
memset(&info, 0, sizeof(info));
info.size = sizeof(info);
size_t size = (buffer_size > 0 && pixels) ? (size_t)buffer_size : 0;
int rc = modavd_capture(h, 0, MODAVD_FMT_RGBA, MODAVD_CAPTURE_BOTTOM_UP, size ? pixels : NULL, size, &info);
/* mx_copy_frame, not modavd_capture: when a new frame arrived during every
* copy, hand the last copy over as the DLL this replaces did, instead of
* failing (MaaCore would drop to its screencap speed test again). */
int rc = mx_copy_frame(&h->shm, MODAVD_FMT_RGBA, MODAVD_CAPTURE_BOTTOM_UP, h->simd, size ? pixels : NULL, size,
&info);
if (rc == MX_TORN) {
rc = MODAVD_OK;
}
if (rc == MODAVD_OK || rc == MODAVD_E_BUFFER) {
if (width) {
*width = info.width;
Expand Down
18 changes: 15 additions & 3 deletions src/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,9 @@ void mx_convert_row(const uint8_t* src, uint8_t* dst, uint32_t width, uint32_t f
}
}

void (*mx_test_after_copy)(void* user) = NULL;
void* mx_test_after_copy_user = NULL;

int mx_copy_frame(const mx_shm* shm, uint32_t format, uint32_t flags, int simd, void* buf, size_t buf_size,
modavd_frame_info* info)
{
Expand Down Expand Up @@ -316,14 +319,17 @@ int mx_copy_frame(const mx_shm* shm, uint32_t format, uint32_t flags, int simd,
uint32_t sy = (flags & MODAVD_CAPTURE_BOTTOM_UP) ? h - 1 - y : y;
mx_convert_row(src + (size_t)sy * src_stride, dst + (size_t)y * (size_t)info->stride, w, format, simd);
}
if (mx_test_after_copy) {
mx_test_after_copy(mx_test_after_copy_user);
}
uint32_t after = vi->frame_number;
info->frame_number = after;
info->timestamp_us = vi->ts_us;
if (after == before) {
break;
return MODAVD_OK;
}
}
return MODAVD_OK;
return MX_TORN;
}

int modavd_capture(modavd_handle h, int display, uint32_t format, uint32_t flags, void* buf, size_t buf_size,
Expand All @@ -335,7 +341,13 @@ int modavd_capture(modavd_handle h, int display, uint32_t format, uint32_t flags
if (display != 0) {
return MODAVD_E_UNSUPPORTED;
}
return mx_copy_frame(&h->shm, format, flags, h->simd, buf, buf_size, info);
int rc = mx_copy_frame(&h->shm, format, flags, h->simd, buf, buf_size, info);
if (rc == MX_TORN) {
mx_log(MODAVD_LOG_WARN, "port=%d: a new frame arrived during each of %d copies", h->console_port,
MX_MAX_COPY_ATTEMPTS);
return MODAVD_E_NOT_READY;
}
return rc;
}

/* Input arrives in the next version. */
Expand Down
16 changes: 15 additions & 1 deletion src/mx_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ typedef struct mx_video_info {
#define MX_MAX_COPY_ATTEMPTS 3
#define MX_DEFAULT_TIMEOUT_MS 2000

/* mx_copy_frame() when the emulator published a new frame during every one
* of the MX_MAX_COPY_ATTEMPTS copies: the buffer may mix two frames. Internal
* only (positive, never a MODAVD_* code): modavd_capture() reports
* MODAVD_E_NOT_READY; the MuMu-shaped shim hands the last copy over, as the
* DLL it replaces did. */
#define MX_TORN 1

/* ---- log.c ---- */
void mx_log(int level, const char* fmt, ...)
#if defined(__GNUC__)
Expand Down Expand Up @@ -75,10 +82,17 @@ int mx_cpu_simd(void);
* simd: the value of mx_cpu_simd(), or 0 for the portable code only. */
void mx_convert_row(const uint8_t* src, uint8_t* dst, uint32_t width, uint32_t format, int simd);

/* Copies one frame; the work behind modavd_capture(). */
/* Copies one frame; the work behind modavd_capture(). MODAVD_OK, a MODAVD_E_*
* code, or MX_TORN. */
int mx_copy_frame(const mx_shm* shm, uint32_t format, uint32_t flags, int simd, void* buf, size_t buf_size,
modavd_frame_info* info);

/* Tests only: when set, mx_copy_frame() calls it after each copy, before it
* reads the frame number again, so a test can publish a frame mid-copy. NULL
* everywhere else. */
extern void (*mx_test_after_copy)(void* user);
extern void* mx_test_after_copy_user;

#ifdef __cplusplus
}
#endif
Expand Down
70 changes: 70 additions & 0 deletions tests/test_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,75 @@ static void test_capture_from_fake_frame(void)
fake_frame_destroy(&f);
}

/* The emulator writes without a lock. The hook publishes a frame after a copy,
* as if the emulator wrote one meanwhile. */
typedef struct publish_state {
fake_frame* frame;
int remaining; /* how many more copies get a new frame after them */
int calls;
} publish_state;

static void publish_after_copy(void* user)
{
publish_state* s = (publish_state*)user;
++s->calls;
if (s->remaining > 0) {
--s->remaining;
++((mx_video_info*)s->frame->view)->frame_number;
}
}

/* One new frame mid-copy: the next copy is clean. A new frame during every
* copy: MODAVD_E_NOT_READY rather than a frame that may mix two. */
static void test_capture_new_frame_mid_copy(void)
{
const int port = 65020;
const uint32_t w = 8, hgt = 4;
fake_frame f;
if (fake_frame_create(&f, port, w, hgt, w, hgt) != 0) {
fprintf(stderr, "cannot create the fake frame\n");
++g_failures;
return;
}
modavd_open_params p;
memset(&p, 0, sizeof(p));
p.size = sizeof(p);
p.console_port = port;
p.flags = MODAVD_OPEN_NO_START;
modavd_handle h = NULL;
CHECK_EQ(modavd_open(&p, &h), MODAVD_OK);
if (h) {
uint8_t buf[8 * 4 * 4];
modavd_frame_info info;
memset(&info, 0, sizeof(info));
info.size = sizeof(info);
uint32_t first = ((mx_video_info*)f.view)->frame_number;
publish_state s = { &f, 1, 0 };
mx_test_after_copy = publish_after_copy;
mx_test_after_copy_user = &s;

CHECK_EQ(modavd_capture(h, 0, MODAVD_FMT_BGRA, 0, buf, sizeof(buf), &info), MODAVD_OK);
CHECK_EQ(s.calls, 2);
CHECK_EQ(info.frame_number, first + 1);

s.remaining = MX_MAX_COPY_ATTEMPTS;
s.calls = 0;
CHECK_EQ(modavd_capture(h, 0, MODAVD_FMT_BGRA, 0, buf, sizeof(buf), &info), MODAVD_E_NOT_READY);
CHECK_EQ(s.calls, MX_MAX_COPY_ATTEMPTS);

/* The shim's call: it hands the last copy over, so it needs to tell this apart. */
s.remaining = MX_MAX_COPY_ATTEMPTS;
s.calls = 0;
CHECK_EQ(mx_copy_frame(&h->shm, MODAVD_FMT_RGBA, MODAVD_CAPTURE_BOTTOM_UP, 0, buf, sizeof(buf), &info),
MX_TORN);

mx_test_after_copy = NULL;
mx_test_after_copy_user = NULL;
modavd_close(h);
}
fake_frame_destroy(&f);
}

/* Same frame through the public call at sizes that leave every possible tail after the
* 16- and 4-pixel blocks, top-down and bottom-up. */
static void test_capture_odd_sizes(void)
Expand Down Expand Up @@ -347,6 +416,7 @@ int main(void)
test_version_and_errors();
test_open_errors();
test_capture_from_fake_frame();
test_capture_new_frame_mid_copy();
test_capture_odd_sizes();
test_convert_row();
test_log_callback();
Expand Down
Loading