SLD context now running in main thread (for better compatibility)

This commit is contained in:
Ondřej Novák 2025-08-28 12:17:24 +02:00
parent 96eaeb4851
commit e0aa5096ba
16 changed files with 259 additions and 275 deletions

View file

@ -10,7 +10,12 @@ static std::unique_ptr<uint16_t[]> buffer2nd;
static uint16_t *render_target;
static uint16_t screen_pitch = 640;
char game_display_init(const INI_CONFIG_SECTION *display_section, const char *title) {
int game_display_init(const INI_CONFIG_SECTION *display_section,
const char *title,
int (*game_thread)(va_list), ...) {
va_list args;
va_start(args,game_thread);
SDLContext::VideoConfig cfg = {};
const char *aspect_str;
@ -43,19 +48,19 @@ char game_display_init(const INI_CONFIG_SECTION *display_section, const char *ti
cfg.cursor_size = ini_get_int(display_section, "cursor_size", 100)*0.01f;
screen_pitch = 640;
get_sdl_global_context().init_video(cfg, title);
screen_buffer = std::make_unique<uint16_t[]>(screen_pitch*480);
buffer2nd = std::make_unique<uint16_t[]>(screen_pitch*480);
std::fill(screen_buffer.get(), screen_buffer.get()+screen_pitch*480,0);
render_target = screen_buffer.get();
return 1;
return get_sdl_global_context().init_window(cfg, title, [&]{
screen_buffer = std::make_unique<uint16_t[]>(screen_pitch*480);
buffer2nd = std::make_unique<uint16_t[]>(screen_pitch*480);
std::fill(screen_buffer.get(), screen_buffer.get()+screen_pitch*480,0);
render_target = screen_buffer.get();
return game_thread(args);
});
}
void game_display_close(void) {
get_sdl_global_context().close_video();
}
uint16_t *GetScreenAdr() {
return render_target;

View file

@ -1,4 +1,5 @@
#include <stdint.h>
#include <stdarg.h>
#include "../config.h"
#include <stddef.h>
@ -19,8 +20,11 @@ void RedirectScreen(uint16_t *newaddr);
void RestoreScreen(void);
void RedirectScreenBufferSecond(void);
char game_display_init(const INI_CONFIG_SECTION *display_section, const char *title);
void game_display_close(void);
///Initializes display - in current thread (main thread), starts game thread. Display is closed when thread finishes
int game_display_init(const INI_CONFIG_SECTION *display_section,
const char *title,
int (*game_thread)(va_list), ...);
void game_display_update_rect(unsigned short x,unsigned short y,unsigned short xs,unsigned short ys);
char game_display_is_quit_requested();
void game_display_cancel_quit_request();

View file

@ -268,14 +268,12 @@ static Uint32 find_best_rgba_like_format(SDL_Renderer* renderer) {
return 0;
}
void SDLContext::init_video(const VideoConfig &config, const char *title) {
int SDLContext::init_window(const VideoConfig &config, const char *title, std::function<int()> game_thread) {
static Uint32 update_request_event = SDL_RegisterEvents(1);
static Uint32 refresh_request_event = SDL_RegisterEvents(1);
_update_request_event = update_request_event;
_refresh_request = refresh_request_event;
assert(!_render_thread.joinable());
int width = config.window_width;
int height = config.window_height;
@ -294,142 +292,132 @@ void SDLContext::init_video(const VideoConfig &config, const char *title) {
std::exception_ptr e;
std::string_view stage;
std::string rname;
_render_thread = std::jthread([&](std::stop_token stp){
bool err = false;
try {
stage = "window";
SDL_Window *window = SDL_CreateWindow(title,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
width, height, SDL_WINDOW_RESIZABLE|(_fullscreen_mode?SDL_WINDOW_FULLSCREEN_DESKTOP:0));
int exit_code = 0;
try {
stage = "window";
SDL_Window *window = SDL_CreateWindow(title,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
width, height, SDL_WINDOW_RESIZABLE|(_fullscreen_mode?SDL_WINDOW_FULLSCREEN_DESKTOP:0));
if (!window) {
handle_sdl_error("SDL Error create window");
}
_window.reset(window);
auto composer = config.composer;
stage = "renderer";
while (true) {
SDL_Renderer *renderer = SDL_CreateRenderer(_window.get(), -1, composer);
if (!renderer) {
if (config.composer & SDL_RENDERER_SOFTWARE) {
handle_sdl_error("Failed to create composer");
} else {
composer |= SDL_RENDERER_SOFTWARE;
continue;
}
}
_texture_render_format = find_best_rgba_like_format(renderer);
if (_texture_render_format == 0) {
if (composer & SDL_RENDERER_SOFTWARE) {
throw std::runtime_error("Failed to create composer, failed software fallback");
} else {
SDL_DestroyRenderer(renderer);
composer |= SDL_RENDERER_SOFTWARE;
continue;
}
}
_renderer.reset(renderer);
break;
}
SDL_RendererInfo rinfo;
SDL_GetRendererInfo(_renderer.get(), &rinfo);
rname = rinfo.name;
stage = "pixel format";
_main_pixel_format.reset(SDL_AllocFormat(_texture_render_format));
if (!_main_pixel_format) {
handle_sdl_error("Failed to create texture format");
}
if (istrcmp(config.scale_quality, "auto") == 0) {
if (rinfo.flags & SDL_RENDERER_ACCELERATED) {
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
}
} else {
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, config.scale_quality);
}
stage = "main render target";
SDL_Texture *texture = SDL_CreateTexture(_renderer.get(), _texture_render_format, SDL_TEXTUREACCESS_STREAMING, 640, 480);
if (!texture) {
handle_sdl_error("Failed to create render target");
}
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
_texture.reset(texture);
stage = "secondary render target";
texture = SDL_CreateTexture(_renderer.get(), _texture_render_format, SDL_TEXTUREACCESS_STREAMING, 640, 480);
if (!texture) {
handle_sdl_error("Failed to create second render target");
}
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
_texture2.reset(texture);
stage = "all done";
_visible_texture = _texture.get();
_hidden_texture = _texture2.get();
} catch (...) {
e = std::current_exception();
err = true;
if (!window) {
handle_sdl_error("SDL Error create window");
}
done = true;
done.notify_all();
if (!err) {
_window.reset(window);
auto composer = config.composer;
stage = "renderer";
while (true) {
SDL_Renderer *renderer = SDL_CreateRenderer(_window.get(), -1, composer);
if (!renderer) {
if (config.composer & SDL_RENDERER_SOFTWARE) {
handle_sdl_error("Failed to create composer");
} else {
composer |= SDL_RENDERER_SOFTWARE;
continue;
}
}
_texture_render_format = find_best_rgba_like_format(renderer);
if (_texture_render_format == 0) {
if (composer & SDL_RENDERER_SOFTWARE) {
throw std::runtime_error("Failed to create composer, failed software fallback");
} else {
SDL_DestroyRenderer(renderer);
composer |= SDL_RENDERER_SOFTWARE;
continue;
}
}
_renderer.reset(renderer);
break;
}
SDL_RendererInfo rinfo;
SDL_GetRendererInfo(_renderer.get(), &rinfo);
rname = rinfo.name;
stage = "pixel format";
_main_pixel_format.reset(SDL_AllocFormat(_texture_render_format));
if (!_main_pixel_format) {
handle_sdl_error("Failed to create texture format");
}
if (istrcmp(config.scale_quality, "auto") == 0) {
if (rinfo.flags & SDL_RENDERER_ACCELERATED) {
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
}
} else {
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, config.scale_quality);
}
stage = "main render target";
SDL_Texture *texture = SDL_CreateTexture(_renderer.get(), _texture_render_format, SDL_TEXTUREACCESS_STREAMING, 640, 480);
if (!texture) {
handle_sdl_error("Failed to create render target");
}
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
_texture.reset(texture);
stage = "secondary render target";
texture = SDL_CreateTexture(_renderer.get(), _texture_render_format, SDL_TEXTUREACCESS_STREAMING, 640, 480);
if (!texture) {
handle_sdl_error("Failed to create second render target");
}
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
_texture2.reset(texture);
stage = "all done";
_visible_texture = _texture.get();
_hidden_texture = _texture2.get();
std::stop_source stop_src;
std::thread main_thrd([&]{
try {
SDL_ShowCursor(SDL_DISABLE);
event_loop(stp);
exit_code = game_thread();
} catch (...) {
SDL_ShowCursor(SDL_ENABLE);
crash_sdl_exception();
e = std::current_exception();
}
}
_texture.reset();
_texture2.reset();
_renderer.reset();
_window.reset();
});
stop_src.request_stop();
});
main_thrd.detach();
SDL_ShowCursor(SDL_DISABLE);
event_loop(stop_src.get_token());
SDL_ShowCursor(SDL_ENABLE);
done.wait(false);
if (e) {
_render_thread.join();
try {
std::rethrow_exception(e);
} catch (...) {
std::throw_with_nested(
std::runtime_error(std::string("Oops! The application couldn't start properly (problem during SDL initialization). Stage: [")
.append(stage).append("]\n\n"
"Renderer: ").append(rname).append("\n\n"
"This may be caused by outdated or missing graphics or audio drivers."
"To fix this, please try the following:\n- Restart your computer and try again\n- "
"Make sure your graphics and sound drivers are up to date.")));
}
} catch (...) {
crash_sdl_exception();
return -1;
}
_texture.reset();
_texture2.reset();
_renderer.reset();
_window.reset();
if (e) {
std::rethrow_exception(e);
}
return exit_code;
}
void SDLContext::init_video(const VideoConfig &config, const char *title) {
}
void SDLContext::close_video() {
_render_thread.request_stop();
_render_thread.join();
}
int SDLContext::check_axis_dir(int &cooldown, int value) {
int range = 0x8000-_jcontrol_map.walk_deadzone;

View file

@ -98,13 +98,14 @@ public:
int freq;
};
int init_window(const VideoConfig &config, const char *title, std::function<int()> game_thread);
void init_video(const VideoConfig &config, const char *title);
void set_window_icon(const void *icon_data, size_t icon_size);
void configure_controller(const JoystickConfig &cfg);
void close_video();
AudioInfo init_audio(const AudioConfig &config, SDL_AudioCallback cb, void *cb_ctx);
void pause_audio(bool pause);
@ -272,7 +273,6 @@ protected:
Uint32 _update_request_event;
Uint32 _refresh_request;
std::jthread _render_thread;
void event_loop(std::stop_token stp);