Android: It builds again, but needs a lot more work
This commit is contained in:
parent
411fbe8416
commit
b418f880ab
|
@ -1,6 +1,4 @@
|
||||||
set(SRCS
|
set(SRCS
|
||||||
android_audio_stream.cpp
|
|
||||||
android_audio_stream.h
|
|
||||||
android_host_interface.cpp
|
android_host_interface.cpp
|
||||||
android_host_interface.h
|
android_host_interface.h
|
||||||
android_gles_host_display.cpp
|
android_gles_host_display.cpp
|
||||||
|
@ -9,4 +7,4 @@ set(SRCS
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(duckstation-native SHARED ${SRCS})
|
add_library(duckstation-native SHARED ${SRCS})
|
||||||
target_link_libraries(duckstation-native PRIVATE android core common glad imgui EGL::EGL)
|
target_link_libraries(duckstation-native PRIVATE android frontend-common core common glad imgui EGL::EGL)
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
#include "android_audio_stream.h"
|
|
||||||
#include "YBaseLib/Assert.h"
|
|
||||||
#include "YBaseLib/Log.h"
|
|
||||||
Log_SetChannel(AndroidAudioStream);
|
|
||||||
|
|
||||||
AndroidAudioStream::AndroidAudioStream() = default;
|
|
||||||
|
|
||||||
AndroidAudioStream::~AndroidAudioStream()
|
|
||||||
{
|
|
||||||
if (m_is_open)
|
|
||||||
AndroidAudioStream::CloseDevice();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<AudioStream> AndroidAudioStream::Create()
|
|
||||||
{
|
|
||||||
return std::make_unique<AndroidAudioStream>();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AndroidAudioStream::OpenDevice()
|
|
||||||
{
|
|
||||||
DebugAssert(!m_is_open);
|
|
||||||
#if 0
|
|
||||||
SDL_AudioSpec spec = {};
|
|
||||||
spec.freq = m_output_sample_rate;
|
|
||||||
spec.channels = static_cast<Uint8>(m_channels);
|
|
||||||
spec.format = AUDIO_S16;
|
|
||||||
spec.samples = static_cast<Uint16>(m_buffer_size);
|
|
||||||
spec.callback = AudioCallback;
|
|
||||||
spec.userdata = static_cast<void*>(this);
|
|
||||||
|
|
||||||
SDL_AudioSpec obtained = {};
|
|
||||||
if (SDL_OpenAudio(&spec, &obtained) < 0)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("SDL_OpenAudio failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_is_open = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidAudioStream::PauseDevice(bool paused)
|
|
||||||
{
|
|
||||||
// SDL_PauseAudio(paused ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidAudioStream::CloseDevice()
|
|
||||||
{
|
|
||||||
DebugAssert(m_is_open);
|
|
||||||
// SDL_CloseAudio();
|
|
||||||
m_is_open = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
void AndroidAudioStream::AudioCallback(void* userdata, uint8_t* stream, int len)
|
|
||||||
{
|
|
||||||
AndroidAudioStream* const this_ptr = static_cast<AndroidAudioStream*>(userdata);
|
|
||||||
const u32 num_samples = len / sizeof(SampleType) / this_ptr->m_channels;
|
|
||||||
const u32 read_samples = this_ptr->ReadSamples(reinterpret_cast<SampleType*>(stream), num_samples);
|
|
||||||
const u32 silence_samples = num_samples - read_samples;
|
|
||||||
if (silence_samples > 0)
|
|
||||||
{
|
|
||||||
std::memset(reinterpret_cast<SampleType*>(stream) + (read_samples * this_ptr->m_channels), 0,
|
|
||||||
silence_samples * this_ptr->m_channels * sizeof(SampleType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,21 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include "common/audio_stream.h"
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
class AndroidAudioStream final : public AudioStream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AndroidAudioStream();
|
|
||||||
~AndroidAudioStream();
|
|
||||||
|
|
||||||
static std::unique_ptr<AudioStream> Create();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool OpenDevice() override;
|
|
||||||
void PauseDevice(bool paused) override;
|
|
||||||
void CloseDevice() override;
|
|
||||||
|
|
||||||
// static void AudioCallback(void* userdata, uint8_t* stream, int len);
|
|
||||||
|
|
||||||
bool m_is_open = false;
|
|
||||||
};
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "android_gles_host_display.h"
|
#include "android_gles_host_display.h"
|
||||||
#include "YBaseLib/Log.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/log.h"
|
||||||
#include <EGL/eglext.h>
|
#include <EGL/eglext.h>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
@ -20,7 +21,7 @@ public:
|
||||||
GLuint GetGLID() const { return m_id; }
|
GLuint GetGLID() const { return m_id; }
|
||||||
|
|
||||||
static std::unique_ptr<AndroidGLESHostDisplayTexture> Create(u32 width, u32 height, const void* initial_data,
|
static std::unique_ptr<AndroidGLESHostDisplayTexture> Create(u32 width, u32 height, const void* initial_data,
|
||||||
u32 initial_data_stride)
|
u32 initial_data_stride)
|
||||||
{
|
{
|
||||||
GLuint id;
|
GLuint id;
|
||||||
glGenTextures(1, &id);
|
glGenTextures(1, &id);
|
||||||
|
@ -102,13 +103,13 @@ void AndroidGLESHostDisplay::ChangeRenderWindow(void* new_window)
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<HostDisplayTexture> AndroidGLESHostDisplay::CreateTexture(u32 width, u32 height, const void* data,
|
std::unique_ptr<HostDisplayTexture> AndroidGLESHostDisplay::CreateTexture(u32 width, u32 height, const void* data,
|
||||||
u32 data_stride, bool dynamic)
|
u32 data_stride, bool dynamic)
|
||||||
{
|
{
|
||||||
return AndroidGLESHostDisplayTexture::Create(width, height, data, data_stride);
|
return AndroidGLESHostDisplayTexture::Create(width, height, data, data_stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidGLESHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
|
void AndroidGLESHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
|
||||||
const void* data, u32 data_stride)
|
const void* data, u32 data_stride)
|
||||||
{
|
{
|
||||||
AndroidGLESHostDisplayTexture* tex = static_cast<AndroidGLESHostDisplayTexture*>(texture);
|
AndroidGLESHostDisplayTexture* tex = static_cast<AndroidGLESHostDisplayTexture*>(texture);
|
||||||
Assert(data_stride == (width * sizeof(u32)));
|
Assert(data_stride == (width * sizeof(u32)));
|
||||||
|
@ -122,28 +123,22 @@ void AndroidGLESHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u
|
||||||
glBindTexture(GL_TEXTURE_2D, old_texture_binding);
|
glBindTexture(GL_TEXTURE_2D, old_texture_binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidGLESHostDisplay::SetDisplayTexture(void* texture, s32 offset_x, s32 offset_y, s32 width, s32 height,
|
bool AndroidGLESHostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height,
|
||||||
u32 texture_width, u32 texture_height, float aspect_ratio)
|
void* out_data, u32 out_data_stride)
|
||||||
{
|
{
|
||||||
m_display_texture_id = static_cast<GLuint>(reinterpret_cast<uintptr_t>(texture));
|
GLint old_alignment = 0, old_row_length = 0;
|
||||||
m_display_offset_x = offset_x;
|
glGetIntegerv(GL_PACK_ALIGNMENT, &old_alignment);
|
||||||
m_display_offset_y = offset_y;
|
glGetIntegerv(GL_PACK_ROW_LENGTH, &old_row_length);
|
||||||
m_display_width = width;
|
glPixelStorei(GL_PACK_ALIGNMENT, sizeof(u32));
|
||||||
m_display_height = height;
|
glPixelStorei(GL_PACK_ROW_LENGTH, out_data_stride / sizeof(u32));
|
||||||
m_display_texture_width = texture_width;
|
|
||||||
m_display_texture_height = texture_height;
|
|
||||||
m_display_aspect_ratio = aspect_ratio;
|
|
||||||
m_display_texture_changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidGLESHostDisplay::SetDisplayLinearFiltering(bool enabled)
|
const GLuint texture = static_cast<GLuint>(reinterpret_cast<uintptr_t>(texture_handle));
|
||||||
{
|
GL::Texture::GetTextureSubImage(texture, 0, x, y, 0, width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||||
m_display_linear_filtering = enabled;
|
height * out_data_stride, out_data);
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidGLESHostDisplay::SetDisplayTopMargin(int height)
|
glPixelStorei(GL_PACK_ALIGNMENT, old_alignment);
|
||||||
{
|
glPixelStorei(GL_PACK_ROW_LENGTH, old_row_length);
|
||||||
m_display_top_margin = height;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidGLESHostDisplay::SetVSync(bool enabled)
|
void AndroidGLESHostDisplay::SetVSync(bool enabled)
|
||||||
|
@ -151,13 +146,9 @@ void AndroidGLESHostDisplay::SetVSync(bool enabled)
|
||||||
eglSwapInterval(m_egl_display, enabled ? 1 : 0);
|
eglSwapInterval(m_egl_display, enabled ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<u32, u32> AndroidGLESHostDisplay::GetWindowSize() const
|
void AndroidGLESHostDisplay::WindowResized(s32 new_window_width, s32 new_window_height)
|
||||||
{
|
|
||||||
return std::make_tuple(static_cast<u32>(m_window_width), static_cast<u32>(m_window_height));
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidGLESHostDisplay::WindowResized()
|
|
||||||
{
|
{
|
||||||
|
HostDisplay::WindowResized(new_window_width, new_window_height);
|
||||||
m_window_width = ANativeWindow_getWidth(m_window);
|
m_window_width = ANativeWindow_getWidth(m_window);
|
||||||
m_window_height = ANativeWindow_getHeight(m_window);
|
m_window_height = ANativeWindow_getHeight(m_window);
|
||||||
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_width);
|
ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_width);
|
||||||
|
@ -212,10 +203,10 @@ bool AndroidGLESHostDisplay::CreateGLContext()
|
||||||
eglBindAPI(EGL_OPENGL_ES_API);
|
eglBindAPI(EGL_OPENGL_ES_API);
|
||||||
|
|
||||||
// Try GLES 3, then fall back to GLES 2.
|
// Try GLES 3, then fall back to GLES 2.
|
||||||
for (int major_version : {3, 2}) {
|
for (int major_version : {3, 2})
|
||||||
|
{
|
||||||
std::array<int, 3> egl_context_attribs = {{EGL_CONTEXT_CLIENT_VERSION, major_version, EGL_NONE}};
|
std::array<int, 3> egl_context_attribs = {{EGL_CONTEXT_CLIENT_VERSION, major_version, EGL_NONE}};
|
||||||
m_egl_context = eglCreateContext(m_egl_display, m_egl_config, EGL_NO_CONTEXT,
|
m_egl_context = eglCreateContext(m_egl_display, m_egl_config, EGL_NO_CONTEXT, egl_context_attribs.data());
|
||||||
egl_context_attribs.data());
|
|
||||||
if (m_egl_context)
|
if (m_egl_context)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -262,7 +253,7 @@ bool AndroidGLESHostDisplay::CreateSurface()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowResized();
|
WindowResized(m_window_width, m_window_height);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,10 +342,12 @@ void AndroidGLESHostDisplay::Render()
|
||||||
|
|
||||||
RenderDisplay();
|
RenderDisplay();
|
||||||
|
|
||||||
|
ImGui::Render();
|
||||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
|
||||||
eglSwapBuffers(m_egl_display, m_egl_surface);
|
eglSwapBuffers(m_egl_display, m_egl_surface);
|
||||||
|
|
||||||
|
ImGui::NewFrame();
|
||||||
ImGui_ImplOpenGL3_NewFrame();
|
ImGui_ImplOpenGL3_NewFrame();
|
||||||
|
|
||||||
GL::Program::ResetLastProgram();
|
GL::Program::ResetLastProgram();
|
||||||
|
@ -362,14 +355,13 @@ void AndroidGLESHostDisplay::Render()
|
||||||
|
|
||||||
void AndroidGLESHostDisplay::RenderDisplay()
|
void AndroidGLESHostDisplay::RenderDisplay()
|
||||||
{
|
{
|
||||||
if (!m_display_texture_id)
|
if (!m_display_texture_handle)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// - 20 for main menu padding
|
const auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect();
|
||||||
const auto [vp_left, vp_top, vp_width, vp_height] =
|
|
||||||
CalculateDrawRect(m_window_width, std::max(m_window_height - m_display_top_margin, 1), m_display_aspect_ratio);
|
glViewport(vp_left, m_window_height - vp_top - vp_height, vp_width, vp_height);
|
||||||
|
|
||||||
glViewport(vp_left, m_window_height - (m_display_top_margin + vp_top) - vp_height, vp_width, vp_height);
|
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
glDisable(GL_CULL_FACE);
|
glDisable(GL_CULL_FACE);
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
@ -377,11 +369,14 @@ void AndroidGLESHostDisplay::RenderDisplay()
|
||||||
glDepthMask(GL_FALSE);
|
glDepthMask(GL_FALSE);
|
||||||
m_display_program.Bind();
|
m_display_program.Bind();
|
||||||
|
|
||||||
const float tex_left = static_cast<float>(m_display_offset_x) / static_cast<float>(m_display_texture_width);
|
const float tex_left =
|
||||||
const float tex_right = tex_left + static_cast<float>(m_display_width) / static_cast<float>(m_display_texture_width);
|
(static_cast<float>(m_display_texture_view_x) + 0.25f) / static_cast<float>(m_display_texture_width);
|
||||||
const float tex_top = static_cast<float>(m_display_offset_y) / static_cast<float>(m_display_texture_height);
|
const float tex_top =
|
||||||
|
(static_cast<float>(m_display_texture_view_y) - 0.25f) / static_cast<float>(m_display_texture_height);
|
||||||
|
const float tex_right =
|
||||||
|
(tex_left + static_cast<float>(m_display_texture_view_width) - 0.5f) / static_cast<float>(m_display_texture_width);
|
||||||
const float tex_bottom =
|
const float tex_bottom =
|
||||||
tex_top + static_cast<float>(m_display_height) / static_cast<float>(m_display_texture_height);
|
(tex_top + static_cast<float>(m_display_texture_view_height) + 0.5f) / static_cast<float>(m_display_texture_height);
|
||||||
const std::array<std::array<float, 4>, 4> vertices = {{
|
const std::array<std::array<float, 4>, 4> vertices = {{
|
||||||
{{-1.0f, -1.0f, tex_left, tex_bottom}}, // bottom-left
|
{{-1.0f, -1.0f, tex_left, tex_bottom}}, // bottom-left
|
||||||
{{1.0f, -1.0f, tex_right, tex_bottom}}, // bottom-right
|
{{1.0f, -1.0f, tex_right, tex_bottom}}, // bottom-right
|
||||||
|
@ -395,7 +390,7 @@ void AndroidGLESHostDisplay::RenderDisplay()
|
||||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertices[0]), &vertices[0][2]);
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertices[0]), &vertices[0][2]);
|
||||||
glEnableVertexAttribArray(1);
|
glEnableVertexAttribArray(1);
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, m_display_texture_id);
|
glBindTexture(GL_TEXTURE_2D, static_cast<GLuint>(reinterpret_cast<uintptr_t>(m_display_texture_handle)));
|
||||||
|
|
||||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
|
|
||||||
|
|
|
@ -22,24 +22,19 @@ public:
|
||||||
void* GetRenderWindow() const override;
|
void* GetRenderWindow() const override;
|
||||||
|
|
||||||
void ChangeRenderWindow(void* new_window) override;
|
void ChangeRenderWindow(void* new_window) override;
|
||||||
|
void WindowResized(s32 new_window_width, s32 new_window_height) override;
|
||||||
|
|
||||||
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride,
|
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride,
|
||||||
bool dynamic) override;
|
bool dynamic) override;
|
||||||
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data,
|
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data,
|
||||||
u32 data_stride) override;
|
u32 data_stride) override;
|
||||||
|
bool DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, void* out_data,
|
||||||
void SetDisplayTexture(void* texture, s32 offset_x, s32 offset_y, s32 width, s32 height, u32 texture_width,
|
u32 out_data_stride) override;
|
||||||
u32 texture_height, float aspect_ratio) override;
|
|
||||||
void SetDisplayLinearFiltering(bool enabled) override;
|
|
||||||
void SetDisplayTopMargin(int height) override;
|
|
||||||
|
|
||||||
void SetVSync(bool enabled) override;
|
void SetVSync(bool enabled) override;
|
||||||
|
|
||||||
void Render() override;
|
void Render() override;
|
||||||
|
|
||||||
std::tuple<u32, u32> GetWindowSize() const override;
|
|
||||||
void WindowResized() override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const char* GetGLSLVersionString() const;
|
const char* GetGLSLVersionString() const;
|
||||||
std::string GetGLSLVersionHeader() const;
|
std::string GetGLSLVersionHeader() const;
|
||||||
|
@ -63,16 +58,4 @@ private:
|
||||||
EGLConfig m_egl_config = {};
|
EGLConfig m_egl_config = {};
|
||||||
|
|
||||||
GL::Program m_display_program;
|
GL::Program m_display_program;
|
||||||
GLuint m_display_texture_id = 0;
|
|
||||||
s32 m_display_offset_x = 0;
|
|
||||||
s32 m_display_offset_y = 0;
|
|
||||||
s32 m_display_width = 0;
|
|
||||||
s32 m_display_height = 0;
|
|
||||||
u32 m_display_texture_width = 0;
|
|
||||||
u32 m_display_texture_height = 0;
|
|
||||||
int m_display_top_margin = 0;
|
|
||||||
float m_display_aspect_ratio = 1.0f;
|
|
||||||
|
|
||||||
bool m_display_texture_changed = false;
|
|
||||||
bool m_display_linear_filtering = false;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
#include "android_host_interface.h"
|
#include "android_host_interface.h"
|
||||||
#include "YBaseLib/Assert.h"
|
|
||||||
#include "YBaseLib/Log.h"
|
|
||||||
#include "YBaseLib/String.h"
|
|
||||||
#include "android_audio_stream.h"
|
|
||||||
#include "android_gles_host_display.h"
|
#include "android_gles_host_display.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
#include "common/string.h"
|
||||||
|
#include "common/audio_stream.h"
|
||||||
#include "core/controller.h"
|
#include "core/controller.h"
|
||||||
#include "core/gpu.h"
|
|
||||||
#include "core/game_list.h"
|
#include "core/game_list.h"
|
||||||
|
#include "core/gpu.h"
|
||||||
#include "core/host_display.h"
|
#include "core/host_display.h"
|
||||||
#include "core/system.h"
|
#include "core/system.h"
|
||||||
|
#include "frontend-common/ini_settings_interface.h"
|
||||||
#include <android/native_window_jni.h>
|
#include <android/native_window_jni.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
@ -53,20 +54,7 @@ static std::string JStringToString(JNIEnv* env, jstring str)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
AndroidHostInterface::AndroidHostInterface(jobject java_object) : m_java_object(java_object)
|
AndroidHostInterface::AndroidHostInterface(jobject java_object) : m_java_object(java_object) {}
|
||||||
{
|
|
||||||
m_settings.SetDefaults();
|
|
||||||
m_settings.bios_path = "/sdcard/PSX/BIOS/scph1001.bin";
|
|
||||||
m_settings.controller_types[0] = ControllerType::DigitalController;
|
|
||||||
m_settings.memory_card_paths[0] = "/sdcard/PSX/memory_card_1.mcd";
|
|
||||||
m_settings.cpu_execution_mode = CPUExecutionMode::Recompiler;
|
|
||||||
//m_settings.cpu_execution_mode = CPUExecutionMode::CachedInterpreter;
|
|
||||||
//m_settings.gpu_renderer = GPURenderer::Software;
|
|
||||||
m_settings.speed_limiter_enabled = false;
|
|
||||||
m_settings.video_sync_enabled = false;
|
|
||||||
m_settings.audio_sync_enabled = false;
|
|
||||||
//m_settings.debugging.show_vram = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
AndroidHostInterface::~AndroidHostInterface()
|
AndroidHostInterface::~AndroidHostInterface()
|
||||||
{
|
{
|
||||||
|
@ -74,6 +62,24 @@ AndroidHostInterface::~AndroidHostInterface()
|
||||||
GetJNIEnv()->DeleteGlobalRef(m_java_object);
|
GetJNIEnv()->DeleteGlobalRef(m_java_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AndroidHostInterface::Initialize()
|
||||||
|
{
|
||||||
|
if (!HostInterface::Initialize())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check settings version, if invalid set defaults, then load settings
|
||||||
|
INISettingsInterface settings_interface(GetSettingsFileName());
|
||||||
|
CheckSettings(settings_interface);
|
||||||
|
UpdateSettings([this, &settings_interface]() { m_settings.Load(settings_interface); });
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidHostInterface::Shutdown()
|
||||||
|
{
|
||||||
|
HostInterface::Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
void AndroidHostInterface::ReportError(const char* message)
|
void AndroidHostInterface::ReportError(const char* message)
|
||||||
{
|
{
|
||||||
HostInterface::ReportError(message);
|
HostInterface::ReportError(message);
|
||||||
|
@ -84,15 +90,20 @@ void AndroidHostInterface::ReportMessage(const char* message)
|
||||||
HostInterface::ReportMessage(message);
|
HostInterface::ReportMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AndroidHostInterface::StartEmulationThread(ANativeWindow* initial_surface, std::string initial_filename,
|
void AndroidHostInterface::SetUserDirectory()
|
||||||
std::string initial_state_filename)
|
{
|
||||||
|
// TODO: Should this be customizable or use an API-determined path?
|
||||||
|
m_user_directory = "/sdcard/duckstation";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidHostInterface::StartEmulationThread(ANativeWindow* initial_surface, SystemBootParameters boot_params)
|
||||||
{
|
{
|
||||||
Assert(!IsEmulationThreadRunning());
|
Assert(!IsEmulationThreadRunning());
|
||||||
|
|
||||||
Log_DevPrintf("Starting emulation thread...");
|
Log_DevPrintf("Starting emulation thread...");
|
||||||
m_emulation_thread_stop_request.store(false);
|
m_emulation_thread_stop_request.store(false);
|
||||||
m_emulation_thread = std::thread(&AndroidHostInterface::EmulationThreadEntryPoint, this, initial_surface,
|
m_emulation_thread = std::thread(&AndroidHostInterface::EmulationThreadEntryPoint, this, initial_surface,
|
||||||
std::move(initial_filename), std::move(initial_state_filename));
|
std::move(boot_params));
|
||||||
m_emulation_thread_started.Wait();
|
m_emulation_thread_started.Wait();
|
||||||
if (!m_emulation_thread_start_result.load())
|
if (!m_emulation_thread_start_result.load())
|
||||||
{
|
{
|
||||||
|
@ -140,43 +151,14 @@ void AndroidHostInterface::RunOnEmulationThread(std::function<void()> function,
|
||||||
m_callback_mutex.unlock();
|
m_callback_mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidHostInterface::EmulationThreadEntryPoint(ANativeWindow* initial_surface, std::string initial_filename,
|
void AndroidHostInterface::EmulationThreadEntryPoint(ANativeWindow* initial_surface, SystemBootParameters boot_params)
|
||||||
std::string initial_state_filename)
|
|
||||||
{
|
{
|
||||||
CreateImGuiContext();
|
CreateImGuiContext();
|
||||||
|
|
||||||
// Create display.
|
|
||||||
m_display = AndroidGLESHostDisplay::Create(initial_surface);
|
|
||||||
if (!m_display)
|
|
||||||
{
|
|
||||||
Log_ErrorPrint("Failed to create display on emulation thread.");
|
|
||||||
DestroyImGuiContext();
|
|
||||||
m_emulation_thread_start_result.store(false);
|
|
||||||
m_emulation_thread_started.Signal();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create audio stream.
|
|
||||||
m_audio_stream = AndroidAudioStream::Create();
|
|
||||||
if (!m_audio_stream || !m_audio_stream->Reconfigure(44100, 2))
|
|
||||||
{
|
|
||||||
Log_ErrorPrint("Failed to create audio stream on emulation thread.");
|
|
||||||
m_audio_stream.reset();
|
|
||||||
m_display.reset();
|
|
||||||
DestroyImGuiContext();
|
|
||||||
m_emulation_thread_start_result.store(false);
|
|
||||||
m_emulation_thread_started.Signal();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Boot system.
|
// Boot system.
|
||||||
if (!CreateSystem() || !BootSystem(initial_filename.empty() ? nullptr : initial_filename.c_str(),
|
if (!BootSystem(boot_params))
|
||||||
initial_state_filename.empty() ? nullptr : initial_state_filename.c_str()))
|
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Failed to boot system on emulation thread (file:%s state:%s).", initial_filename.c_str(),
|
Log_ErrorPrintf("Failed to boot system on emulation thread (file:%s).", boot_params.filename.c_str());
|
||||||
initial_state_filename.c_str());
|
|
||||||
m_audio_stream.reset();
|
|
||||||
m_display.reset();
|
|
||||||
DestroyImGuiContext();
|
DestroyImGuiContext();
|
||||||
m_emulation_thread_start_result.store(false);
|
m_emulation_thread_start_result.store(false);
|
||||||
m_emulation_thread_started.Signal();
|
m_emulation_thread_started.Signal();
|
||||||
|
@ -212,33 +194,88 @@ void AndroidHostInterface::EmulationThreadEntryPoint(ANativeWindow* initial_surf
|
||||||
|
|
||||||
// rendering
|
// rendering
|
||||||
{
|
{
|
||||||
DrawImGui();
|
DrawImGuiWindows();
|
||||||
|
|
||||||
if (m_system)
|
if (m_system)
|
||||||
m_system->GetGPU()->ResetGraphicsAPIState();
|
m_system->GetGPU()->ResetGraphicsAPIState();
|
||||||
|
|
||||||
ImGui::Render();
|
|
||||||
m_display->Render();
|
m_display->Render();
|
||||||
|
|
||||||
ImGui::NewFrame();
|
|
||||||
|
|
||||||
if (m_system)
|
if (m_system)
|
||||||
{
|
{
|
||||||
m_system->GetGPU()->RestoreGraphicsAPIState();
|
m_system->GetGPU()->RestoreGraphicsAPIState();
|
||||||
|
m_system->UpdatePerformanceCounters();
|
||||||
|
|
||||||
if (m_speed_limiter_enabled)
|
if (m_speed_limiter_enabled)
|
||||||
Throttle();
|
m_system->Throttle();
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdatePerformanceCounters();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_display.reset();
|
DestroySystem();
|
||||||
m_audio_stream.reset();
|
|
||||||
DestroyImGuiContext();
|
DestroyImGuiContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AndroidHostInterface::AcquireHostDisplay()
|
||||||
|
{
|
||||||
|
std::unique_ptr<HostDisplay> display = AndroidGLESHostDisplay::Create(m_surface);
|
||||||
|
if (!display)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to create GLES host display");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_display = display.release();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidHostInterface::ReleaseHostDisplay()
|
||||||
|
{
|
||||||
|
delete m_display;
|
||||||
|
m_display = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<AudioStream> AndroidHostInterface::CreateAudioStream(AudioBackend backend)
|
||||||
|
{
|
||||||
|
std::unique_ptr<AudioStream> stream;
|
||||||
|
|
||||||
|
switch (m_settings.audio_backend)
|
||||||
|
{
|
||||||
|
case AudioBackend::Cubeb:
|
||||||
|
stream = AudioStream::CreateCubebAudioStream();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
stream = AudioStream::CreateNullAudioStream();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stream)
|
||||||
|
{
|
||||||
|
ReportFormattedError("Failed to create %s audio stream, falling back to null",
|
||||||
|
Settings::GetAudioBackendName(m_settings.audio_backend));
|
||||||
|
stream = AudioStream::CreateNullAudioStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidHostInterface::SurfaceChanged(ANativeWindow* surface, int format, int width, int height)
|
||||||
|
{
|
||||||
|
Log_InfoPrintf("SurfaceChanged %p %d %d %d", surface, format, width, height);
|
||||||
|
if (m_surface == surface)
|
||||||
|
{
|
||||||
|
if (m_display)
|
||||||
|
m_display->WindowResized(width, height);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_surface = surface;
|
||||||
|
if (m_display)
|
||||||
|
m_display->ChangeRenderWindow(surface);
|
||||||
|
}
|
||||||
|
|
||||||
void AndroidHostInterface::CreateImGuiContext()
|
void AndroidHostInterface::CreateImGuiContext()
|
||||||
{
|
{
|
||||||
ImGui::CreateContext();
|
ImGui::CreateContext();
|
||||||
|
@ -253,88 +290,10 @@ void AndroidHostInterface::DestroyImGuiContext()
|
||||||
ImGui::DestroyContext();
|
ImGui::DestroyContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidHostInterface::DrawImGui()
|
|
||||||
{
|
|
||||||
DrawFPSWindow();
|
|
||||||
DrawOSDMessages();
|
|
||||||
|
|
||||||
ImGui::Render();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidHostInterface::DrawFPSWindow()
|
|
||||||
{
|
|
||||||
const bool show_fps = true;
|
|
||||||
const bool show_vps = true;
|
|
||||||
const bool show_speed = true;
|
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x - 175.0f, 0.0f), ImGuiCond_Always);
|
|
||||||
ImGui::SetNextWindowSize(ImVec2(175.0f, 16.0f));
|
|
||||||
|
|
||||||
if (!ImGui::Begin("FPSWindow", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse |
|
|
||||||
ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoResize |
|
|
||||||
ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoBringToFrontOnFocus))
|
|
||||||
{
|
|
||||||
ImGui::End();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool first = true;
|
|
||||||
if (show_fps)
|
|
||||||
{
|
|
||||||
ImGui::Text("%.2f", m_fps);
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
if (show_vps)
|
|
||||||
{
|
|
||||||
if (first) {
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::Text("/");
|
|
||||||
ImGui::SameLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Text("%.2f", m_vps);
|
|
||||||
}
|
|
||||||
if (show_speed)
|
|
||||||
{
|
|
||||||
if (first) {
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::Text("/");
|
|
||||||
ImGui::SameLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 rounded_speed = static_cast<u32>(std::round(m_speed));
|
|
||||||
if (m_speed < 90.0f)
|
|
||||||
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%u%%", rounded_speed);
|
|
||||||
else if (m_speed < 110.0f)
|
|
||||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.0f), "%u%%", rounded_speed);
|
|
||||||
else
|
|
||||||
ImGui::TextColored(ImVec4(0.4f, 1.0f, 0.4f, 1.0f), "%u%%", rounded_speed);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidHostInterface::SurfaceChanged(ANativeWindow* window, int format, int width, int height)
|
|
||||||
{
|
|
||||||
Log_InfoPrintf("SurfaceChanged %p %d %d %d", window, format, width, height);
|
|
||||||
if (m_display->GetRenderWindow() == window)
|
|
||||||
{
|
|
||||||
m_display->WindowResized();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_display->ChangeRenderWindow(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidHostInterface::SetControllerType(u32 index, std::string_view type_name)
|
void AndroidHostInterface::SetControllerType(u32 index, std::string_view type_name)
|
||||||
{
|
{
|
||||||
ControllerType type = Settings::ParseControllerTypeName(std::string(type_name).c_str()).value_or(ControllerType::None);
|
ControllerType type =
|
||||||
|
Settings::ParseControllerTypeName(std::string(type_name).c_str()).value_or(ControllerType::None);
|
||||||
|
|
||||||
if (!IsEmulationThreadRunning())
|
if (!IsEmulationThreadRunning())
|
||||||
{
|
{
|
||||||
|
@ -342,11 +301,13 @@ void AndroidHostInterface::SetControllerType(u32 index, std::string_view type_na
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RunOnEmulationThread([this, index, type]() {
|
RunOnEmulationThread(
|
||||||
Log_InfoPrintf("Changing controller slot %d to %s", index, Settings::GetControllerTypeName(type));
|
[this, index, type]() {
|
||||||
m_settings.controller_types[index] = type;
|
Log_InfoPrintf("Changing controller slot %d to %s", index, Settings::GetControllerTypeName(type));
|
||||||
m_system->UpdateControllers();
|
m_settings.controller_types[index] = type;
|
||||||
}, false);
|
m_system->UpdateControllers();
|
||||||
|
},
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidHostInterface::SetControllerButtonState(u32 index, s32 button_code, bool pressed)
|
void AndroidHostInterface::SetControllerButtonState(u32 index, s32 button_code, bool pressed)
|
||||||
|
@ -354,18 +315,20 @@ void AndroidHostInterface::SetControllerButtonState(u32 index, s32 button_code,
|
||||||
if (!IsEmulationThreadRunning())
|
if (!IsEmulationThreadRunning())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
RunOnEmulationThread([this, index, button_code, pressed]() {
|
RunOnEmulationThread(
|
||||||
Controller* controller = m_system->GetController(index);
|
[this, index, button_code, pressed]() {
|
||||||
if (!controller)
|
Controller* controller = m_system->GetController(index);
|
||||||
return;
|
if (!controller)
|
||||||
|
return;
|
||||||
|
|
||||||
controller->SetButtonState(button_code, pressed);
|
controller->SetButtonState(button_code, pressed);
|
||||||
}, false);
|
},
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||||
{
|
{
|
||||||
Log::GetInstance().SetDebugOutputParams(true, nullptr, LOGLEVEL_DEV);
|
Log::SetDebugOutputParams(true, nullptr, LOGLEVEL_DEV);
|
||||||
s_jvm = vm;
|
s_jvm = vm;
|
||||||
|
|
||||||
JNIEnv* env = GetJNIEnv();
|
JNIEnv* env = GetJNIEnv();
|
||||||
|
@ -445,8 +408,12 @@ DEFINE_JNI_ARGS_METHOD(jboolean, AndroidHostInterface_startEmulationThread, jobj
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetNativeClass(env, obj)->StartEmulationThread(native_surface, JStringToString(env, filename),
|
std::string state_filename_str = JStringToString(env, state_filename);
|
||||||
JStringToString(env, state_filename));
|
|
||||||
|
SystemBootParameters boot_params;
|
||||||
|
boot_params.filename = JStringToString(env, filename);
|
||||||
|
|
||||||
|
return GetNativeClass(env, obj)->StartEmulationThread(native_surface, std::move(boot_params));
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_stopEmulationThread, jobject obj)
|
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_stopEmulationThread, jobject obj)
|
||||||
|
@ -471,12 +438,14 @@ DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_setControllerType, jobject obj
|
||||||
GetNativeClass(env, obj)->SetControllerType(index, JStringToString(env, controller_type));
|
GetNativeClass(env, obj)->SetControllerType(index, JStringToString(env, controller_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_setControllerButtonState, jobject obj, jint index, jint button_code, jboolean pressed)
|
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_setControllerButtonState, jobject obj, jint index, jint button_code,
|
||||||
|
jboolean pressed)
|
||||||
{
|
{
|
||||||
GetNativeClass(env, obj)->SetControllerButtonState(index, button_code, pressed);
|
GetNativeClass(env, obj)->SetControllerButtonState(index, button_code, pressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_JNI_ARGS_METHOD(jint, AndroidHostInterface_getControllerButtonCode, jobject unused, jstring controller_type, jstring button_name)
|
DEFINE_JNI_ARGS_METHOD(jint, AndroidHostInterface_getControllerButtonCode, jobject unused, jstring controller_type,
|
||||||
|
jstring button_name)
|
||||||
{
|
{
|
||||||
std::optional<ControllerType> type = Settings::ParseControllerTypeName(JStringToString(env, controller_type).c_str());
|
std::optional<ControllerType> type = Settings::ParseControllerTypeName(JStringToString(env, controller_type).c_str());
|
||||||
if (!type)
|
if (!type)
|
||||||
|
@ -486,14 +455,16 @@ DEFINE_JNI_ARGS_METHOD(jint, AndroidHostInterface_getControllerButtonCode, jobje
|
||||||
return code.value_or(-1);
|
return code.value_or(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_JNI_ARGS_METHOD(jarray, GameList_getEntries, jobject unused, jstring j_cache_path, jstring j_redump_dat_path, jarray j_search_directories, jboolean search_recursively)
|
DEFINE_JNI_ARGS_METHOD(jarray, GameList_getEntries, jobject unused, jstring j_cache_path, jstring j_redump_dat_path,
|
||||||
|
jarray j_search_directories, jboolean search_recursively)
|
||||||
{
|
{
|
||||||
const std::string cache_path = JStringToString(env, j_cache_path);
|
//const std::string cache_path = JStringToString(env, j_cache_path);
|
||||||
const std::string redump_dat_path = JStringToString(env, j_redump_dat_path);
|
std::string redump_dat_path = JStringToString(env, j_redump_dat_path);
|
||||||
|
|
||||||
|
// TODO: This should use the base HostInterface.
|
||||||
GameList gl;
|
GameList gl;
|
||||||
if (!redump_dat_path.empty())
|
if (!redump_dat_path.empty())
|
||||||
gl.ParseRedumpDatabase(redump_dat_path.c_str());
|
gl.SetDatabaseFilename(std::move(redump_dat_path));
|
||||||
|
|
||||||
const jsize search_directories_size = env->GetArrayLength(j_search_directories);
|
const jsize search_directories_size = env->GetArrayLength(j_search_directories);
|
||||||
for (jsize i = 0; i < search_directories_size; i++)
|
for (jsize i = 0; i < search_directories_size; i++)
|
||||||
|
@ -507,19 +478,21 @@ DEFINE_JNI_ARGS_METHOD(jarray, GameList_getEntries, jobject unused, jstring j_ca
|
||||||
jclass entry_class = env->FindClass("com/github/stenzek/duckstation/GameListEntry");
|
jclass entry_class = env->FindClass("com/github/stenzek/duckstation/GameListEntry");
|
||||||
Assert(entry_class != nullptr);
|
Assert(entry_class != nullptr);
|
||||||
|
|
||||||
jmethodID entry_constructor = env->GetMethodID(entry_class, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V");
|
jmethodID entry_constructor =
|
||||||
|
env->GetMethodID(entry_class, "<init>",
|
||||||
|
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V");
|
||||||
Assert(entry_constructor != nullptr);
|
Assert(entry_constructor != nullptr);
|
||||||
|
|
||||||
jobjectArray entry_array = env->NewObjectArray(gl.GetEntryCount(), entry_class, nullptr);
|
jobjectArray entry_array = env->NewObjectArray(gl.GetEntryCount(), entry_class, nullptr);
|
||||||
Assert(entry_array != nullptr);
|
Assert(entry_array != nullptr);
|
||||||
|
|
||||||
u32 counter = 0;
|
u32 counter = 0;
|
||||||
for (const GameList::GameListEntry& entry : gl.GetEntries())
|
for (const GameListEntry& entry : gl.GetEntries())
|
||||||
{
|
{
|
||||||
jstring path = env->NewStringUTF(entry.path.c_str());
|
jstring path = env->NewStringUTF(entry.path.c_str());
|
||||||
jstring code = env->NewStringUTF(entry.code.c_str());
|
jstring code = env->NewStringUTF(entry.code.c_str());
|
||||||
jstring title = env->NewStringUTF(entry.title.c_str());
|
jstring title = env->NewStringUTF(entry.title.c_str());
|
||||||
jstring region = env->NewStringUTF(Settings::GetConsoleRegionName(entry.region));
|
jstring region = env->NewStringUTF(Settings::GetDiscRegionName(entry.region));
|
||||||
jstring type = env->NewStringUTF(GameList::EntryTypeToString(entry.type));
|
jstring type = env->NewStringUTF(GameList::EntryTypeToString(entry.type));
|
||||||
jlong size = entry.total_size;
|
jlong size = entry.total_size;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "YBaseLib/Event.h"
|
#include "common/event.h"
|
||||||
#include "YBaseLib/Timer.h"
|
|
||||||
#include "core/host_interface.h"
|
#include "core/host_interface.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
@ -19,42 +18,48 @@ public:
|
||||||
AndroidHostInterface(jobject java_object);
|
AndroidHostInterface(jobject java_object);
|
||||||
~AndroidHostInterface() override;
|
~AndroidHostInterface() override;
|
||||||
|
|
||||||
|
bool Initialize() override;
|
||||||
|
void Shutdown() override;
|
||||||
|
|
||||||
void ReportError(const char* message) override;
|
void ReportError(const char* message) override;
|
||||||
void ReportMessage(const char* message) override;
|
void ReportMessage(const char* message) override;
|
||||||
|
|
||||||
bool IsEmulationThreadRunning() const { return m_emulation_thread.joinable(); }
|
bool IsEmulationThreadRunning() const { return m_emulation_thread.joinable(); }
|
||||||
bool StartEmulationThread(ANativeWindow* initial_surface, std::string initial_filename,
|
bool StartEmulationThread(ANativeWindow* initial_surface, SystemBootParameters boot_params);
|
||||||
std::string initial_state_filename);
|
|
||||||
void RunOnEmulationThread(std::function<void()> function, bool blocking = false);
|
void RunOnEmulationThread(std::function<void()> function, bool blocking = false);
|
||||||
void StopEmulationThread();
|
void StopEmulationThread();
|
||||||
|
|
||||||
void SurfaceChanged(ANativeWindow* window, int format, int width, int height);
|
void SurfaceChanged(ANativeWindow* surface, int format, int width, int height);
|
||||||
|
|
||||||
void SetControllerType(u32 index, std::string_view type_name);
|
void SetControllerType(u32 index, std::string_view type_name);
|
||||||
void SetControllerButtonState(u32 index, s32 button_code, bool pressed);
|
void SetControllerButtonState(u32 index, s32 button_code, bool pressed);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void SetUserDirectory() override;
|
||||||
|
bool AcquireHostDisplay() override;
|
||||||
|
void ReleaseHostDisplay() override;
|
||||||
|
std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum : u32
|
enum : u32
|
||||||
{
|
{
|
||||||
NUM_CONTROLLERS = 2
|
NUM_CONTROLLERS = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
void EmulationThreadEntryPoint(ANativeWindow* initial_surface, std::string initial_filename,
|
void EmulationThreadEntryPoint(ANativeWindow* initial_surface, SystemBootParameters boot_params);
|
||||||
std::string initial_state_filename);
|
|
||||||
|
|
||||||
void CreateImGuiContext();
|
void CreateImGuiContext();
|
||||||
void DestroyImGuiContext();
|
void DestroyImGuiContext();
|
||||||
void DrawImGui();
|
|
||||||
|
|
||||||
void DrawFPSWindow();
|
|
||||||
|
|
||||||
jobject m_java_object = {};
|
jobject m_java_object = {};
|
||||||
|
|
||||||
|
ANativeWindow* m_surface = nullptr;
|
||||||
|
|
||||||
std::mutex m_callback_mutex;
|
std::mutex m_callback_mutex;
|
||||||
std::deque<std::function<void()>> m_callback_queue;
|
std::deque<std::function<void()>> m_callback_queue;
|
||||||
|
|
||||||
std::thread m_emulation_thread;
|
std::thread m_emulation_thread;
|
||||||
std::atomic_bool m_emulation_thread_stop_request{false};
|
std::atomic_bool m_emulation_thread_stop_request{false};
|
||||||
std::atomic_bool m_emulation_thread_start_result{false};
|
std::atomic_bool m_emulation_thread_start_result{false};
|
||||||
Event m_emulation_thread_started;
|
Common::Event m_emulation_thread_started;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue