Skip to content

Commit 7f6a3f5

Browse files
authored
Add compatibility with mupen64 and other libco cores (#455)
1 parent 7a16a5f commit 7f6a3f5

File tree

3 files changed

+165
-15
lines changed

3 files changed

+165
-15
lines changed

libretro/cfuncs.go

Lines changed: 142 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,122 @@ package libretro
55
#include <stdbool.h>
66
#include <stdarg.h>
77
#include <stdio.h>
8+
#include <pthread.h>
9+
10+
#ifdef __APPLE__
11+
#include <mach/semaphore.h>
12+
#include <mach/task.h>
13+
#include <mach/mach_init.h>
14+
#define SEM_T semaphore_t
15+
#define SEM_INIT(x) semaphore_create(mach_task_self(), &x, SYNC_POLICY_FIFO, 0);
16+
#define SEM_POST(x) semaphore_signal(x)
17+
#define SEM_WAIT(x) semaphore_wait(x)
18+
#else
19+
#include <semaphore.h>
20+
#define SEM_T sem_t
21+
#define SEM_INIT(x) sem_init(&x, 0, 0)
22+
#define SEM_POST(x) sem_post(&x)
23+
#define SEM_WAIT(x) sem_wait(&x)
24+
#endif
25+
26+
#if 0
27+
#define print_sema(...) (printf(__VA_ARGS__))
28+
#else
29+
#define print_sema(...) do {} while (0)
30+
#endif
31+
32+
enum {
33+
CMD_F,
34+
CMD_SERIALIZE,
35+
};
36+
37+
struct thread_cmd_t {
38+
int cmd;
39+
void* f;
40+
void* arg1;
41+
void* arg2;
42+
void* arg3;
43+
void* arg4;
44+
void* res;
45+
};
46+
47+
struct thread_cmd_t s_job;
48+
static pthread_t s_thread;
49+
static SEM_T s_sem_do;
50+
static SEM_T s_sem_done;
51+
static bool s_use_thread = false;
52+
53+
void* emu_thread_loop(void *a0) {
54+
print_sema("begin thread\n");
55+
56+
SEM_POST(s_sem_done);
57+
58+
print_sema("signal thread\n");
59+
60+
while (1) {
61+
print_sema("wait do\n");
62+
SEM_WAIT(s_sem_do);
63+
64+
print_sema("do\n");
65+
switch (s_job.cmd) {
66+
case CMD_F:
67+
((void (*)(void))s_job.f)();
68+
break;
69+
case CMD_SERIALIZE: {
70+
bool res = ((bool (*)(void*, size_t))s_job.f)(s_job.arg1, *(size_t*)s_job.arg2);
71+
*(bool*)s_job.res = res;
72+
break;
73+
}
74+
default:
75+
break;
76+
}
77+
78+
print_sema("signal done\n");
79+
SEM_POST(s_sem_done);
80+
}
81+
}
82+
83+
void thread_sync() {
84+
// Fire the job
85+
print_sema("signal do\n");
86+
SEM_POST(s_sem_do);
87+
88+
// Wait the result
89+
print_sema("wait done\n");
90+
SEM_WAIT(s_sem_done);
91+
92+
print_sema("done\n");
93+
}
94+
95+
void run_wrapper(void *f) {
96+
if (s_use_thread) {
97+
s_job.cmd = CMD_F;
98+
s_job.f = f;
99+
thread_sync();
100+
} else {
101+
((void (*)(void))f)();
102+
}
103+
}
104+
105+
void cothread_init() {
106+
s_use_thread = true;
107+
108+
SEM_INIT(s_sem_do);
109+
SEM_INIT(s_sem_done);
110+
111+
print_sema("create thread\n");
112+
pthread_create(&s_thread, NULL, emu_thread_loop, NULL);
113+
114+
print_sema("wait thread\n");
115+
SEM_WAIT(s_sem_done);
116+
}
8117
9118
void bridge_retro_init(void *f) {
10-
return ((void (*)(void))f)();
119+
run_wrapper(f);
11120
}
12121
13122
void bridge_retro_deinit(void *f) {
14-
return ((void (*)(void))f)();
123+
run_wrapper(f);
15124
}
16125
17126
unsigned bridge_retro_api_version(void *f) {
@@ -75,23 +184,49 @@ size_t bridge_retro_serialize_size(void *f) {
75184
}
76185
77186
bool bridge_retro_serialize(void *f, void *data, size_t size) {
78-
return ((bool (*)(void*, size_t))f)(data, size);
187+
if (s_use_thread) {
188+
bool res;
189+
s_job.cmd = CMD_SERIALIZE;
190+
s_job.f = f;
191+
s_job.arg1 = data;
192+
s_job.arg2 = &size;
193+
s_job.res = &res;
194+
195+
thread_sync();
196+
197+
return s_job.res;
198+
} else {
199+
return ((bool (*)(void*, size_t))f)(data, size);
200+
}
79201
}
80202
81203
bool bridge_retro_unserialize(void *f, void *data, size_t size) {
82-
return ((bool (*)(void*, size_t))f)(data, size);
204+
if (s_use_thread) {
205+
bool res;
206+
s_job.cmd = CMD_SERIALIZE; // Same command format for both serialize & unserialize
207+
s_job.f = f;
208+
s_job.arg1 = data;
209+
s_job.arg2 = &size;
210+
s_job.res = &res;
211+
212+
thread_sync();
213+
214+
return s_job.res;
215+
} else {
216+
return ((bool (*)(void*, size_t))f)(data, size);
217+
}
83218
}
84219
85220
void bridge_retro_unload_game(void *f) {
86-
return ((void (*)(void))f)();
221+
run_wrapper(f);
87222
}
88223
89224
void bridge_retro_run(void *f) {
90-
return ((void (*)(void))f)();
225+
run_wrapper(f);
91226
}
92227
93228
void bridge_retro_reset(void *f) {
94-
return ((void (*)(void))f)();
229+
run_wrapper(f);
95230
}
96231
97232
size_t bridge_retro_get_memory_size(void *f, unsigned id) {

libretro/libretro.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ Package libretro is a cgo binding for the libretro API.
44
Libretro is a simple but powerful development interface that allows for the easy creation of
55
emulators, games and multimedia applications that can plug straight into any libretro-compatible
66
frontend. This development interface is open to others so that they can run these pluggable emulator
7-
and game cores also in their own programs or devices. */
7+
and game cores also in their own programs or devices.
8+
*/
89
package libretro
910

1011
/*
@@ -13,6 +14,8 @@ package libretro
1314
#include <stdio.h>
1415
#include <string.h>
1516
17+
void cothread_init();
18+
1619
void bridge_retro_init(void *f);
1720
void bridge_retro_deinit(void *f);
1821
unsigned bridge_retro_api_version(void *f);
@@ -441,6 +444,8 @@ func Load(sofile string) (*Core, error) {
441444
return nil, err
442445
}
443446

447+
C.cothread_init()
448+
444449
core.symRetroInit = DlSym(core.handle, "retro_init")
445450
core.symRetroDeinit = DlSym(core.handle, "retro_deinit")
446451
core.symRetroAPIVersion = DlSym(core.handle, "retro_api_version")

video/video.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ type Video struct {
4040
bpp int32
4141
width, height int32 // dimensions set by the refresh callback
4242
rot uint
43+
44+
needUpload bool
45+
data unsafe.Pointer
4346
}
4447

4548
// Init instanciates the video package
@@ -259,7 +262,7 @@ func (video *Video) SetPixelFormat(format uint32) bool {
259262
}
260263

261264
// PixelStorei also needs to be updated whenever bpp changes
262-
defer gl.PixelStorei(gl.UNPACK_ROW_LENGTH, video.pitch/video.bpp)
265+
defer func() { video.needUpload = true }()
263266

264267
switch format {
265268
case libretro.PixelFormat0RGB1555:
@@ -350,6 +353,8 @@ func (video *Video) Render() {
350353
return
351354
}
352355

356+
video.uploadTexture()
357+
353358
fbw, fbh := video.Window.GetFramebufferSize()
354359
_, _, w, h := video.coreRatioViewport(fbw, fbh)
355360

@@ -366,21 +371,26 @@ func (video *Video) Render() {
366371

367372
// Refresh the texture framebuffer
368373
func (video *Video) Refresh(data unsafe.Pointer, width int32, height int32, pitch int32) {
374+
video.needUpload = true
369375
video.width = width
370376
video.height = height
371377
video.pitch = pitch
378+
video.data = data // maybe need a full copy
379+
}
380+
381+
func (video *Video) uploadTexture() {
382+
if !video.needUpload || video.data == nil {
383+
return
384+
}
372385

373386
gl.BindTexture(gl.TEXTURE_2D, video.texID)
374387
gl.PixelStorei(gl.UNPACK_ROW_LENGTH, video.pitch/video.bpp)
375388

376389
gl.UseProgram(video.program)
377-
gl.Uniform2f(gl.GetUniformLocation(video.program, gl.Str("TextureSize\x00")), float32(width), float32(height))
378-
gl.Uniform2f(gl.GetUniformLocation(video.program, gl.Str("InputSize\x00")), float32(width), float32(height))
390+
gl.Uniform2f(gl.GetUniformLocation(video.program, gl.Str("TextureSize\x00")), float32(video.width), float32(video.height))
391+
gl.Uniform2f(gl.GetUniformLocation(video.program, gl.Str("InputSize\x00")), float32(video.width), float32(video.height))
379392

380-
if data == nil {
381-
return
382-
}
383-
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, width, height, 0, video.pixType, video.pixFmt, data)
393+
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, video.width, video.height, 0, video.pixType, video.pixFmt, video.data)
384394
}
385395

386396
// SetRotation rotates the game image as requested by the core

0 commit comments

Comments
 (0)