mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
smoother fmv. better voices in game. minor other fixes
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
package com.izzy2lost.x1box
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.SystemClock
|
||||
import android.view.KeyEvent
|
||||
import org.libsdl.app.SDLControllerManager
|
||||
|
||||
@@ -8,9 +11,16 @@ import org.libsdl.app.SDLControllerManager
|
||||
*/
|
||||
class ControllerInputBridge : OnScreenController.ControllerListener {
|
||||
|
||||
private data class ButtonDispatchState(
|
||||
var isDown: Boolean = false,
|
||||
var pressedAtMs: Long = 0L,
|
||||
var pendingRelease: Runnable? = null,
|
||||
)
|
||||
|
||||
companion object {
|
||||
// Virtual device ID for on-screen controller
|
||||
const val VIRTUAL_DEVICE_ID = -2
|
||||
private const val MIN_TAP_HOLD_MS = 50L
|
||||
|
||||
// Axis indices for SDL
|
||||
const val AXIS_LEFT_X = 0
|
||||
@@ -21,33 +31,57 @@ class ControllerInputBridge : OnScreenController.ControllerListener {
|
||||
const val AXIS_RIGHT_TRIGGER = 5
|
||||
}
|
||||
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
private val buttonStates = mutableMapOf<OnScreenController.Button, ButtonDispatchState>()
|
||||
|
||||
override fun onButtonPressed(button: OnScreenController.Button) {
|
||||
val state = buttonStates.getOrPut(button) { ButtonDispatchState() }
|
||||
state.pendingRelease?.let(mainHandler::removeCallbacks)
|
||||
state.pendingRelease = null
|
||||
state.pressedAtMs = SystemClock.uptimeMillis()
|
||||
if (state.isDown) {
|
||||
return
|
||||
}
|
||||
state.isDown = true
|
||||
|
||||
try {
|
||||
when (button) {
|
||||
OnScreenController.Button.LEFT_TRIGGER,
|
||||
OnScreenController.Button.RIGHT_TRIGGER ->
|
||||
setTriggerState(button, pressed = true)
|
||||
else -> {
|
||||
val keyCode = getKeyCodeForButton(button)
|
||||
SDLControllerManager.onNativePadDown(VIRTUAL_DEVICE_ID, keyCode)
|
||||
}
|
||||
}
|
||||
dispatchButtonState(button, pressed = true)
|
||||
} catch (e: Exception) {
|
||||
DebugLog.e("ControllerBridge", e) { "Error on button press: ${e.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onButtonReleased(button: OnScreenController.Button) {
|
||||
try {
|
||||
when (button) {
|
||||
OnScreenController.Button.LEFT_TRIGGER,
|
||||
OnScreenController.Button.RIGHT_TRIGGER ->
|
||||
setTriggerState(button, pressed = false)
|
||||
else -> {
|
||||
val keyCode = getKeyCodeForButton(button)
|
||||
SDLControllerManager.onNativePadUp(VIRTUAL_DEVICE_ID, keyCode)
|
||||
val state = buttonStates.getOrPut(button) { ButtonDispatchState() }
|
||||
state.pendingRelease?.let(mainHandler::removeCallbacks)
|
||||
state.pendingRelease = null
|
||||
if (!state.isDown) {
|
||||
return
|
||||
}
|
||||
|
||||
val elapsed = SystemClock.uptimeMillis() - state.pressedAtMs
|
||||
val remaining = MIN_TAP_HOLD_MS - elapsed
|
||||
if (remaining > 0L) {
|
||||
val releaseTask = Runnable {
|
||||
state.pendingRelease = null
|
||||
if (!state.isDown) {
|
||||
return@Runnable
|
||||
}
|
||||
state.isDown = false
|
||||
try {
|
||||
dispatchButtonState(button, pressed = false)
|
||||
} catch (e: Exception) {
|
||||
DebugLog.e("ControllerBridge", e) { "Error on delayed button release: ${e.message}" }
|
||||
}
|
||||
}
|
||||
state.pendingRelease = releaseTask
|
||||
mainHandler.postDelayed(releaseTask, remaining)
|
||||
return
|
||||
}
|
||||
|
||||
state.isDown = false
|
||||
try {
|
||||
dispatchButtonState(button, pressed = false)
|
||||
} catch (e: Exception) {
|
||||
DebugLog.e("ControllerBridge", e) { "Error on button release: ${e.message}" }
|
||||
}
|
||||
@@ -94,6 +128,38 @@ class ControllerInputBridge : OnScreenController.ControllerListener {
|
||||
}
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
buttonStates.forEach { (button, state) ->
|
||||
state.pendingRelease?.let(mainHandler::removeCallbacks)
|
||||
state.pendingRelease = null
|
||||
if (!state.isDown) {
|
||||
return@forEach
|
||||
}
|
||||
state.isDown = false
|
||||
try {
|
||||
dispatchButtonState(button, pressed = false)
|
||||
} catch (e: Exception) {
|
||||
DebugLog.e("ControllerBridge", e) { "Error resetting $button: ${e.message}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun dispatchButtonState(button: OnScreenController.Button, pressed: Boolean) {
|
||||
when (button) {
|
||||
OnScreenController.Button.LEFT_TRIGGER,
|
||||
OnScreenController.Button.RIGHT_TRIGGER ->
|
||||
setTriggerState(button, pressed)
|
||||
else -> {
|
||||
val keyCode = getKeyCodeForButton(button)
|
||||
if (pressed) {
|
||||
SDLControllerManager.onNativePadDown(VIRTUAL_DEVICE_ID, keyCode)
|
||||
} else {
|
||||
SDLControllerManager.onNativePadUp(VIRTUAL_DEVICE_ID, keyCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setTriggerState(button: OnScreenController.Button, pressed: Boolean) {
|
||||
val keyCode = getKeyCodeForButton(button)
|
||||
val axis = when (button) {
|
||||
|
||||
@@ -99,6 +99,7 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener {
|
||||
// gesture panel, notification shade, or dialog appears). Without this,
|
||||
// triggers can stay "pressed" if the touch UP event is never delivered.
|
||||
onScreenController?.resetAllInputs()
|
||||
controllerBridge?.reset()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +219,7 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener {
|
||||
override fun onPause() {
|
||||
swipeUpGestureRecognizer.reset()
|
||||
onScreenController?.resetAllInputs()
|
||||
controllerBridge?.reset()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
|
||||
+36
-3
@@ -264,6 +264,22 @@ static int getenv_int_clamped(const char *name, int min_value, int max_value,
|
||||
return (int)parsed;
|
||||
}
|
||||
|
||||
static void monitor_hold_last_sample(MCPXAPUState *s, uint8_t *stream, int len)
|
||||
{
|
||||
int frame_bytes = sizeof(s->monitor.last_output_sample);
|
||||
int frames = len / frame_bytes;
|
||||
int16_t *out = (int16_t *)stream;
|
||||
for (int i = 0; i < frames; i++) {
|
||||
out[i * 2 + 0] = s->monitor.last_output_sample[0];
|
||||
out[i * 2 + 1] = s->monitor.last_output_sample[1];
|
||||
}
|
||||
|
||||
int tail_bytes = len - (frames * frame_bytes);
|
||||
if (tail_bytes > 0) {
|
||||
memset(stream + (frames * frame_bytes), 0, tail_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
static void monitor_sink_cb(void *opaque, uint8_t *stream, int free_b)
|
||||
{
|
||||
MCPXAPUState *s = MCPX_APU_DEVICE(opaque);
|
||||
@@ -274,7 +290,12 @@ static void monitor_sink_cb(void *opaque, uint8_t *stream, int free_b)
|
||||
}
|
||||
|
||||
int avail = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
#ifdef __ANDROID__
|
||||
int wait_attempts = 24;
|
||||
#else
|
||||
int wait_attempts = 10;
|
||||
#endif
|
||||
for (int i = 0; i < wait_attempts; i++) {
|
||||
qemu_spin_lock(&s->monitor.fifo_lock);
|
||||
avail = fifo8_num_used(&s->monitor.fifo);
|
||||
qemu_spin_unlock(&s->monitor.fifo_lock);
|
||||
@@ -304,7 +325,14 @@ static void monitor_sink_cb(void *opaque, uint8_t *stream, int free_b)
|
||||
}
|
||||
|
||||
if (copied < free_b) {
|
||||
memset(stream + copied, 0, free_b - copied);
|
||||
monitor_hold_last_sample(s, stream + copied, free_b - copied);
|
||||
}
|
||||
|
||||
if (free_b >= sizeof(s->monitor.last_output_sample)) {
|
||||
int16_t *last = (int16_t *)(stream + free_b -
|
||||
sizeof(s->monitor.last_output_sample));
|
||||
s->monitor.last_output_sample[0] = last[0];
|
||||
s->monitor.last_output_sample[1] = last[1];
|
||||
}
|
||||
|
||||
qemu_cond_broadcast(&s->cond);
|
||||
@@ -317,11 +345,16 @@ static void monitor_init(MCPXAPUState *d)
|
||||
d->monitor.device_buffer_bytes = 0;
|
||||
d->monitor.queued_bytes_low = 0;
|
||||
d->monitor.queued_bytes_high = 0;
|
||||
d->monitor.last_output_sample[0] = 0;
|
||||
d->monitor.last_output_sample[1] = 0;
|
||||
|
||||
int fifo_frames = 3;
|
||||
int audio_samples = 512;
|
||||
#ifdef __ANDROID__
|
||||
fifo_frames = 16;
|
||||
/* Give Android a little more audio headroom to ride out short stalls
|
||||
* without increasing the device callback size again.
|
||||
*/
|
||||
fifo_frames = 24;
|
||||
audio_samples = 2048;
|
||||
fifo_frames = getenv_int_clamped("XEMU_ANDROID_AUDIO_FIFO_FRAMES", 3, 32,
|
||||
fifo_frames);
|
||||
|
||||
@@ -102,6 +102,7 @@ typedef struct MCPXAPUState {
|
||||
struct {
|
||||
McpxApuDebugMonitorPoint point;
|
||||
int16_t frame_buf[256][2]; // 1 EP frame (0x400 bytes), 8 buffered
|
||||
int16_t last_output_sample[2];
|
||||
QemuSpin fifo_lock;
|
||||
Fifo8 fifo;
|
||||
int fifo_capacity_bytes;
|
||||
|
||||
+52
-10
@@ -1751,6 +1751,43 @@ voice_work_dispatch(MCPXAPUState *d,
|
||||
qemu_mutex_lock(&vwd->lock);
|
||||
|
||||
if (vwd->queue_len) {
|
||||
if (vwd->num_workers == 1) {
|
||||
VoiceWorker *worker = &vwd->workers[0];
|
||||
int voice_count = vwd->queue_len;
|
||||
|
||||
memset(worker->mixbins, 0, sizeof(worker->mixbins));
|
||||
if (d->monitor.point == MCPX_APU_DEBUG_MON_VP) {
|
||||
memset(worker->sample_buf, 0, sizeof(worker->sample_buf));
|
||||
}
|
||||
|
||||
for (int i = 0; i < vwd->queue_len; i++) {
|
||||
voice_process(d, worker->mixbins, worker->sample_buf,
|
||||
vwd->queue[i].voice, vwd->queue[i].list);
|
||||
}
|
||||
|
||||
voice_work_release_voice_locks(d);
|
||||
vwd->queue_len = 0;
|
||||
|
||||
for (int b = 0; b < NUM_MIXBINS; b++) {
|
||||
for (int s = 0; s < NUM_SAMPLES_PER_FRAME; s++) {
|
||||
mixbins[b][s] += worker->mixbins[b][s];
|
||||
}
|
||||
}
|
||||
if (d->monitor.point == MCPX_APU_DEBUG_MON_VP) {
|
||||
for (int i = 0; i < NUM_SAMPLES_PER_FRAME; i++) {
|
||||
d->vp.sample_buf[i][0] += worker->sample_buf[i][0];
|
||||
d->vp.sample_buf[i][1] += worker->sample_buf[i][1];
|
||||
}
|
||||
}
|
||||
|
||||
g_dbg.vp.workers[0].num_voices = voice_count;
|
||||
g_dbg.vp.workers[0].time_us =
|
||||
qemu_clock_get_us(QEMU_CLOCK_REALTIME) - start_time;
|
||||
g_dbg.vp.total_worker_time_us = g_dbg.vp.workers[0].time_us;
|
||||
qemu_mutex_unlock(&vwd->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(vwd->mixbins, 0, sizeof(vwd->mixbins));
|
||||
|
||||
// Signal workers and wait for completion
|
||||
@@ -1782,8 +1819,9 @@ static int mcpx_apu_default_vp_worker_count(void)
|
||||
#ifdef __ANDROID__
|
||||
/*
|
||||
* Mobile SoCs are often oversubscribed already (TCG + render + I/O).
|
||||
* Keep VP worker defaults conservative to reduce thread contention and
|
||||
* audio underruns; allow explicit override via env.
|
||||
* Keep VP worker defaults very conservative to reduce thread contention and
|
||||
* leave more CPU time for the emulation threads that gate frametime.
|
||||
* Allow explicit override via env.
|
||||
*/
|
||||
const char *value = getenv("XEMU_ANDROID_VP_WORKERS");
|
||||
if (value && value[0] != '\0') {
|
||||
@@ -1794,16 +1832,10 @@ static int mcpx_apu_default_vp_worker_count(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu_count <= 2) {
|
||||
if (cpu_count <= 6) {
|
||||
return 1;
|
||||
}
|
||||
if (cpu_count <= 4) {
|
||||
return 2;
|
||||
}
|
||||
if (cpu_count <= 6) {
|
||||
return 3;
|
||||
}
|
||||
return 4;
|
||||
return 2;
|
||||
#else
|
||||
return cpu_count;
|
||||
#endif
|
||||
@@ -1829,6 +1861,10 @@ static void voice_work_init(MCPXAPUState *d)
|
||||
qemu_mutex_lock(&vwd->lock);
|
||||
qemu_cond_init(&vwd->work_pending);
|
||||
qemu_cond_init(&vwd->work_finished);
|
||||
if (vwd->num_workers == 1) {
|
||||
qemu_mutex_unlock(&vwd->lock);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < vwd->num_workers; i++) {
|
||||
vwd->workers_pending |= 1 << i;
|
||||
qemu_thread_create(&vwd->workers[i].thread, "mcpx.voice_worker",
|
||||
@@ -1843,6 +1879,12 @@ static void voice_work_finalize(MCPXAPUState *d)
|
||||
{
|
||||
VoiceWorkDispatch *vwd = &d->vp.voice_work_dispatch;
|
||||
|
||||
if (vwd->num_workers == 1) {
|
||||
g_free(vwd->workers);
|
||||
vwd->workers = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_mutex_lock(&vwd->lock);
|
||||
vwd->workers_should_exit = true;
|
||||
qemu_cond_broadcast(&vwd->work_pending);
|
||||
|
||||
@@ -180,7 +180,7 @@ static void pgraph_gl_finalize(NV2AState *d)
|
||||
static void pgraph_gl_flip_stall(NV2AState *d)
|
||||
{
|
||||
NV2A_GL_DFRAME_TERMINATOR();
|
||||
glFinish();
|
||||
glFlush();
|
||||
}
|
||||
|
||||
static void pgraph_gl_flush(NV2AState *d)
|
||||
|
||||
@@ -193,6 +193,11 @@ typedef struct PGRAPHGLState {
|
||||
bool download_dirty_surfaces_pending;
|
||||
QemuEvent dirty_surfaces_download_complete; // common
|
||||
|
||||
#ifdef __ANDROID__
|
||||
GLuint gl_download_pbo;
|
||||
size_t gl_download_pbo_size;
|
||||
#endif
|
||||
|
||||
TextureBinding *texture_binding[NV2A_MAX_TEXTURES];
|
||||
Lru texture_cache;
|
||||
TextureLruNode *texture_cache_entries;
|
||||
|
||||
@@ -92,6 +92,54 @@ static bool android_drain_gl_errors_silent(void)
|
||||
return had_error;
|
||||
}
|
||||
|
||||
static void android_glo_readpixels(PGRAPHGLState *r, GLenum gl_format,
|
||||
GLenum gl_type,
|
||||
unsigned int bytes_per_pixel,
|
||||
unsigned int stride,
|
||||
unsigned int width,
|
||||
unsigned int height, bool vflip,
|
||||
void *data)
|
||||
{
|
||||
size_t required_size = stride * height;
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, r->gl_download_pbo);
|
||||
if (required_size > r->gl_download_pbo_size) {
|
||||
glBufferData(GL_PIXEL_PACK_BUFFER, required_size, NULL,
|
||||
GL_STREAM_READ);
|
||||
r->gl_download_pbo_size = required_size;
|
||||
}
|
||||
|
||||
int rl, pa;
|
||||
glGetIntegerv(GL_PACK_ROW_LENGTH, &rl);
|
||||
glGetIntegerv(GL_PACK_ALIGNMENT, &pa);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, stride / bytes_per_pixel);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
|
||||
glReadPixels(0, 0, width, height, gl_format, gl_type, 0);
|
||||
|
||||
void *mapped_data = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0,
|
||||
required_size, GL_MAP_READ_BIT);
|
||||
if (mapped_data) {
|
||||
if (vflip) {
|
||||
GLubyte *b = (GLubyte *) data;
|
||||
GLubyte *c =
|
||||
&((GLubyte *) mapped_data)[stride * (height - 1)];
|
||||
for (unsigned int irow = 0; irow < height; irow++) {
|
||||
memcpy(b, c, width * bytes_per_pixel);
|
||||
b += stride;
|
||||
c -= stride;
|
||||
}
|
||||
} else {
|
||||
memcpy(data, mapped_data, required_size);
|
||||
}
|
||||
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, rl);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, pa);
|
||||
}
|
||||
|
||||
static void android_sanitize_surface_format(PGRAPHGLState *r,
|
||||
SurfaceFormatInfo *fmt)
|
||||
{
|
||||
@@ -1375,8 +1423,8 @@ static bool android_surface_download_depth16_to_guest(NV2AState *d,
|
||||
}
|
||||
|
||||
rgba_pixels = g_malloc(read_height * rgba_stride);
|
||||
glo_readpixels(GL_RGBA, GL_UNSIGNED_BYTE, 4, rgba_stride, read_width,
|
||||
read_height, flip, rgba_pixels);
|
||||
android_glo_readpixels(r, GL_RGBA, GL_UNSIGNED_BYTE, 4, rgba_stride,
|
||||
read_width, read_height, flip, rgba_pixels);
|
||||
|
||||
if (android_log_surface_download_errors(
|
||||
"surface_download_depth16: pack read", surface)) {
|
||||
@@ -1533,8 +1581,9 @@ static bool android_surface_download_z24s8_to_guest(NV2AState *d,
|
||||
}
|
||||
|
||||
depth_pixels = g_malloc(read_width * read_height * 4);
|
||||
glo_readpixels(GL_RGBA, GL_UNSIGNED_BYTE, 4, read_width * 4, read_width,
|
||||
read_height, flip, depth_pixels);
|
||||
android_glo_readpixels(r, GL_RGBA, GL_UNSIGNED_BYTE, 4,
|
||||
read_width * 4, read_width, read_height, flip,
|
||||
depth_pixels);
|
||||
if (android_log_surface_download_errors(
|
||||
"surface_download_z24s8: post-read-depth-pack", surface)) {
|
||||
goto cleanup;
|
||||
@@ -1553,9 +1602,9 @@ static bool android_surface_download_z24s8_to_guest(NV2AState *d,
|
||||
}
|
||||
|
||||
stencil_pixels = g_malloc0(read_width * read_height);
|
||||
glo_readpixels(GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, 1, read_width,
|
||||
read_width, read_height, flip,
|
||||
stencil_pixels);
|
||||
android_glo_readpixels(r, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, 1,
|
||||
read_width, read_width, read_height, flip,
|
||||
stencil_pixels);
|
||||
if (android_drain_gl_errors_silent()) {
|
||||
if (!warned_stencil_readback_unsupported) {
|
||||
warned_stencil_readback_unsupported = true;
|
||||
@@ -1713,8 +1762,9 @@ static void surface_download_to_buffer(NV2AState *d, SurfaceBinding *surface,
|
||||
uint8_t *rgba_linear = rgba_pixels;
|
||||
uint8_t *linear_guest = pixels;
|
||||
|
||||
glo_readpixels(GL_RGBA, GL_UNSIGNED_BYTE, 4, rgba_stride,
|
||||
read_width, read_height, flip, rgba_pixels);
|
||||
android_glo_readpixels(pg->gl_renderer_state, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, 4, rgba_stride,
|
||||
read_width, read_height, flip, rgba_pixels);
|
||||
android_log_surface_download_errors(
|
||||
"surface_download_to_buffer: post-read_rgba8", surface);
|
||||
|
||||
@@ -1768,7 +1818,11 @@ static void surface_download_to_buffer(NV2AState *d, SurfaceBinding *surface,
|
||||
gl_read_buf = pg->scale_buf;
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
android_glo_readpixels(pg->gl_renderer_state,
|
||||
#else
|
||||
glo_readpixels(
|
||||
#endif
|
||||
surface->fmt.gl_format, surface->fmt.gl_type, surface->fmt.bytes_per_pixel,
|
||||
pg->surface_scale_factor * surface->pitch,
|
||||
pg->surface_scale_factor * surface->width,
|
||||
@@ -2520,6 +2574,10 @@ void pgraph_gl_init_surfaces(PGRAPHState *pg)
|
||||
pgraph_gl_reload_surface_scale_factor(pg);
|
||||
glGenFramebuffers(1, &r->gl_framebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, r->gl_framebuffer);
|
||||
#ifdef __ANDROID__
|
||||
glGenBuffers(1, &r->gl_download_pbo);
|
||||
r->gl_download_pbo_size = 0;
|
||||
#endif
|
||||
QTAILQ_INIT(&r->surfaces);
|
||||
r->downloads_pending = false;
|
||||
qemu_event_init(&r->downloads_complete, false);
|
||||
@@ -2558,6 +2616,14 @@ void pgraph_gl_finalize_surfaces(PGRAPHState *pg)
|
||||
glDeleteFramebuffers(1, &r->gl_framebuffer);
|
||||
r->gl_framebuffer = 0;
|
||||
|
||||
#ifdef __ANDROID__
|
||||
if (r->gl_download_pbo) {
|
||||
glDeleteBuffers(1, &r->gl_download_pbo);
|
||||
r->gl_download_pbo = 0;
|
||||
r->gl_download_pbo_size = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
finalize_render_to_texture(pg);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
|
||||
#define XEMU_INPUT_MIN_INPUT_UPDATE_INTERVAL_US 2500
|
||||
#define XEMU_INPUT_MIN_RUMBLE_UPDATE_INTERVAL_US 2500
|
||||
#define XEMU_INPUT_MIN_BUTTON_HOLD_US 50000
|
||||
|
||||
#if 0
|
||||
static void xemu_input_print_controller_state(ControllerState *state)
|
||||
@@ -92,6 +93,58 @@ const char *bound_drivers[4] = { DRIVER_DUKE, DRIVER_DUKE, DRIVER_DUKE,
|
||||
DRIVER_DUKE };
|
||||
int test_mode;
|
||||
|
||||
static ControllerState *xemu_input_find_sdl_controller(SDL_JoystickID id)
|
||||
{
|
||||
ControllerState *iter;
|
||||
|
||||
QTAILQ_FOREACH(iter, &available_controllers, entry) {
|
||||
if (iter->type == INPUT_DEVICE_SDL_GAMECONTROLLER &&
|
||||
iter->sdl_joystick_id == id) {
|
||||
return iter;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int xemu_input_sdl_button_to_button_id(uint8_t button)
|
||||
{
|
||||
switch (button) {
|
||||
case SDL_CONTROLLER_BUTTON_A:
|
||||
return 0;
|
||||
case SDL_CONTROLLER_BUTTON_B:
|
||||
return 1;
|
||||
case SDL_CONTROLLER_BUTTON_X:
|
||||
return 2;
|
||||
case SDL_CONTROLLER_BUTTON_Y:
|
||||
return 3;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
|
||||
return 4;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP:
|
||||
return 5;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
|
||||
return 6;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
|
||||
return 7;
|
||||
case SDL_CONTROLLER_BUTTON_BACK:
|
||||
return 8;
|
||||
case SDL_CONTROLLER_BUTTON_START:
|
||||
return 9;
|
||||
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
|
||||
return 10;
|
||||
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
|
||||
return 11;
|
||||
case SDL_CONTROLLER_BUTTON_LEFTSTICK:
|
||||
return 12;
|
||||
case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
|
||||
return 13;
|
||||
case SDL_CONTROLLER_BUTTON_GUIDE:
|
||||
return 14;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static const char **port_index_to_settings_key_map[] = {
|
||||
&g_config.input.bindings.port1,
|
||||
&g_config.input.bindings.port2,
|
||||
@@ -444,6 +497,17 @@ void xemu_input_process_sdl_events(const SDL_Event *event)
|
||||
xemu_queue_notification(buf);
|
||||
xemu_input_rebind_xmu(port);
|
||||
}
|
||||
} else if (event->type == SDL_CONTROLLERBUTTONDOWN) {
|
||||
ControllerState *state =
|
||||
xemu_input_find_sdl_controller(event->cbutton.which);
|
||||
int button_id =
|
||||
xemu_input_sdl_button_to_button_id(event->cbutton.button);
|
||||
|
||||
if (state && button_id >= 0) {
|
||||
state->button_hold_until_us[button_id] =
|
||||
qemu_clock_get_us(QEMU_CLOCK_REALTIME) +
|
||||
XEMU_INPUT_MIN_BUTTON_HOLD_US;
|
||||
}
|
||||
} else if (event->type == SDL_CONTROLLERDEVICEREMOVED) {
|
||||
DPRINTF("Controller Removed: %d\n", event->cdevice.which);
|
||||
int handled = 0;
|
||||
@@ -575,6 +639,8 @@ void xemu_input_update_sdl_kbd_controller_state(ControllerState *state)
|
||||
|
||||
void xemu_input_update_sdl_controller_state(ControllerState *state)
|
||||
{
|
||||
int64_t now = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
|
||||
|
||||
state->buttons = 0;
|
||||
memset(state->axis, 0, sizeof(state->axis));
|
||||
if (!state->controller_map) {
|
||||
@@ -641,6 +707,14 @@ void xemu_input_update_sdl_controller_state(ControllerState *state)
|
||||
|
||||
#undef INVERT_AXIS
|
||||
|
||||
for (int i = 0; i < 15; i++) {
|
||||
if (state->button_hold_until_us[i] > now) {
|
||||
state->buttons |= CONTROLLER_STATE_BUTTON_ID_TO_MASK(i);
|
||||
} else {
|
||||
state->button_hold_until_us[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// xemu_input_print_controller_state(state);
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,7 @@ typedef struct ControllerState {
|
||||
// Input state
|
||||
uint16_t buttons;
|
||||
int16_t axis[CONTROLLER_AXIS__COUNT];
|
||||
int64_t button_hold_until_us[15];
|
||||
|
||||
// Rendering state hacked on here for convenience but needs to be moved (FIXME)
|
||||
uint32_t animate_guide_button_end;
|
||||
|
||||
@@ -1381,7 +1381,7 @@ void xemu_android_display_loop(void)
|
||||
}
|
||||
#ifdef __ANDROID__
|
||||
xemu_android_refresh_frame_limit_from_env();
|
||||
SDL_GL_SetSwapInterval(g_config.display.window.vsync ? 1 : 0);
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
xemu_hud_init(m_window, m_context);
|
||||
#endif
|
||||
tcg_register_init_ctx();
|
||||
@@ -1737,10 +1737,14 @@ void sdl2_gl_refresh(DisplayChangeListener *dcl)
|
||||
*/
|
||||
static int64_t last_update = 0;
|
||||
#ifdef __ANDROID__
|
||||
int64_t deadline = last_update + g_android_frame_interval_ns;
|
||||
const int64_t frame_interval = g_android_frame_interval_ns;
|
||||
#else
|
||||
int64_t deadline = last_update + 16666666;
|
||||
const int64_t frame_interval = 16666666;
|
||||
#endif
|
||||
if (last_update == 0) {
|
||||
last_update = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||
}
|
||||
int64_t deadline = last_update + frame_interval;
|
||||
|
||||
#ifdef DEBUG_XEMU_C
|
||||
int64_t sleep_acc = 0;
|
||||
@@ -1775,7 +1779,11 @@ void sdl2_gl_refresh(DisplayChangeListener *dcl)
|
||||
}
|
||||
} else {
|
||||
DPRINTF("zzZz %g %ld\n", (double)sleep_acc/1000000.0, spin_acc);
|
||||
last_update = now;
|
||||
if (now - deadline > frame_interval) {
|
||||
last_update = now;
|
||||
} else {
|
||||
last_update = deadline;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user