Add "Separate Windows" LayoutOption (#6177)

This commit is contained in:
Ameer J 2022-11-17 10:37:30 -05:00 committed by GitHub
parent 4f715b6718
commit f44c95d638
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 358 additions and 124 deletions

View file

@ -35,6 +35,7 @@
#include "core/loader/loader.h"
#include "core/movie.h"
#include "core/settings.h"
#include "input_common/main.h"
#include "network/network.h"
#include "video_core/renderer_base.h"
@ -359,11 +360,23 @@ int main(int argc, char** argv) {
// Register generic image interface
Core::System::GetInstance().RegisterImageInterface(std::make_shared<LodePNGImageInterface>());
std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2>(fullscreen)};
Frontend::ScopeAcquireContext scope(*emu_window);
Core::System& system = Core::System::GetInstance();
EmuWindow_SDL2::InitializeSDL2();
const Core::System::ResultStatus load_result{system.Load(*emu_window, filepath)};
const auto emu_window{std::make_unique<EmuWindow_SDL2>(fullscreen, false)};
const bool use_secondary_window{Settings::values.layout_option ==
Settings::LayoutOption::SeparateWindows};
const auto secondary_window =
use_secondary_window ? std::make_unique<EmuWindow_SDL2>(false, true) : nullptr;
Frontend::ScopeAcquireContext scope(*emu_window);
LOG_INFO(Frontend, "Citra Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
Common::g_scm_desc);
Settings::LogSettings();
Core::System& system = Core::System::GetInstance();
const Core::System::ResultStatus load_result{
system.Load(*emu_window, filepath, secondary_window.get())};
switch (load_result) {
case Core::System::ResultStatus::ErrorGetLoader:
@ -431,7 +444,12 @@ int main(int argc, char** argv) {
system.VideoDumper().StartDumping(dump_video, layout);
}
std::thread render_thread([&emu_window] { emu_window->Present(); });
std::thread main_render_thread([&emu_window] { emu_window->Present(); });
std::thread secondary_render_thread([&secondary_window] {
if (secondary_window) {
secondary_window->Present();
}
});
std::atomic_bool stop_run;
system.Renderer().Rasterizer()->LoadDiskResources(
@ -440,7 +458,11 @@ int main(int argc, char** argv) {
total);
});
while (emu_window->IsOpen()) {
const auto secondary_is_open = [&secondary_window] {
// if the secondary window isn't created, it shouldn't affect the main loop
return secondary_window ? secondary_window->IsOpen() : true;
};
while (emu_window->IsOpen() && secondary_is_open()) {
const auto result = system.RunLoop();
switch (result) {
@ -454,13 +476,21 @@ int main(int argc, char** argv) {
break;
}
}
render_thread.join();
emu_window->RequestClose();
if (secondary_window) {
secondary_window->RequestClose();
}
main_render_thread.join();
secondary_render_thread.join();
Core::Movie::GetInstance().Shutdown();
if (system.VideoDumper().IsDumping()) {
system.VideoDumper().StopDumping();
}
Network::Shutdown();
InputCommon::Shutdown();
system.Shutdown();
detached_tasks.WaitForAllTasks();

View file

@ -182,7 +182,11 @@ filter_mode =
[Layout]
# Layout for the screen inside the render window.
# 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen, 3: Side by Side
# 0 (default): Default Top Bottom Screen
# 1: Single Screen Only
# 2: Large Screen Small Screen
# 3: Side by Side
# 4: Separate Windows
layout_option =
# Toggle custom layout (using the settings below) on or off.

View file

@ -135,18 +135,8 @@ void EmuWindow_SDL2::Fullscreen() {
SDL_MaximizeWindow(render_window);
}
EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen, bool is_secondary) : EmuWindow(is_secondary) {
// Initialize the window
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) {
LOG_CRITICAL(Frontend, "Failed to initialize SDL2: {}! Exiting...", SDL_GetError());
exit(1);
}
InputCommon::Init();
Network::Init();
SDL_SetMainReady();
if (Settings::values.use_gles) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
@ -201,6 +191,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
exit(1);
}
render_window_id = SDL_GetWindowID(render_window);
auto gl_load_func = Settings::values.use_gles ? gladLoadGLES2Loader : gladLoadGLLoader;
if (!gl_load_func(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
@ -211,19 +202,26 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
OnResize();
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
SDL_PumpEvents();
LOG_INFO(Frontend, "Citra Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
Common::g_scm_desc);
Settings::LogSettings();
}
EmuWindow_SDL2::~EmuWindow_SDL2() {
core_context.reset();
Network::Shutdown();
InputCommon::Shutdown();
SDL_GL_DeleteContext(window_context);
SDL_Quit();
}
void EmuWindow_SDL2::InitializeSDL2() {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) {
LOG_CRITICAL(Frontend, "Failed to initialize SDL2: {}! Exiting...", SDL_GetError());
exit(1);
}
InputCommon::Init();
Network::Init();
SDL_SetMainReady();
}
std::unique_ptr<Frontend::GraphicsContext> EmuWindow_SDL2::CreateSharedContext() const {
return std::make_unique<SharedContext_SDL2>();
}
@ -240,7 +238,7 @@ void EmuWindow_SDL2::Present() {
SDL_GL_MakeCurrent(render_window, window_context);
SDL_GL_SetSwapInterval(1);
while (IsOpen()) {
VideoCore::g_renderer->TryPresent(100);
VideoCore::g_renderer->TryPresent(100, is_secondary);
SDL_GL_SwapWindow(render_window);
}
SDL_GL_MakeCurrent(render_window, nullptr);
@ -248,9 +246,14 @@ void EmuWindow_SDL2::Present() {
void EmuWindow_SDL2::PollEvents() {
SDL_Event event;
std::vector<SDL_Event> other_window_events;
// SDL_PollEvent returns 0 when there are no more events in the event queue
while (SDL_PollEvent(&event)) {
if (event.window.windowID != render_window_id) {
other_window_events.push_back(event);
continue;
}
switch (event.type) {
case SDL_WINDOWEVENT:
switch (event.window.event) {
@ -299,16 +302,13 @@ void EmuWindow_SDL2::PollEvents() {
break;
}
}
const u32 current_time = SDL_GetTicks();
if (current_time > last_time + 2000) {
const auto results = Core::System::GetInstance().GetAndResetPerfStats();
const auto title =
fmt::format("Citra {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname,
Common::g_scm_branch, Common::g_scm_desc, results.game_fps,
results.emulation_speed * 100.0f);
SDL_SetWindowTitle(render_window, title.c_str());
last_time = current_time;
for (auto& e : other_window_events) {
// This is a somewhat hacky workaround to re-emit window events meant for another window
// since SDL_PollEvent() is global but we poll events per window.
SDL_PushEvent(&e);
}
if (!is_secondary) {
UpdateFramerateCounter();
}
}
@ -323,3 +323,16 @@ void EmuWindow_SDL2::DoneCurrent() {
void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) {
SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
}
void EmuWindow_SDL2::UpdateFramerateCounter() {
const u32 current_time = SDL_GetTicks();
if (current_time > last_time + 2000) {
const auto results = Core::System::GetInstance().GetAndResetPerfStats();
const auto title =
fmt::format("Citra {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname,
Common::g_scm_branch, Common::g_scm_desc, results.game_fps,
results.emulation_speed * 100.0f);
SDL_SetWindowTitle(render_window, title.c_str());
last_time = current_time;
}
}

View file

@ -29,9 +29,11 @@ private:
class EmuWindow_SDL2 : public Frontend::EmuWindow {
public:
explicit EmuWindow_SDL2(bool fullscreen);
explicit EmuWindow_SDL2(bool fullscreen, bool is_secondary);
~EmuWindow_SDL2();
static void InitializeSDL2();
void Present();
/// Polls window events
@ -88,12 +90,18 @@ private:
/// Called when a configuration change affects the minimal size of the window
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
/// Called when polling to update framerate
void UpdateFramerateCounter();
/// Is the window still open?
bool is_open = true;
/// Internal SDL2 render window
SDL_Window* render_window;
/// Internal SDL2 window ID
int render_window_id{};
/// Fake hidden window for the core context
SDL_Window* dummy_window;