GGPO fixes

Memwatchers weren't properly reset after a ggpo session.

Race condition when stopping if GGPO restarts the cpu for a new frame.

The emu thread might still run some rollback frames before stopping, so
the emu state must be updated only after ggpo is stopped.
Fixes MINIDUMP-6P

Don't autosavestate when GGPO is on

Really disable renderer during GGPO advance frame

Don't stop the sh4 after render (single threaded) when GGPO is on

android: Don't restart when unpaused if online
This commit is contained in:
Flyinghead 2023-02-01 17:55:24 +01:00
parent 5875eda4d0
commit 6b2063b06f
11 changed files with 64 additions and 47 deletions

View File

@ -365,8 +365,8 @@ void dc_reset(bool hard)
NetworkHandshake::term();
if (hard)
{
_vmem_unprotect_vram(0, VRAM_SIZE);
memwatch::elanWatcher.unprotectMem(0, 0xffffffff);
memwatch::unprotect();
memwatch::reset();
}
sh4_sched_reset(hard);
pvr::reset(hard);
@ -656,9 +656,14 @@ void Emulator::term()
}
}
void Emulator::stop() {
void Emulator::stop()
{
if (state != Running)
return;
// Avoid race condition with GGPO restarting the sh4 for a new frame
if (config::GGPOEnable)
NetworkHandshake::term();
// must be updated after GGPO is stopped since it may run some rollback frames
state = Loaded;
sh4_cpu.Stop();
if (config::ThreadedRendering)
@ -686,6 +691,8 @@ void Emulator::stop() {
void Emulator::requestReset()
{
resetRequested = true;
if (config::GGPOEnable)
NetworkHandshake::term();
sh4_cpu.Stop();
}

View File

@ -215,8 +215,6 @@ inline static void protect()
inline static void unprotect()
{
if (!config::GGPOEnable)
return;
vramWatcher.unprotect();
ramWatcher.unprotect();
aramWatcher.unprotect();

View File

@ -8,6 +8,7 @@
#include "hw/holly/holly_intc.h"
#include "hw/sh4/sh4_if.h"
#include "profiler/fc_profiler.h"
#include "network/ggpo.h"
#include <mutex>
#include <deque>
@ -32,6 +33,7 @@ u32 fb_watch_addr_end;
bool fb_dirty;
static bool pend_rend;
static bool rendererEnabled = true;
TA_context* _pvrrc;
@ -230,7 +232,7 @@ private:
if (renderer->Present())
{
presented = true;
if (!config::ThreadedRendering)
if (!config::ThreadedRendering && !ggpo::active())
sh4_cpu.Stop();
#ifdef LIBRETRO
retro_rend_present();
@ -311,6 +313,7 @@ static void rend_create_renderer()
bool rend_init_renderer()
{
rendererEnabled = true;
if (renderer == nullptr)
rend_create_renderer();
bool success = renderer->Init();
@ -340,6 +343,7 @@ void rend_reset()
FrameCount = 1;
fb_w_cur = 1;
pvrQueue.reset();
rendererEnabled = true;
}
void rend_start_render()
@ -426,12 +430,15 @@ void rend_vblank()
if (config::EmulateFramebuffer
|| (!render_called && fb_dirty && FB_R_CTRL.fb_enable))
{
FramebufferInfo fbInfo;
fbInfo.update();
pvrQueue.enqueue(PvrMessageQueue::RenderFramebuffer, fbInfo);
pvrQueue.enqueue(PvrMessageQueue::Present);
if (!config::EmulateFramebuffer)
DEBUG_LOG(PVR, "Direct framebuffer write detected");
if (rend_is_enabled())
{
FramebufferInfo fbInfo;
fbInfo.update();
pvrQueue.enqueue(PvrMessageQueue::RenderFramebuffer, fbInfo);
pvrQueue.enqueue(PvrMessageQueue::Present);
if (!config::EmulateFramebuffer)
DEBUG_LOG(PVR, "Direct framebuffer write detected");
}
fb_dirty = false;
}
render_called = false;
@ -470,7 +477,7 @@ void rend_set_fb_write_addr(u32 fb_w_sof1)
void rend_swap_frame(u32 fb_r_sof)
{
if (!config::EmulateFramebuffer && fb_r_sof == fb_w_cur)
if (!config::EmulateFramebuffer && fb_r_sof == fb_w_cur && rend_is_enabled())
pvrQueue.enqueue(PvrMessageQueue::Present);
}
@ -490,6 +497,14 @@ void rend_start_rollback()
vramRollback.Wait();
}
void rend_enable_renderer(bool enabled) {
rendererEnabled = enabled;
}
bool rend_is_enabled() {
return rendererEnabled;
}
void rend_serialize(Serializer& ser)
{
ser << fb_w_cur;

View File

@ -17,6 +17,8 @@ void rend_reset();
void rend_disable_rollback();
void rend_start_rollback();
void rend_allow_rollback();
void rend_enable_renderer(bool enabled);
bool rend_is_enabled();
void rend_serialize(Serializer& ser);
void rend_deserialize(Deserializer& deser);

View File

@ -50,7 +50,7 @@ bool QueueRender(TA_context* ctx)
{
verify(ctx != 0);
bool skipFrame = settings.disableRenderer;
bool skipFrame = !rend_is_enabled();
if (!skipFrame)
{
RenderCount++;
@ -67,7 +67,7 @@ bool QueueRender(TA_context* ctx)
if (skipFrame || rqueue)
{
tactx_Recycle(ctx);
if (!settings.disableRenderer)
if (rend_is_enabled())
fskip++;
return false;
}

View File

@ -255,14 +255,14 @@ static bool advance_frame(int)
{
INFO_LOG(NETWORK, "advance_frame");
settings.aica.muteAudio = true;
settings.disableRenderer = true;
rend_enable_renderer(false);
inRollback = true;
emu.run();
ggpo_advance_frame(ggpoSession);
settings.aica.muteAudio = false;
settings.disableRenderer = false;
rend_enable_renderer(true);
inRollback = false;
_endOfFrame = false;
@ -597,6 +597,8 @@ void stopSession()
ggpoSession = nullptr;
miniupnp.Term();
emu.setNetworkState(false);
memwatch::unprotect();
memwatch::reset();
}
void getInput(MapleInputState inputState[4])

View File

@ -86,6 +86,9 @@ void flycast_term()
void dc_savestate(int index)
{
if (settings.network.online)
return;
Serializer ser;
dc_serialize(ser);

View File

@ -2591,20 +2591,11 @@ static void gui_network_start()
ImGui::Text("Starting...");
try {
if (networkStatus.get())
{
gui_state = GuiState::Closed;
}
else
{
emu.unloadGame();
gui_state = GuiState::Main;
}
gui_stop_game();
} catch (const FlycastException& e) {
if (NetworkHandshake::instance != nullptr)
NetworkHandshake::instance->stop();
emu.unloadGame();
gui_error(e.what());
gui_state = GuiState::Main;
gui_stop_game(e.what());
}
}
else
@ -2626,8 +2617,7 @@ static void gui_network_start()
}
catch (const FlycastException& e) {
}
emu.unloadGame();
gui_state = GuiState::Main;
gui_stop_game();
}
ImGui::PopStyleVar();
@ -2685,12 +2675,10 @@ static void gui_display_loadscreen()
}
} catch (const FlycastException& ex) {
ERROR_LOG(BOOT, "%s", ex.what());
gui_error(ex.what());
#ifdef TEST_AUTOMATION
die("Game load failed");
#endif
emu.unloadGame();
gui_state = GuiState::Main;
gui_stop_game(ex.what());
}
ImGui::PopStyleVar();

View File

@ -122,7 +122,7 @@ public:
nameInfo.objectType = objectType;
nameInfo.object = object;
nameInfo.pObjectName = name.c_str();
vkDebugMarkerSetObjectNameEXT((VkDevice)*device, &nameInfo);
VULKAN_HPP_DEFAULT_DISPATCHER.vkDebugMarkerSetObjectNameEXT((VkDevice)*device, &nameInfo);
}
#endif
constexpr static int VENDOR_AMD = 0x1022;

View File

@ -301,7 +301,6 @@ struct settings_t
u8 vmu[16];
} md5;
} network;
bool disableRenderer;
};
extern settings_t settings;

View File

@ -341,14 +341,19 @@ extern "C" JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_setupMic(J
extern "C" JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_pause(JNIEnv *env,jobject obj)
{
if (emu.running())
{
stopEmu();
game_started = true; // restart when resumed
if (config::AutoSaveState)
dc_savestate(config::SavestateSlot);
}
gui_save();
if (config::GGPOEnable)
{
stopEmu();
gui_stop_game();
}
else if (emu.running())
{
stopEmu();
game_started = true; // restart when resumed
if (config::AutoSaveState)
dc_savestate(config::SavestateSlot);
}
gui_save();
}
extern "C" JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_resume(JNIEnv *env,jobject obj)
@ -359,10 +364,8 @@ extern "C" JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_resume(JNI
extern "C" JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_stop(JNIEnv *env,jobject obj)
{
stopEmu();
emu.unloadGame();
gui_state = GuiState::Main;
game_started = false;
stopEmu();
gui_stop_game();
}
static void *render_thread_func(void *)