mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-09 03:29:43 -10:00
crt: rebind the modeline ops when the display server is rebuilt under a live engine
The CRT consumer captured the display server instance pointer at engine init. A content load or close goes through the main deinit, which clears the runloop inited flag, so the next video init destroys that instance and builds a new one; the win32 modeline_close puts the desktop timing back on the wire and the consumer's ops table points at freed memory. Every later set read ml->opened from the fresh zeroed struct, returned false before logging, and the screen stayed on the desktop mode (480i on CRT Emudriver) while the log showed the modeline calculated and "Error switching" with no CDS call. video_display_server_destroy now tells the consumer the instance is going away: the ops table is dropped, current is cleared and the change detection is reset. On the next switch the engine rebinds to the server that is up and applies the mode again through a real set. Fixes #19528
This commit is contained in:
1 parent
75d79f9009
commit
94d2365126
3 files changed
+70
-13
No files matched your search
+57
-13
@@ -250,6 +250,53 @@ static bool crt_load_config_ini(videocrt_switch_t *p_switch)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Point the ops table at the display server instance that is up
|
||||
* right now and open its modeline path. The instance is torn down
|
||||
* and rebuilt under a full video init (content load and close go
|
||||
* through the main deinit), so this runs at engine init and again
|
||||
* after crt_switch_display_server_lost() dropped the old table. */
|
||||
static void crt_bind_display_server(videocrt_switch_t *p_switch)
|
||||
{
|
||||
video_modeline_gen_t *gen = p_switch->gen;
|
||||
|
||||
memset(&p_switch->ops, 0, sizeof(p_switch->ops));
|
||||
p_switch->ops_valid = false;
|
||||
p_switch->ops_lost = false;
|
||||
if (!p_switch->khr_ctx && video_display_server_get_modeline_ops(&p_switch->ops))
|
||||
{
|
||||
if (p_switch->ops.open && !p_switch->ops.open(p_switch->ops.data, &gen->disp))
|
||||
{
|
||||
RARCH_ERR("[CRT] Display server could not open the modeline path, generating only.\n");
|
||||
memset(&p_switch->ops, 0, sizeof(p_switch->ops));
|
||||
}
|
||||
else
|
||||
p_switch->ops_valid = true;
|
||||
}
|
||||
p_switch->ops.name = p_switch->ops_valid ? video_display_server_get_ident() : "dummy";
|
||||
}
|
||||
|
||||
void crt_switch_display_server_lost(videocrt_switch_t *p_switch, void *data)
|
||||
{
|
||||
if (!p_switch->gen || !p_switch->ops_valid || p_switch->ops.data != data)
|
||||
return;
|
||||
|
||||
/* The server closes its modeline path as it goes down, which
|
||||
* puts the desktop timing back on the wire, and the ops table
|
||||
* points into memory that is about to be freed. Drop the table,
|
||||
* forget what was current, and make the next frame's request
|
||||
* look new so the mode is applied again through the rebound
|
||||
* server. */
|
||||
memset(&p_switch->ops, 0, sizeof(p_switch->ops));
|
||||
p_switch->ops.name = "dummy";
|
||||
p_switch->ops_valid = false;
|
||||
p_switch->ops_lost = true;
|
||||
p_switch->gen->current = NULL;
|
||||
p_switch->ra_tmp_height = 0;
|
||||
p_switch->ra_tmp_width = 0;
|
||||
p_switch->ra_tmp_core_hz = 0.0f;
|
||||
RARCH_LOG("[CRT] Display server going down, rebinding on the next switch.\n");
|
||||
}
|
||||
|
||||
static bool crt_engine_init(videocrt_switch_t *p_switch,
|
||||
int monitor_index, unsigned int crt_mode, unsigned int super_width)
|
||||
{
|
||||
@@ -299,19 +346,7 @@ static bool crt_engine_init(videocrt_switch_t *p_switch,
|
||||
modeline_ini_load(gen, "display0.ini");
|
||||
modeline_parse_options(gen);
|
||||
|
||||
memset(&p_switch->ops, 0, sizeof(p_switch->ops));
|
||||
p_switch->ops_valid = false;
|
||||
if (!p_switch->khr_ctx && video_display_server_get_modeline_ops(&p_switch->ops))
|
||||
{
|
||||
if (p_switch->ops.open && !p_switch->ops.open(p_switch->ops.data, &gen->disp))
|
||||
{
|
||||
RARCH_ERR("[CRT] Display server could not open the modeline path, generating only.\n");
|
||||
memset(&p_switch->ops, 0, sizeof(p_switch->ops));
|
||||
}
|
||||
else
|
||||
p_switch->ops_valid = true;
|
||||
}
|
||||
p_switch->ops.name = p_switch->ops_valid ? video_display_server_get_ident() : "dummy";
|
||||
crt_bind_display_server(p_switch);
|
||||
|
||||
p_switch->rtn = modeline_list_init(gen, &p_switch->ops) ? 0 : -1;
|
||||
RARCH_LOG("[CRT] Engine rtn %d.\n", p_switch->rtn);
|
||||
@@ -326,6 +361,14 @@ static bool crt_engine_init(videocrt_switch_t *p_switch,
|
||||
}
|
||||
}
|
||||
|
||||
else if (p_switch->ops_lost && p_switch->rtn >= 0)
|
||||
{
|
||||
/* Engine alive, display server rebuilt underneath it */
|
||||
crt_bind_display_server(p_switch);
|
||||
if (p_switch->ops_valid)
|
||||
RARCH_LOG("[CRT] Rebound to display server \"%s\".\n", p_switch->ops.name);
|
||||
}
|
||||
|
||||
if (p_switch->rtn >= 0)
|
||||
{
|
||||
p_switch->active = true;
|
||||
@@ -547,6 +590,7 @@ void crt_destroy_modes(videocrt_switch_t *p_switch)
|
||||
}
|
||||
memset(&p_switch->ops, 0, sizeof(p_switch->ops));
|
||||
p_switch->ops_valid = false;
|
||||
p_switch->ops_lost = false;
|
||||
}
|
||||
|
||||
void crt_switch_res_core(
|
||||
|
||||
@@ -72,6 +72,7 @@ typedef struct videocrt_switch
|
||||
uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan;
|
||||
bool active;
|
||||
bool ops_valid;
|
||||
bool ops_lost;
|
||||
bool menu_active;
|
||||
bool hh_core;
|
||||
|
||||
@@ -100,6 +101,12 @@ void crt_switch_res_core(
|
||||
|
||||
void crt_destroy_modes(videocrt_switch_t *p_switch);
|
||||
|
||||
/* The display server instance whose data is @data is about to be
|
||||
* destroyed: drop the ops table bound to it so nothing dereferences
|
||||
* freed memory, and arrange for the next switch to rebind and apply
|
||||
* the mode again. No-op when the consumer is not bound to it. */
|
||||
void crt_switch_display_server_lost(videocrt_switch_t *p_switch, void *data);
|
||||
|
||||
/* Write an EDID block for the configured CRT preset (menu mode, or
|
||||
* the ini set for mode 4) to <config>/edid/<preset>.bin; s receives
|
||||
* the path. Generation only: installing the block on a connector is
|
||||
|
||||
@@ -1614,7 +1614,13 @@ void video_display_server_destroy(void)
|
||||
|
||||
if (current_display_server && (current_display_server != &dispserv_null))
|
||||
if (video_st->current_display_server_data)
|
||||
{
|
||||
#if defined(HAVE_MODELINE)
|
||||
crt_switch_display_server_lost(&video_st->crt_switch_st,
|
||||
video_st->current_display_server_data);
|
||||
#endif
|
||||
current_display_server->destroy(video_st->current_display_server_data);
|
||||
}
|
||||
#if defined(HAVE_SDL2) || defined(HAVE_SDL3)
|
||||
if (sdl_display_server_data)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user