Add "Separate Windows" LayoutOption (#6177)
This commit is contained in:
parent
4f715b6718
commit
f44c95d638
24 changed files with 358 additions and 124 deletions
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue