mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2025-03-26 07:28:26 +01:00
Update everything to enable second screen on android under GL and Vulkan. Still some issues!
This commit is contained in:
parent
c4667b1cf0
commit
12afea9015
@ -125,6 +125,9 @@ object NativeLibrary {
|
||||
external fun surfaceDestroyed()
|
||||
external fun doFrame()
|
||||
|
||||
//Second window
|
||||
external fun enableSecondWindow(secondary_surface: Surface)
|
||||
external fun disableSecondWindow()
|
||||
/**
|
||||
* Unpauses emulation from a paused state.
|
||||
*/
|
||||
|
@ -1,11 +1,10 @@
|
||||
import android.app.Presentation
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.os.Bundle
|
||||
import android.view.Display
|
||||
import android.view.SurfaceHolder
|
||||
import android.view.SurfaceView
|
||||
import org.citra.citra_emu.NativeLibrary
|
||||
|
||||
class SecondScreenPresentation(
|
||||
context: Context,
|
||||
@ -20,7 +19,7 @@ class SecondScreenPresentation(
|
||||
surfaceView = SurfaceView(context)
|
||||
surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
|
||||
override fun surfaceCreated(holder: SurfaceHolder) {
|
||||
|
||||
NativeLibrary.enableSecondWindow(holder.surface)
|
||||
}
|
||||
|
||||
override fun surfaceChanged(
|
||||
@ -29,9 +28,12 @@ class SecondScreenPresentation(
|
||||
width: Int,
|
||||
height: Int
|
||||
) {
|
||||
NativeLibrary.enableSecondWindow(holder.surface)
|
||||
}
|
||||
|
||||
override fun surfaceDestroyed(holder: SurfaceHolder) {}
|
||||
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
||||
NativeLibrary.disableSecondWindow();
|
||||
}
|
||||
})
|
||||
|
||||
setContentView(surfaceView) // Set SurfaceView as content
|
||||
|
@ -1329,7 +1329,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
||||
}
|
||||
|
||||
private fun runWithValidSurface() {
|
||||
NativeLibrary.surfaceChanged(surface!!, surface2!!)
|
||||
NativeLibrary.surfaceChanged(surface!!)
|
||||
when (state) {
|
||||
State.STOPPED -> {
|
||||
Thread({
|
||||
|
@ -49,16 +49,17 @@ void EmuWindow_Android::OnFramebufferSizeChanged() {
|
||||
|
||||
const int bigger{window_width > window_height ? window_width : window_height};
|
||||
const int smaller{window_width < window_height ? window_width : window_height};
|
||||
if (is_portrait_mode) {
|
||||
if (is_portrait_mode && !is_secondary) {
|
||||
UpdateCurrentFramebufferLayout(smaller, bigger, is_portrait_mode);
|
||||
} else {
|
||||
UpdateCurrentFramebufferLayout(bigger, smaller, is_portrait_mode);
|
||||
}
|
||||
}
|
||||
|
||||
EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface) : host_window{surface} {
|
||||
EmuWindow_Android::EmuWindow_Android(ANativeWindow *surface, bool is_secondary) : EmuWindow{
|
||||
is_secondary}, host_window(surface) {
|
||||
LOG_DEBUG(Frontend, "Initializing EmuWindow_Android");
|
||||
|
||||
if (is_secondary) LOG_DEBUG(Frontend, "Initializing secondary window Android");
|
||||
if (!surface) {
|
||||
LOG_CRITICAL(Frontend, "surface is nullptr");
|
||||
return;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <EGL/egl.h>
|
||||
#include "core/frontend/emu_window.h"
|
||||
|
||||
namespace Core {
|
||||
@ -13,7 +14,7 @@ class System;
|
||||
|
||||
class EmuWindow_Android : public Frontend::EmuWindow {
|
||||
public:
|
||||
EmuWindow_Android(ANativeWindow* surface);
|
||||
EmuWindow_Android(ANativeWindow* surface, bool is_secondary = false);
|
||||
~EmuWindow_Android();
|
||||
|
||||
/// Called by the onSurfaceChanges() method to change the surface
|
||||
@ -30,7 +31,10 @@ public:
|
||||
void DoneCurrent() override;
|
||||
|
||||
virtual void TryPresenting() {}
|
||||
|
||||
// EGL Context must be shared
|
||||
// could probably use the existing
|
||||
// SharedContext for this instead, this is maybe temporary
|
||||
virtual EGLContext* GetEGLContext() {return NULL;}
|
||||
virtual void StopPresenting() {}
|
||||
|
||||
protected:
|
||||
|
@ -72,8 +72,8 @@ private:
|
||||
EGLContext egl_context{};
|
||||
};
|
||||
|
||||
EmuWindow_Android_OpenGL::EmuWindow_Android_OpenGL(Core::System& system_, ANativeWindow* surface)
|
||||
: EmuWindow_Android{surface}, system{system_} {
|
||||
EmuWindow_Android_OpenGL::EmuWindow_Android_OpenGL(Core::System& system_, ANativeWindow* surface, bool is_secondary, EGLContext* sharedContext)
|
||||
: EmuWindow_Android{surface,is_secondary}, system{system_} {
|
||||
if (egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); egl_display == EGL_NO_DISPLAY) {
|
||||
LOG_CRITICAL(Frontend, "eglGetDisplay() failed");
|
||||
return;
|
||||
@ -96,8 +96,9 @@ EmuWindow_Android_OpenGL::EmuWindow_Android_OpenGL(Core::System& system_, ANativ
|
||||
if (eglQuerySurface(egl_display, egl_surface, EGL_HEIGHT, &window_height) != EGL_TRUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (egl_context = eglCreateContext(egl_display, egl_config, 0, egl_context_attribs.data());
|
||||
if (sharedContext) {
|
||||
egl_context = *sharedContext;
|
||||
}else if (egl_context = eglCreateContext(egl_display, egl_config, 0, egl_context_attribs.data());
|
||||
egl_context == EGL_NO_CONTEXT) {
|
||||
LOG_CRITICAL(Frontend, "eglCreateContext() failed");
|
||||
return;
|
||||
@ -127,6 +128,10 @@ EmuWindow_Android_OpenGL::EmuWindow_Android_OpenGL(Core::System& system_, ANativ
|
||||
OnFramebufferSizeChanged();
|
||||
}
|
||||
|
||||
EGLContext* EmuWindow_Android_OpenGL::GetEGLContext() {
|
||||
return &egl_context;
|
||||
}
|
||||
|
||||
bool EmuWindow_Android_OpenGL::CreateWindowSurface() {
|
||||
if (!host_window) {
|
||||
return true;
|
||||
@ -204,14 +209,15 @@ void EmuWindow_Android_OpenGL::TryPresenting() {
|
||||
return;
|
||||
}
|
||||
if (presenting_state == PresentingState::Initial) [[unlikely]] {
|
||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
presenting_state = PresentingState::Running;
|
||||
}
|
||||
if (presenting_state != PresentingState::Running) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
// if (presenting_state != PresentingState::Running) [[unlikely]] {
|
||||
// return;
|
||||
// }
|
||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0);
|
||||
system.GPU().Renderer().TryPresent(0);
|
||||
system.GPU().Renderer().TryPresent(100,is_secondary);
|
||||
eglSwapBuffers(egl_display, egl_surface);
|
||||
|
||||
}
|
||||
|
@ -19,13 +19,13 @@ struct ANativeWindow;
|
||||
|
||||
class EmuWindow_Android_OpenGL : public EmuWindow_Android {
|
||||
public:
|
||||
EmuWindow_Android_OpenGL(Core::System& system, ANativeWindow* surface);
|
||||
EmuWindow_Android_OpenGL(Core::System& system, ANativeWindow* surface, bool is_secondary, EGLContext* sharedContext = NULL);
|
||||
~EmuWindow_Android_OpenGL() override = default;
|
||||
|
||||
void TryPresenting() override;
|
||||
void StopPresenting() override;
|
||||
void PollEvents() override;
|
||||
|
||||
EGLContext* GetEGLContext() override;
|
||||
std::unique_ptr<GraphicsContext> CreateSharedContext() const override;
|
||||
|
||||
private:
|
||||
|
@ -24,8 +24,8 @@ private:
|
||||
};
|
||||
|
||||
EmuWindow_Android_Vulkan::EmuWindow_Android_Vulkan(
|
||||
ANativeWindow* surface, std::shared_ptr<Common::DynamicLibrary> driver_library_)
|
||||
: EmuWindow_Android{surface}, driver_library{driver_library_} {
|
||||
ANativeWindow* surface, std::shared_ptr<Common::DynamicLibrary> driver_library_, bool is_secondary)
|
||||
: EmuWindow_Android{surface,is_secondary}, driver_library{driver_library_} {
|
||||
CreateWindowSurface();
|
||||
|
||||
if (core_context = CreateSharedContext(); !core_context) {
|
||||
|
@ -11,7 +11,7 @@ struct ANativeWindow;
|
||||
class EmuWindow_Android_Vulkan : public EmuWindow_Android {
|
||||
public:
|
||||
EmuWindow_Android_Vulkan(ANativeWindow* surface,
|
||||
std::shared_ptr<Common::DynamicLibrary> driver_library);
|
||||
std::shared_ptr<Common::DynamicLibrary> driver_library, bool is_secondary);
|
||||
~EmuWindow_Android_Vulkan() override = default;
|
||||
|
||||
void PollEvents() override {}
|
||||
|
@ -65,9 +65,12 @@
|
||||
namespace {
|
||||
|
||||
ANativeWindow* s_surf;
|
||||
ANativeWindow* s_secondary_surface;
|
||||
bool secondary_enabled = false;
|
||||
|
||||
std::shared_ptr<Common::DynamicLibrary> vulkan_library{};
|
||||
std::unique_ptr<EmuWindow_Android> window;
|
||||
std::unique_ptr<EmuWindow_Android> second_window;
|
||||
|
||||
std::atomic<bool> stop_run{true};
|
||||
std::atomic<bool> pause_emulation{false};
|
||||
@ -118,8 +121,10 @@ static void TryShutdown() {
|
||||
}
|
||||
|
||||
window->DoneCurrent();
|
||||
if (second_window) second_window->DoneCurrent();
|
||||
Core::System::GetInstance().Shutdown();
|
||||
window.reset();
|
||||
if (second_window) second_window.reset();
|
||||
InputManager::Shutdown();
|
||||
MicroProfileShutdown();
|
||||
}
|
||||
@ -148,12 +153,17 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
||||
switch (graphics_api) {
|
||||
#ifdef ENABLE_OPENGL
|
||||
case Settings::GraphicsAPI::OpenGL:
|
||||
window = std::make_unique<EmuWindow_Android_OpenGL>(system, s_surf);
|
||||
window = std::make_unique<EmuWindow_Android_OpenGL>(system, s_surf,false);
|
||||
if (secondary_enabled) {
|
||||
EGLContext* c = window->GetEGLContext();
|
||||
second_window = std::make_unique<EmuWindow_Android_OpenGL>(system,s_secondary_surface, true, c);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
case Settings::GraphicsAPI::Vulkan:
|
||||
window = std::make_unique<EmuWindow_Android_Vulkan>(s_surf, vulkan_library);
|
||||
window = std::make_unique<EmuWindow_Android_Vulkan>(s_surf, vulkan_library,false);
|
||||
if (secondary_enabled) second_window = std::make_unique<EmuWindow_Android_Vulkan>(s_secondary_surface, vulkan_library, true);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
@ -161,9 +171,14 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
||||
"Unknown or unsupported graphics API {}, falling back to available default",
|
||||
graphics_api);
|
||||
#ifdef ENABLE_OPENGL
|
||||
window = std::make_unique<EmuWindow_Android_OpenGL>(system, s_surf);
|
||||
window = std::make_unique<EmuWindow_Android_OpenGL>(system, s_surf, false);
|
||||
if (secondary_enabled) {
|
||||
EGLContext *c = window->GetEGLContext();
|
||||
second_window = std::make_unique<EmuWindow_Android_OpenGL>(system, s_secondary_surface,true, c);
|
||||
}
|
||||
#elif ENABLE_VULKAN
|
||||
window = std::make_unique<EmuWindow_Android_Vulkan>(s_surf, vulkan_library);
|
||||
if (secondary_enabled) second_window = std::make_unique<EmuWindow_Android_Vulkan>(s_secondary_surface, vulkan_library, true);
|
||||
#else
|
||||
// TODO: Add a null renderer backend for this, perhaps.
|
||||
#error "At least one renderer must be enabled."
|
||||
@ -202,7 +217,8 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
||||
InputManager::Init();
|
||||
|
||||
window->MakeCurrent();
|
||||
const Core::System::ResultStatus load_result{system.Load(*window, filepath)};
|
||||
//if (second_window) second_window->MakeCurrent();
|
||||
const Core::System::ResultStatus load_result{system.Load(*window, filepath,second_window.get())};
|
||||
if (load_result != Core::System::ResultStatus::Success) {
|
||||
return load_result;
|
||||
}
|
||||
@ -249,6 +265,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
||||
std::unique_lock pause_lock{paused_mutex};
|
||||
running_cv.wait(pause_lock, [] { return !pause_emulation || stop_run; });
|
||||
window->PollEvents();
|
||||
//if (second_window) second_window->PollEvents();
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,6 +323,23 @@ void Java_org_citra_citra_1emu_NativeLibrary_surfaceChanged(JNIEnv* env,
|
||||
LOG_INFO(Frontend, "Surface changed");
|
||||
}
|
||||
|
||||
void Java_org_citra_citra_1emu_NativeLibrary_enableSecondWindow(JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj,jobject surf) {
|
||||
s_secondary_surface = ANativeWindow_fromSurface(env, surf);
|
||||
secondary_enabled = true;
|
||||
|
||||
LOG_INFO(Frontend, "Secondary Surface Enabled");
|
||||
}
|
||||
|
||||
void Java_org_citra_citra_1emu_NativeLibrary_disableSecondWindow(JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj) {
|
||||
|
||||
secondary_enabled = false;
|
||||
// how do I delete the window? TODO
|
||||
|
||||
LOG_INFO(Frontend, "Secondary Surface Disabled");
|
||||
}
|
||||
|
||||
void Java_org_citra_citra_1emu_NativeLibrary_surfaceDestroyed([[maybe_unused]] JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj) {
|
||||
if (s_surf != nullptr) {
|
||||
@ -325,6 +359,9 @@ void Java_org_citra_citra_1emu_NativeLibrary_doFrame([[maybe_unused]] JNIEnv* en
|
||||
if (window) {
|
||||
window->TryPresenting();
|
||||
}
|
||||
if (second_window) {
|
||||
second_window->TryPresenting();
|
||||
}
|
||||
}
|
||||
|
||||
void JNICALL Java_org_citra_citra_1emu_NativeLibrary_initializeGpuDriver(
|
||||
@ -481,6 +518,7 @@ void Java_org_citra_citra_1emu_NativeLibrary_stopEmulation([[maybe_unused]] JNIE
|
||||
stop_run = true;
|
||||
pause_emulation = false;
|
||||
window->StopPresenting();
|
||||
if (second_window) second_window->StopPresenting();
|
||||
running_cv.notify_all();
|
||||
}
|
||||
|
||||
@ -745,4 +783,4 @@ void Java_org_citra_citra_1emu_NativeLibrary_logDeviceInfo([[maybe_unused]] JNIE
|
||||
LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level());
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
}
|
@ -251,6 +251,13 @@ void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_po
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef ANDROID
|
||||
// if is_secondary is set on android, MUST be a second window
|
||||
if (is_secondary) {
|
||||
layout = Layout::SeparateWindowsLayout(width, height, is_secondary,
|
||||
Settings::values.upright_screen.GetValue());
|
||||
}
|
||||
#endif
|
||||
UpdateMinimumWindowSize(min_size);
|
||||
|
||||
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) {
|
||||
|
@ -35,7 +35,7 @@ void RendererBase::UpdateCurrentFramebufferLayout(bool is_portrait_mode) {
|
||||
window.UpdateCurrentFramebufferLayout(layout.width, layout.height, is_portrait_mode);
|
||||
};
|
||||
update_layout(render_window);
|
||||
if (secondary_window) {
|
||||
if (secondary_window != nullptr) {
|
||||
update_layout(*secondary_window);
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ void RendererOpenGL::SwapBuffers() {
|
||||
PrepareRendertarget();
|
||||
RenderScreenshot();
|
||||
|
||||
const auto& main_layout = render_window.GetFramebufferLayout();
|
||||
const auto &main_layout = render_window.GetFramebufferLayout();
|
||||
RenderToMailbox(main_layout, render_window.mailbox, false);
|
||||
|
||||
#ifndef ANDROID
|
||||
@ -106,6 +106,15 @@ void RendererOpenGL::SwapBuffers() {
|
||||
RenderToMailbox(secondary_layout, secondary_window->mailbox, false);
|
||||
secondary_window->PollEvents();
|
||||
}
|
||||
#endif
|
||||
#ifdef ANDROID
|
||||
// on android, if secondary_window is defined at all
|
||||
// it means we have a second display
|
||||
if (secondary_window) {
|
||||
const auto &secondary_layout = secondary_window->GetFramebufferLayout();
|
||||
RenderToMailbox(secondary_layout, secondary_window->mailbox, false);
|
||||
secondary_window->PollEvents();
|
||||
}
|
||||
#endif
|
||||
if (frame_dumper.IsDumping()) {
|
||||
try {
|
||||
@ -553,7 +562,7 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float x, fl
|
||||
state.texture_units[0].texture_2d = 0;
|
||||
state.texture_units[0].sampler = 0;
|
||||
state.Apply();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD
|
||||
|
@ -829,6 +829,16 @@ void RendererVulkan::SwapBuffers() {
|
||||
RenderToWindow(*second_window, secondary_layout, false);
|
||||
secondary_window->PollEvents();
|
||||
}
|
||||
#endif
|
||||
#ifdef ANDROID
|
||||
if (secondary_window) {
|
||||
const auto &secondary_layout = secondary_window->GetFramebufferLayout();
|
||||
if (!second_window) {
|
||||
second_window = std::make_unique<PresentWindow>(*secondary_window, instance, scheduler);
|
||||
}
|
||||
RenderToWindow(*second_window, secondary_layout, false);
|
||||
secondary_window->PollEvents();
|
||||
}
|
||||
#endif
|
||||
rasterizer.TickFrame();
|
||||
EndFrame();
|
||||
|
Loading…
x
Reference in New Issue
Block a user