diff --git a/src/libretro/libretro-common/include/libretro.h b/src/libretro/libretro-common/include/libretro.h index bb114aed35..1096c7545f 100644 --- a/src/libretro/libretro-common/include/libretro.h +++ b/src/libretro/libretro-common/include/libretro.h @@ -1765,6 +1765,19 @@ enum retro_mod * (see enum retro_savestate_context) */ +#define RETRO_ENVIRONMENT_SET_MULTI_SCREEN_AVAILABILITY 82 + /* const struct retro_multi_screen_info * -- + * Tells the frontend how many screens this core can provide and + * their dimensions. Must be called in retro_load_game() before + * any retro_video_refresh_ext calls are made. + */ + +#define RETRO_ENVIRONMENT_GET_MULTI_SCREEN_ACTIVE 83 + /* struct retro_multi_screen_query * -- + * Allows the core to query which auxiliary screens the frontend + * actually wants rendered. screen_id 0 is always the main display. + */ + /* VFS functionality */ /* File paths: @@ -3800,6 +3813,40 @@ typedef bool (RETRO_CALLCONV *retro_environment_t)(unsigned cmd, void *data); typedef void (RETRO_CALLCONV *retro_video_refresh_t)(const void *data, unsigned width, unsigned height, size_t pitch); +/** + * Extended video refresh callback for multi-screen cores. + * @param data Framebuffer data. + * @param width Width of the framebuffer. + * @param height Height of the framebuffer. + * @param pitch Pitch of the framebuffer. + * @param screen_id Screen identifier (0 = main display, 1+ = auxiliary). + * @see RETRO_ENVIRONMENT_SET_MULTI_SCREEN_AVAILABILITY + * @see retro_set_video_refresh_ext + */ +typedef void (RETRO_CALLCONV *retro_video_refresh_ext_t)(const void *data, unsigned width, + unsigned height, size_t pitch, unsigned screen_id); + +/** + * Multi-screen capability info passed to RETRO_ENVIRONMENT_SET_MULTI_SCREEN_AVAILABILITY. + * Core declares how many screens it supports and their properties. + */ +struct retro_multi_screen_info +{ + unsigned num_screens; /* Total screens available (e.g., 2 for DS) */ + unsigned screen_width; /* Width of each screen (assumed same for all) */ + unsigned screen_height; /* Height of each screen (assumed same for all) */ +}; + +/** + * Query for RETRO_ENVIRONMENT_GET_MULTI_SCREEN_ACTIVE. + * Frontend tells core which screens are currently active. + */ +struct retro_multi_screen_query +{ + unsigned num_active_screens; /* How many screens frontend wants */ + unsigned active_screen_ids[4]; /* Which screen IDs to render (0=main, 1=aux1, etc.) */ +}; + /* Renders a single audio frame. Should only be used if implementation * generates a single sample at a time. * Format is signed 16-bit native endian. @@ -3835,6 +3882,7 @@ typedef int16_t (RETRO_CALLCONV *retro_input_state_t)(unsigned port, unsigned de * before the first call to retro_run() is made. */ RETRO_API void retro_set_environment(retro_environment_t); RETRO_API void retro_set_video_refresh(retro_video_refresh_t); +RETRO_API void retro_set_video_refresh_ext(retro_video_refresh_ext_t); RETRO_API void retro_set_audio_sample(retro_audio_sample_t); RETRO_API void retro_set_audio_sample_batch(retro_audio_sample_batch_t); RETRO_API void retro_set_input_poll(retro_input_poll_t); diff --git a/src/libretro/libretro.cpp b/src/libretro/libretro.cpp index 2207849bfa..fedb1d816c 100644 --- a/src/libretro/libretro.cpp +++ b/src/libretro/libretro.cpp @@ -35,6 +35,9 @@ retro_input_poll_t input_poll_cb; retro_input_state_t input_state_cb; retro_log_printf_t log_cb; retro_video_refresh_t video_cb; +retro_video_refresh_ext_t video_cb_ext = NULL; + +bool aux_stream_bottom_screen = false; std::string save_path; @@ -124,11 +127,23 @@ void retro_get_system_av_info(struct retro_system_av_info *info) { info->timing.fps = 32.0f * 1024.0f * 1024.0f / 560190.0f; info->timing.sample_rate = 32.0f * 1024.0f; - info->geometry.base_width = screen_layout_data.buffer_width; - info->geometry.base_height = screen_layout_data.buffer_height; - info->geometry.max_width = screen_layout_data.buffer_width; - info->geometry.max_height = screen_layout_data.buffer_height; - info->geometry.aspect_ratio = (float)screen_layout_data.buffer_width / (float)screen_layout_data.buffer_height; + + if (aux_stream_bottom_screen) + { + info->geometry.base_width = screen_layout_data.screen_width; + info->geometry.base_height = screen_layout_data.screen_height; + info->geometry.max_width = screen_layout_data.screen_width; + info->geometry.max_height = screen_layout_data.screen_height; + info->geometry.aspect_ratio = (float)screen_layout_data.screen_width / (float)screen_layout_data.screen_height; + } + else + { + info->geometry.base_width = screen_layout_data.buffer_width; + info->geometry.base_height = screen_layout_data.buffer_height; + info->geometry.max_width = screen_layout_data.buffer_width; + info->geometry.max_height = screen_layout_data.buffer_height; + info->geometry.aspect_ratio = (float)screen_layout_data.buffer_width / (float)screen_layout_data.buffer_height; + } } static bool update_option_visibility(void) @@ -311,6 +326,11 @@ void retro_set_video_refresh(retro_video_refresh_t cb) video_cb = cb; } +RETRO_API void retro_set_video_refresh_ext(retro_video_refresh_ext_t cb) +{ + video_cb_ext = cb; +} + void retro_set_controller_port_device(unsigned port, unsigned device) { log_cb(RETRO_LOG_INFO, "Plugging device %u into port %u.\n", device, port); @@ -370,6 +390,15 @@ static void check_variables(bool init) layout = ScreenLayout::HybridBottom; } + var.key = "melonds_aux_stream_bottom_screen"; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + aux_stream_bottom_screen = !strcmp(var.value, "enabled"); + else + aux_stream_bottom_screen = false; + + if (aux_stream_bottom_screen) + layout = ScreenLayout::TopOnly; + var.key = "melonds_screen_gap"; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { @@ -616,7 +645,7 @@ static void check_variables(bool init) input_state.current_touch_mode = new_touch_mode; - update_screenlayout(layout, &screen_layout_data, enable_opengl, swapped_screens); + update_screenlayout(layout, &screen_layout_data, enable_opengl, aux_stream_bottom_screen ? false : swapped_screens); update_option_visibility(); } @@ -659,12 +688,13 @@ static void render_frame(void) #ifdef HAVE_OPENGL if(using_opengl) { + log_cb(RETRO_LOG_INFO, "[melonDS] Using OpenGL rendering\n"); if (current_renderer == CurrentRenderer::Software) render_opengl_frame(true); else render_opengl_frame(false); } - else if(!enable_opengl) + else +#endif { - #endif int frontbuf = GPU::FrontBuffer; if(screen_layout_data.hybrid) @@ -703,9 +733,16 @@ static void render_frame(void) video_cb((uint8_t*)screen_layout_data.buffer_ptr, screen_layout_data.buffer_width, screen_layout_data.buffer_height, screen_layout_data.buffer_width * sizeof(uint32_t)); } -#ifdef HAVE_OPENGL } -#endif + + if (aux_stream_bottom_screen && screen_layout_data.enable_top_screen) + { + int frontbuf = GPU::FrontBuffer; + unsigned pitch = VIDEO_WIDTH * sizeof(uint32_t); + + if (video_cb_ext) + video_cb_ext((uint8_t*)GPU::Framebuffer[frontbuf][1], VIDEO_WIDTH, VIDEO_HEIGHT, pitch, 1); + } } void retro_run(void) @@ -719,7 +756,7 @@ void retro_run(void) if (swapped_screens == false) { swap_screen_toggled = !swap_screen_toggled; - update_screenlayout(current_screen_layout, &screen_layout_data, enable_opengl, swap_screen_toggled); + update_screenlayout(current_screen_layout, &screen_layout_data, enable_opengl, aux_stream_bottom_screen ? false : swap_screen_toggled); refresh_opengl = true; } @@ -728,7 +765,7 @@ void retro_run(void) else { swapped_screens = input_state.swap_screens_btn; - update_screenlayout(current_screen_layout, &screen_layout_data, enable_opengl, swapped_screens); + update_screenlayout(current_screen_layout, &screen_layout_data, enable_opengl, aux_stream_bottom_screen ? false : swapped_screens); refresh_opengl = true; } } @@ -875,6 +912,16 @@ static bool _handle_load_game(unsigned type, const struct retro_game_info *info) check_variables(true); + if (aux_stream_bottom_screen) + { + struct retro_multi_screen_info info = { + 2, + screen_layout_data.screen_width, + screen_layout_data.screen_height + }; + environ_cb(RETRO_ENVIRONMENT_SET_MULTI_SCREEN_AVAILABILITY, &info); + } + // Initialize the opengl state if needed #ifdef HAVE_OPENGL if (enable_opengl) diff --git a/src/libretro/libretro_core_options.h b/src/libretro/libretro_core_options.h index e50715cd8c..20cd8f55bd 100644 --- a/src/libretro/libretro_core_options.h +++ b/src/libretro/libretro_core_options.h @@ -344,6 +344,20 @@ struct retro_core_option_v2_definition option_defs_us[] = { }, "Top/Bottom" }, + { + "melonds_aux_stream_bottom_screen", + "Stream Bottom Screen to Auxiliary Display", + NULL, + "Stream bottom screen to auxiliary display via extended callback.", + NULL, + "screen", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, { "melonds_screen_gap", "Screen Gap", diff --git a/src/libretro/libretro_state.h b/src/libretro/libretro_state.h index 2103cc84f6..4196e12ef9 100644 --- a/src/libretro/libretro_state.h +++ b/src/libretro/libretro_state.h @@ -14,5 +14,6 @@ extern retro_input_poll_t input_poll_cb; extern retro_input_state_t input_state_cb; extern retro_log_printf_t log_cb; extern retro_video_refresh_t video_cb; +extern retro_video_refresh_ext_t video_cb_ext; #endif