diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8d4157062c..2b07111e86 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -730,10 +730,10 @@ if(ANDROID)
android/jni/AndroidVulkanContext.cpp
android/jni/AndroidVulkanContext.h
android/jni/AndroidGraphicsContext.h
- android/jni/native_audio.cpp
- android/jni/native_audio.h
- android/jni/native-audio-so.cpp
- android/jni/native-audio-so.h
+ android/jni/AndroidAudio.cpp
+ android/jni/AndroidAudio.h
+ android/jni/OpenSLContext.cpp
+ android/jni/OpenSLContext.h
)
# No target
elseif(IOS)
diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp
index bb59188e86..20bddb0d1f 100644
--- a/UI/NativeApp.cpp
+++ b/UI/NativeApp.cpp
@@ -255,8 +255,6 @@ std::string NativeQueryConfig(std::string query) {
int max_res = std::max(System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES)) / 480 + 1;
snprintf(temp, sizeof(temp), "%d", std::min(scale, max_res));
return std::string(temp);
- } else if (query == "force44khz") {
- return std::string("0");
} else if (query == "androidJavaGL") {
// If we're using Vulkan, we say no... need C++ to use Vulkan.
if (GetGPUBackend() == GPUBackend::VULKAN) {
@@ -805,29 +803,34 @@ static void UIThemeInit() {
}
void RenderOverlays(UIContext *dc, void *userdata);
+bool CreateGlobalPipelines();
bool NativeInitGraphics(GraphicsContext *graphicsContext) {
ILOG("NativeInitGraphics");
- _assert_msg_(graphicsContext, "No graphics context!");
// We set this now so any resize during init is processed later.
resized = false;
- using namespace Draw;
Core_SetGraphicsContext(graphicsContext);
g_draw = graphicsContext->GetDrawContext();
- _assert_msg_(g_draw, "No draw context available!");
- _assert_msg_(g_draw->GetVshaderPreset(VS_COLOR_2D) != nullptr, "Failed to compile presets");
+
+ if (!CreateGlobalPipelines()) {
+ ERROR_LOG(G3D, "Failed to create global pipelines");
+ return false;
+ }
// Load the atlas.
-
size_t atlas_data_size = 0;
if (!g_ui_atlas.IsMetadataLoaded()) {
const uint8_t *atlas_data = VFSReadFile("ui_atlas.meta", &atlas_data_size);
bool load_success = atlas_data != nullptr && g_ui_atlas.Load(atlas_data, atlas_data_size);
- _assert_msg_(load_success, "Failed to load ui_atlas.meta");
+ if (!load_success) {
+ ERROR_LOG(G3D, "Failed to load ui_atlas.meta - graphics will be broken.");
+ // Stumble along with broken visuals instead of dying.
+ }
delete[] atlas_data;
}
+
ui_draw2d.SetAtlas(&g_ui_atlas);
ui_draw2d_front.SetAtlas(&g_ui_atlas);
@@ -836,43 +839,10 @@ bool NativeInitGraphics(GraphicsContext *graphicsContext) {
uiContext = new UIContext();
uiContext->theme = &ui_theme;
- Draw::InputLayout *inputLayout = ui_draw2d.CreateInputLayout(g_draw);
- Draw::BlendState *blendNormal = g_draw->CreateBlendState({ true, 0xF, BlendFactor::SRC_ALPHA, BlendFactor::ONE_MINUS_SRC_ALPHA });
- Draw::DepthStencilState *depth = g_draw->CreateDepthStencilState({ false, false, Comparison::LESS });
- Draw::RasterState *rasterNoCull = g_draw->CreateRasterState({});
-
- PipelineDesc colorDesc{
- Primitive::TRIANGLE_LIST,
- { g_draw->GetVshaderPreset(VS_COLOR_2D), g_draw->GetFshaderPreset(FS_COLOR_2D) },
- inputLayout, depth, blendNormal, rasterNoCull, &vsColBufDesc,
- };
- PipelineDesc texColorDesc{
- Primitive::TRIANGLE_LIST,
- { g_draw->GetVshaderPreset(VS_TEXTURE_COLOR_2D), g_draw->GetFshaderPreset(FS_TEXTURE_COLOR_2D) },
- inputLayout, depth, blendNormal, rasterNoCull, &vsTexColBufDesc,
- };
-
- colorPipeline = g_draw->CreateGraphicsPipeline(colorDesc);
- texColorPipeline = g_draw->CreateGraphicsPipeline(texColorDesc);
-
- _assert_(colorPipeline);
- _assert_(texColorPipeline);
-
- // Release these now, reference counting should ensure that they get completely released
- // once we delete both pipelines.
- inputLayout->Release();
- rasterNoCull->Release();
- blendNormal->Release();
- depth->Release();
-
ui_draw2d.Init(g_draw, texColorPipeline);
ui_draw2d_front.Init(g_draw, texColorPipeline);
uiContext->Init(g_draw, texColorPipeline, colorPipeline, &ui_draw2d, &ui_draw2d_front);
- RasterStateDesc desc;
- desc.cull = CullMode::NONE;
- desc.frontFace = Facing::CCW;
-
if (uiContext->Text())
uiContext->Text()->SetFont("Tahoma", 20, 0);
@@ -899,13 +869,54 @@ bool NativeInitGraphics(GraphicsContext *graphicsContext) {
g_gameInfoCache = new GameInfoCache();
- if (gpu)
+ if (gpu) {
gpu->DeviceRestore();
+ }
ILOG("NativeInitGraphics completed");
return true;
}
+bool CreateGlobalPipelines() {
+ using namespace Draw;
+
+ InputLayout *inputLayout = ui_draw2d.CreateInputLayout(g_draw);
+ BlendState *blendNormal = g_draw->CreateBlendState({ true, 0xF, BlendFactor::SRC_ALPHA, BlendFactor::ONE_MINUS_SRC_ALPHA });
+ DepthStencilState *depth = g_draw->CreateDepthStencilState({ false, false, Comparison::LESS });
+ RasterState *rasterNoCull = g_draw->CreateRasterState({});
+
+ PipelineDesc colorDesc{
+ Primitive::TRIANGLE_LIST,
+ { g_draw->GetVshaderPreset(VS_COLOR_2D), g_draw->GetFshaderPreset(FS_COLOR_2D) },
+ inputLayout, depth, blendNormal, rasterNoCull, &vsColBufDesc,
+ };
+ PipelineDesc texColorDesc{
+ Primitive::TRIANGLE_LIST,
+ { g_draw->GetVshaderPreset(VS_TEXTURE_COLOR_2D), g_draw->GetFshaderPreset(FS_TEXTURE_COLOR_2D) },
+ inputLayout, depth, blendNormal, rasterNoCull, &vsTexColBufDesc,
+ };
+
+ colorPipeline = g_draw->CreateGraphicsPipeline(colorDesc);
+ if (!colorPipeline) {
+ // Something really critical is wrong, don't care much about correct releasing of the states.
+ return false;
+ }
+
+ texColorPipeline = g_draw->CreateGraphicsPipeline(texColorDesc);
+ if (!texColorPipeline) {
+ // Something really critical is wrong, don't care much about correct releasing of the states.
+ return false;
+ }
+
+ // Release these now, reference counting should ensure that they get completely released
+ // once we delete both pipelines.
+ inputLayout->Release();
+ rasterNoCull->Release();
+ blendNormal->Release();
+ depth->Release();
+ return true;
+}
+
void NativeShutdownGraphics() {
screenManager->deviceLost();
@@ -914,12 +925,12 @@ void NativeShutdownGraphics() {
ILOG("NativeShutdownGraphics");
-#ifdef _WIN32
+#if PPSSPP_PLATFORM(WINDOWS)
delete winAudioBackend;
winAudioBackend = nullptr;
#endif
-#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
+#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
if (winCamera) {
winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::SHUTDOWN, nullptr });
while (!winCamera->isShutDown()) {};// Wait for shutting down.
@@ -1088,7 +1099,7 @@ void NativeRender(GraphicsContext *graphicsContext) {
screenManager->resized();
// TODO: Move this to the GraphicsContext objects for each backend.
-#if !defined(_WIN32) && !defined(ANDROID)
+#if !PPSSPP_PLATFORM(WINDOWS) && !defined(ANDROID)
PSP_CoreParameter().pixelWidth = pixel_xres;
PSP_CoreParameter().pixelHeight = pixel_yres;
NativeMessageReceived("gpu_resized", "");
@@ -1135,7 +1146,7 @@ void HandleGlobalMessage(const std::string &msg, const std::string &value) {
if (msg == "core_powerSaving") {
if (value != "false") {
auto sy = GetI18NCategory("System");
-#ifdef __ANDROID__
+#if PPSSPP_PLATFORM(ANDROID)
osm.Show(sy->T("WARNING: Android battery save mode is on"), 2.0f, 0xFFFFFF, -1, true, "core_powerSaving");
#else
osm.Show(sy->T("WARNING: Battery save mode is on"), 2.0f, 0xFFFFFF, -1, true, "core_powerSaving");
@@ -1144,7 +1155,7 @@ void HandleGlobalMessage(const std::string &msg, const std::string &value) {
Core_SetPowerSaving(value != "false");
}
if (msg == "permission_granted" && value == "storage") {
-#ifdef __ANDROID__
+#if PPSSPP_PLATFORM(ANDROID)
CreateDirectoriesAndroid();
#endif
// We must have failed to load the config before, so load it now to avoid overwriting the old config
diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp
index 9e5e028fc2..9646cf25e9 100644
--- a/Windows/GPU/WindowsGLContext.cpp
+++ b/Windows/GPU/WindowsGLContext.cpp
@@ -399,17 +399,23 @@ bool WindowsGLContext::InitFromRenderThread(std::string *error_message) {
pauseRequested = false;
resumeRequested = false;
+ CheckGLExtensions();
+ draw_ = Draw::T3DCreateGLContext();
+ bool success = draw_->CreatePresets(); // if we get this far, there will always be a GLSL compiler capable of compiling these.
+ if (!success) {
+ delete draw_;
+ draw_ = nullptr;
+ ReleaseGLContext();
+ return false;
+ }
+
// These are auto-reset events.
pauseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
resumeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- CheckGLExtensions();
- draw_ = Draw::T3DCreateGLContext();
renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
renderManager_->SetInflightFrames(g_Config.iInflightFrames);
SetGPUBackend(GPUBackend::OPENGL);
- bool success = draw_->CreatePresets(); // if we get this far, there will always be a GLSL compiler capable of compiling these.
- _assert_msg_(success, "Failed to compile preset shaders");
renderManager_->SetSwapFunction([&]() {::SwapBuffers(hDC); });
if (wglSwapIntervalEXT) {
// glew loads wglSwapIntervalEXT if available
@@ -430,14 +436,10 @@ void WindowsGLContext::Shutdown() {
glslang::FinalizeProcess();
}
-void WindowsGLContext::ShutdownFromRenderThread() {
- delete draw_;
- draw_ = nullptr;
- CloseHandle(pauseEvent);
- CloseHandle(resumeEvent);
+void WindowsGLContext::ReleaseGLContext() {
if (hRC) {
// Are we able to release the DC and RC contexts?
- if (!wglMakeCurrent(NULL,NULL)) {
+ if (!wglMakeCurrent(NULL, NULL)) {
MessageBox(NULL, L"Release of DC and RC failed.", L"SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
}
@@ -458,6 +460,14 @@ void WindowsGLContext::ShutdownFromRenderThread() {
hWnd_ = NULL;
}
+void WindowsGLContext::ShutdownFromRenderThread() {
+ delete draw_;
+ draw_ = nullptr;
+ CloseHandle(pauseEvent);
+ CloseHandle(resumeEvent);
+ ReleaseGLContext();
+}
+
void WindowsGLContext::Resize() {
}
diff --git a/Windows/GPU/WindowsGLContext.h b/Windows/GPU/WindowsGLContext.h
index 1303aece79..df8190f9dc 100644
--- a/Windows/GPU/WindowsGLContext.h
+++ b/Windows/GPU/WindowsGLContext.h
@@ -34,6 +34,8 @@ public:
Draw::DrawContext *GetDrawContext() override { return draw_; }
private:
+ void ReleaseGLContext();
+
bool renderThread_;
Draw::DrawContext *draw_;
GLRenderManager *renderManager_;
diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj
index ed8abc3a30..def7547093 100644
--- a/Windows/PPSSPP.vcxproj
+++ b/Windows/PPSSPP.vcxproj
@@ -600,7 +600,7 @@
true
true
-
+
true
true
true
@@ -610,7 +610,7 @@
true
true
-
+
true
true
true
@@ -1028,7 +1028,7 @@
-
+
true
true
true
@@ -1038,7 +1038,7 @@
true
true
-
+
true
true
true
diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters
index 451be96f95..ffbdc51429 100644
--- a/Windows/PPSSPP.vcxproj.filters
+++ b/Windows/PPSSPP.vcxproj.filters
@@ -205,12 +205,6 @@
Other Platforms\Android
-
- Other Platforms\Android
-
-
- Other Platforms\Android
-
Other Platforms\iOS
@@ -271,6 +265,12 @@
Other Platforms\UWP
+
+ Other Platforms\Android
+
+
+ Other Platforms\Android
+
@@ -430,12 +430,6 @@
Other Platforms\Android
-
- Other Platforms\Android
-
-
- Other Platforms\Android
-
Other Platforms\iOS
@@ -538,6 +532,12 @@
Other Platforms\UWP
+
+ Other Platforms\Android
+
+
+ Other Platforms\Android
+
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
index 510c6a0a1d..3df8536529 100644
--- a/android/jni/Android.mk
+++ b/android/jni/Android.mk
@@ -499,8 +499,8 @@ LOCAL_SRC_FILES := \
$(SRC)/android/jni/AndroidEGLContext.cpp \
$(SRC)/android/jni/AndroidJavaGLContext.cpp \
$(SRC)/android/jni/AndroidVulkanContext.cpp \
- $(SRC)/android/jni/native_audio.cpp \
- $(SRC)/android/jni/native-audio-so.cpp \
+ $(SRC)/android/jni/AndroidAudio.cpp \
+ $(SRC)/android/jni/OpenSLContext.cpp \
$(SRC)/UI/BackgroundAudio.cpp \
$(SRC)/UI/DiscordIntegration.cpp \
$(SRC)/UI/ChatScreen.cpp \
diff --git a/android/jni/native_audio.cpp b/android/jni/AndroidAudio.cpp
similarity index 79%
rename from android/jni/native_audio.cpp
rename to android/jni/AndroidAudio.cpp
index a06159a89c..98cf3a87be 100644
--- a/android/jni/native_audio.cpp
+++ b/android/jni/AndroidAudio.cpp
@@ -1,6 +1,18 @@
#include "base/logging.h"
-#include "android/jni/native_audio.h"
-#include "android/jni/native-audio-so.h"
+#include "android/jni/AndroidAudio.h"
+#include "android/jni/OpenSLContext.h"
+
+AudioContext::AudioContext(AndroidAudioCallback cb, int _FramesPerBuffer, int _SampleRate)
+ : audioCallback(cb), framesPerBuffer(_FramesPerBuffer), sampleRate(_SampleRate) {
+ if (framesPerBuffer == 0)
+ framesPerBuffer = 256;
+ if (framesPerBuffer < 32)
+ framesPerBuffer = 32;
+ if (framesPerBuffer > 4096)
+ framesPerBuffer = 4096;
+
+ sampleRate = _SampleRate;
+}
struct AndroidAudioState {
AudioContext *ctx = nullptr;
diff --git a/android/jni/native_audio.h b/android/jni/AndroidAudio.h
similarity index 55%
rename from android/jni/native_audio.h
rename to android/jni/AndroidAudio.h
index 427f29ca36..a3d1ab8ee8 100644
--- a/android/jni/native_audio.h
+++ b/android/jni/AndroidAudio.h
@@ -1,8 +1,22 @@
#pragma once
-#include "native-audio-so.h"
#include
+typedef int (*AndroidAudioCallback)(short *buffer, int num_samples);
+
+class AudioContext {
+public:
+ AudioContext(AndroidAudioCallback cb, int _FramesPerBuffer, int _SampleRate);
+ virtual bool Init() { return false; }
+ virtual ~AudioContext() {}
+
+protected:
+ AndroidAudioCallback audioCallback;
+
+ int framesPerBuffer;
+ int sampleRate;
+};
+
struct AndroidAudioState;
// It's okay for optimalFramesPerBuffer and optimalSampleRate to be 0. Defaults will be used.
diff --git a/android/jni/AndroidJavaGLContext.cpp b/android/jni/AndroidJavaGLContext.cpp
index afc5de6921..d1b8e17a6a 100644
--- a/android/jni/AndroidJavaGLContext.cpp
+++ b/android/jni/AndroidJavaGLContext.cpp
@@ -4,6 +4,7 @@
#include "base/display.h"
#include "base/NativeApp.h"
#include "gfx_es2/gpu_features.h"
+#include "Common/Log.h"
#include "Core/ConfigValues.h"
#include "Core/System.h"
@@ -17,12 +18,11 @@ bool AndroidJavaEGLGraphicsContext::InitFromRenderThread(ANativeWindow *wnd, int
// OpenGL handles rotated rendering in the driver.
g_display_rotation = DisplayRotation::ROTATE_0;
g_display_rot_matrix.setIdentity();
- draw_ = Draw::T3DCreateGLContext();
+ draw_ = Draw::T3DCreateGLContext(); // Can't fail
renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
renderManager_->SetInflightFrames(g_Config.iInflightFrames);
- bool success = draw_->CreatePresets();
- _assert_msg_(success, "Failed to compile preset shaders");
- return success;
+ draw_->CreatePresets();
+ return true;
}
void AndroidJavaEGLGraphicsContext::ShutdownFromRenderThread() {
diff --git a/android/jni/AndroidVulkanContext.cpp b/android/jni/AndroidVulkanContext.cpp
index 06b09dae9b..76fc4d3e3c 100644
--- a/android/jni/AndroidVulkanContext.cpp
+++ b/android/jni/AndroidVulkanContext.cpp
@@ -71,7 +71,6 @@ bool AndroidVulkanContext::InitAPI() {
}
g_Vulkan->ChooseDevice(physicalDevice);
- // Here we can enable device extensions if we like.
ILOG("Creating Vulkan device");
if (g_Vulkan->CreateDevice() != VK_SUCCESS) {
@@ -82,6 +81,7 @@ bool AndroidVulkanContext::InitAPI() {
g_Vulkan = nullptr;
return false;
}
+
ILOG("Vulkan device created!");
return true;
}
diff --git a/android/jni/native-audio-so.cpp b/android/jni/OpenSLContext.cpp
similarity index 80%
rename from android/jni/native-audio-so.cpp
rename to android/jni/OpenSLContext.cpp
index 17e665d2de..26965a72d5 100644
--- a/android/jni/native-audio-so.cpp
+++ b/android/jni/OpenSLContext.cpp
@@ -2,8 +2,7 @@
//
// Loosely based on the Android NDK sample code.
-#include
-#include
+#include
#include
// for native audio
@@ -11,19 +10,8 @@
#include
#include "base/logging.h"
-#include "native-audio-so.h"
-
-AudioContext::AudioContext(AndroidAudioCallback cb, int _FramesPerBuffer, int _SampleRate)
- : audioCallback(cb), framesPerBuffer(_FramesPerBuffer), sampleRate(_SampleRate) {
- if (framesPerBuffer == 0)
- framesPerBuffer = 256;
- if (framesPerBuffer < 32)
- framesPerBuffer = 32;
- if (framesPerBuffer > 4096)
- framesPerBuffer = 4096;
-
- sampleRate = _SampleRate;
-}
+#include "Common/Log.h"
+#include "OpenSLContext.h"
// This callback handler is called every time a buffer finishes playing.
// The documentation available is very unclear about how to best manage buffers.
@@ -36,7 +24,7 @@ void OpenSLContext::bqPlayerCallbackWrap(SLAndroidSimpleBufferQueueItf bq, void
void OpenSLContext::BqPlayerCallback(SLAndroidSimpleBufferQueueItf bq) {
if (bq != bqPlayerBufferQueue) {
- ELOG("Wrong bq!");
+ ELOG("OpenSL: Wrong bq!");
return;
}
@@ -53,7 +41,7 @@ void OpenSLContext::BqPlayerCallback(SLAndroidSimpleBufferQueueItf bq) {
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
// which for this code example would indicate a programming error
if (result != SL_RESULT_SUCCESS) {
- ELOG("OpenSL ES: Failed to enqueue! %i %i", renderedFrames, sizeInBytes);
+ ELOG("OpenSL: Failed to enqueue! %i %i", renderedFrames, sizeInBytes);
}
curBuffer += 1; // Switch buffer
@@ -70,24 +58,26 @@ bool OpenSLContext::Init() {
// create engine
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
if (result != SL_RESULT_SUCCESS) {
- ELOG("OpenSL ES: Failed to create the engine: %d", (int)result);
+ ELOG("OpenSL: Failed to create the engine: %d", (int)result);
engineObject = nullptr;
return false;
}
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
- assert(SL_RESULT_SUCCESS == result);
+ _assert_(SL_RESULT_SUCCESS == result);
+
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
- assert(SL_RESULT_SUCCESS == result);
+ _assert_(SL_RESULT_SUCCESS == result);
+
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);
if (result != SL_RESULT_SUCCESS) {
- ELOG("OpenSL ES: Failed to create output mix: %d", (int)result);
+ ELOG("OpenSL: Failed to create output mix: %d", (int)result);
(*engineObject)->Destroy(engineObject);
engineEngine = nullptr;
engineObject = nullptr;
return false;
}
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
- assert(SL_RESULT_SUCCESS == result);
+ _assert_(SL_RESULT_SUCCESS == result);
// The constants, such as SL_SAMPLINGRATE_44_1, are just 44100000.
SLuint32 sr = (SLuint32)sampleRate * 1000;
@@ -114,7 +104,7 @@ bool OpenSLContext::Init() {
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
if (result != SL_RESULT_SUCCESS) {
- ELOG("OpenSL ES: CreateAudioPlayer failed: %d", (int)result);
+ ELOG("OpenSL: CreateAudioPlayer failed: %d", (int)result);
(*outputMixObject)->Destroy(outputMixObject);
outputMixObject = nullptr;
@@ -126,18 +116,18 @@ bool OpenSLContext::Init() {
}
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
- assert(SL_RESULT_SUCCESS == result);
+ _assert_(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
- assert(SL_RESULT_SUCCESS == result);
+ _assert_(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
&bqPlayerBufferQueue);
- assert(SL_RESULT_SUCCESS == result);
+ _assert_(SL_RESULT_SUCCESS == result);
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, &bqPlayerCallbackWrap, this);
- assert(SL_RESULT_SUCCESS == result);
+ _assert_(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
- assert(SL_RESULT_SUCCESS == result);
+ _assert_(SL_RESULT_SUCCESS == result);
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
- assert(SL_RESULT_SUCCESS == result);
+ _assert_(SL_RESULT_SUCCESS == result);
// Allocate and enqueue N empty buffers.
for (int i = 0; i < NUM_BUFFERS; i++) {
@@ -159,7 +149,7 @@ bool OpenSLContext::Init() {
// shut down the native audio system
OpenSLContext::~OpenSLContext() {
if (bqPlayerPlay) {
- ILOG("OpenSLWrap_Shutdown - stopping playback");
+ ILOG("OpenSL: Shutdown - stopping playback");
SLresult result;
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
if (SL_RESULT_SUCCESS != result) {
@@ -167,7 +157,7 @@ OpenSLContext::~OpenSLContext() {
}
}
- ILOG("OpenSLWrap_Shutdown - deleting player object");
+ ILOG("OpenSL: Shutdown - deleting player object");
if (bqPlayerObject) {
(*bqPlayerObject)->Destroy(bqPlayerObject);
@@ -177,14 +167,14 @@ OpenSLContext::~OpenSLContext() {
bqPlayerVolume = nullptr;
}
- ILOG("OpenSLWrap_Shutdown - deleting mix object");
+ ILOG("OpenSL: Shutdown - deleting mix object");
if (outputMixObject) {
(*outputMixObject)->Destroy(outputMixObject);
outputMixObject = nullptr;
}
- ILOG("OpenSLWrap_Shutdown - deleting engine object");
+ ILOG("OpenSL: Shutdown - deleting engine object");
if (engineObject) {
(*engineObject)->Destroy(engineObject);
@@ -196,6 +186,6 @@ OpenSLContext::~OpenSLContext() {
delete[] buffer[i];
buffer[i] = nullptr;
}
- ILOG("OpenSLWrap_Shutdown - finished");
+ ILOG("OpenSL: Shutdown - finished");
}
diff --git a/android/jni/native-audio-so.h b/android/jni/OpenSLContext.h
similarity index 73%
rename from android/jni/native-audio-so.h
rename to android/jni/OpenSLContext.h
index 9623e51709..3705f66b82 100644
--- a/android/jni/native-audio-so.h
+++ b/android/jni/OpenSLContext.h
@@ -3,20 +3,7 @@
#include
#include
-typedef int (*AndroidAudioCallback)(short *buffer, int num_samples);
-
-class AudioContext {
-public:
- AudioContext(AndroidAudioCallback cb, int _FramesPerBuffer, int _SampleRate);
- virtual bool Init() { return false; }
- virtual ~AudioContext() {}
-
-protected:
- AndroidAudioCallback audioCallback;
-
- int framesPerBuffer;
- int sampleRate;
-};
+#include "AndroidAudio.h"
class OpenSLContext : public AudioContext {
public:
diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp
index 086371085d..0ac1036011 100644
--- a/android/jni/app-android.cpp
+++ b/android/jni/app-android.cpp
@@ -4,7 +4,6 @@
// It calls a set of methods defined in NativeApp.h. These should be implemented
// by your game or app.
-#include
#include
#include
@@ -63,7 +62,7 @@ struct JNIEnv {};
#include "math/math_util.h"
#include "net/resolve.h"
#include "util/text/parsers.h"
-#include "android/jni/native_audio.h"
+#include "android/jni/AndroidAudio.h"
#include "gfx/gl_common.h"
#include "gfx_es2/gpu_features.h"
@@ -594,7 +593,6 @@ retry:
ELOG("NativeApp.init(): iGPUBackend %d not supported. Switching to OpenGL.", (int)g_Config.iGPUBackend);
g_Config.iGPUBackend = (int)GPUBackend::OPENGL;
goto retry;
- // Crash();
}
if (useCPUThread) {
@@ -605,7 +603,7 @@ retry:
extern "C" void Java_org_ppsspp_ppsspp_NativeApp_audioInit(JNIEnv *, jclass) {
sampleRate = optimalSampleRate;
- if (NativeQueryConfig("force44khz") != "0" || optimalSampleRate == 0) {
+ if (optimalSampleRate == 0) {
sampleRate = 44100;
}
if (optimalFramesPerBuffer > 0) {
@@ -688,7 +686,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) {
}
// JavaEGL
-extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, jobject obj) {
+extern "C" bool Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, jobject obj) {
// We should be running on the render thread here.
std::string errorMessage;
if (renderer_inited) {
@@ -712,21 +710,34 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env,
ILOG("Shut down both threads. Now let's bring it up again!");
- graphicsContext->InitFromRenderThread(nullptr, 0, 0, 0, 0);
+ if (!graphicsContext->InitFromRenderThread(nullptr, 0, 0, 0, 0)) {
+ SystemToast("Graphics initialization failed. Quitting.");
+ return false;
+ }
+
if (useCPUThread) {
EmuThreadStart();
} else {
- NativeInitGraphics(graphicsContext);
+ if (!NativeInitGraphics(graphicsContext)) {
+ // Gonna be in a weird state here, not good.
+ SystemToast("Failed to initialize graphics.");
+ return false;
+ }
}
+
graphicsContext->ThreadStart();
ILOG("Restored.");
} else {
ILOG("NativeApp.displayInit() first time");
- graphicsContext->InitFromRenderThread(nullptr, 0, 0, 0, 0);
+ if (!graphicsContext->InitFromRenderThread(nullptr, 0, 0, 0, 0)) {
+ SystemToast("Graphics initialization failed. Quitting.");
+ return false;
+ }
graphicsContext->ThreadStart();
renderer_inited = true;
}
NativeMessageReceived("recreateviews", "");
+ return true;
}
static void recalculateDpi() {
@@ -1156,6 +1167,11 @@ static void ProcessFrameCommands(JNIEnv *env) {
}
extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(JNIEnv *env, jobject obj, jobject _surf) {
+ if (!graphicsContext) {
+ ELOG("runEGLRenderLoop: Tried to enter without a created graphics context.");
+ return false;
+ }
+
// Needed for Vulkan, even if we're not using the old EGL path.
exitRenderLoop = false;
@@ -1175,10 +1191,11 @@ extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J
auto tryInit = [&]() {
if (graphicsContext->InitFromRenderThread(wnd, desiredBackbufferSizeX, desiredBackbufferSizeY, backbuffer_format, androidVersion)) {
return true;
+ } else {
+ ELOG("Failed to initialize graphics context.");
+ SystemToast("Failed to initialize graphics context.");
+ return false;
}
-
- ELOG("Failed to initialize graphics context.");
- return false;
};
bool initSuccess = tryInit();
@@ -1202,7 +1219,10 @@ extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J
if (!exitRenderLoop) {
if (!useCPUThread) {
- NativeInitGraphics(graphicsContext);
+ if (!NativeInitGraphics(graphicsContext)) {
+ ELOG("Failed to initialize graphics.");
+ // Gonna be in a weird state here..
+ }
}
graphicsContext->ThreadStart();
renderer_inited = true;
diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java
index ac272d2d75..477d7b42cf 100644
--- a/android/src/org/ppsspp/ppsspp/NativeActivity.java
+++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java
@@ -18,7 +18,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.PixelFormat;
-import android.graphics.Point;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
@@ -51,11 +50,12 @@ import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+@SuppressWarnings("ConstantConditions")
public abstract class NativeActivity extends Activity {
// Remember to loadLibrary your JNI .so in a static {} block
// Adjust these as necessary
- private static String TAG = "PPSSPPNativeActivity";
+ private static final String TAG = "PPSSPPNativeActivity";
// Allows us to skip a lot of initialization on secondary calls to onCreate.
private static boolean initialized = false;
@@ -98,7 +98,7 @@ public abstract class NativeActivity extends Activity {
// This is to avoid losing the game/menu state etc when we are just
// switched-away from or rotated etc.
private boolean shuttingDown;
- private static int RESULT_LOAD_IMAGE = 1;
+ private static final int RESULT_LOAD_IMAGE = 1;
// Allow for multiple connected gamepads but just consider them the same for now.
// Actually this is not entirely true, see the code.
@@ -132,10 +132,6 @@ public abstract class NativeActivity extends Activity {
public native void registerCallbacks();
public native void unregisterCallbacks();
- public boolean useLowProfileButtons() {
- return true;
- }
-
NativeRenderer getRenderer() {
return nativeRenderer;
}
@@ -209,7 +205,7 @@ public abstract class NativeActivity extends Activity {
}
@Override
- public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
+ public void onRequestPermissionsResult(int requestCode, String [] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_STORAGE_PERMISSION:
if (permissionsGranted(permissions, grantResults)) {
@@ -248,7 +244,7 @@ public abstract class NativeActivity extends Activity {
}
powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- if (powerManager.isSustainedPerformanceModeSupported()) {
+ if (powerManager != null && powerManager.isSustainedPerformanceModeSupported()) {
sustainedPerfSupported = true;
NativeApp.sendMessage("sustained_perf_supported", "1");
}
@@ -259,10 +255,10 @@ public abstract class NativeActivity extends Activity {
Log.d(TAG, "Landscape: " + landscape);
// Get system information
- ApplicationInfo appInfo = null;
-
PackageManager packMgmr = getPackageManager();
String packageName = getPackageName();
+
+ ApplicationInfo appInfo;
try {
appInfo = packMgmr.getApplicationInfo(packageName, 0);
} catch (NameNotFoundException e) {
@@ -345,22 +341,20 @@ public abstract class NativeActivity extends Activity {
private void updateSustainedPerformanceMode() {
if (sustainedPerfSupported) {
// Query the native application on the desired rotation.
- int enable = 0;
String str = NativeApp.queryConfig("sustainedPerformanceMode");
try {
- enable = Integer.parseInt(str);
+ int enable = Integer.parseInt(str);
+ getWindow().setSustainedPerformanceMode(enable != 0);
} catch (NumberFormatException e) {
Log.e(TAG, "Invalid perf mode: " + str);
- return;
}
- getWindow().setSustainedPerformanceMode(enable != 0);
}
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
private void updateScreenRotation(String cause) {
// Query the native application on the desired rotation.
- int rot = 0;
+ int rot;
String rotString = NativeApp.queryConfig("screenRotation");
try {
rot = Integer.parseInt(rotString);
@@ -407,10 +401,7 @@ public abstract class NativeActivity extends Activity {
}
// Compute our _desired_ systemUiVisibility
- int flags = 0;
- if (useLowProfileButtons()) {
- flags |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
- }
+ int flags = View.SYSTEM_UI_FLAG_LOW_PROFILE;
if (useImmersive()) {
flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
flags |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
@@ -436,7 +427,7 @@ public abstract class NativeActivity extends Activity {
}
}
- private Runnable mEmulationRunner = new Runnable() {
+ private final Runnable mEmulationRunner = new Runnable() {
@Override
public void run() {
Log.i(TAG, "Starting the render loop: " + mSurface);
@@ -616,6 +607,7 @@ public abstract class NativeActivity extends Activity {
@Override
protected void onDestroy() {
super.onDestroy();
+ Log.i(TAG, "onDestroy");
if (javaGL) {
if (nativeRenderer.isRenderingFrame()) {
Log.i(TAG, "Waiting for renderer to finish.");
@@ -628,21 +620,23 @@ public abstract class NativeActivity extends Activity {
tries--;
} while (nativeRenderer.isRenderingFrame() && tries > 0);
}
- Log.i(TAG, "onDestroy");
mGLSurfaceView.onDestroy();
- // Probably vain attempt to help the garbage collector...
mGLSurfaceView = null;
- audioFocusChangeListener = null;
- audioManager = null;
} else {
mSurfaceView.onDestroy();
mSurfaceView = null;
}
+
+ // Probably vain attempt to help the garbage collector...
+ audioFocusChangeListener = null;
+ audioManager = null;
+
sizeManager.setSurfaceView(null);
if (mPowerSaveModeReceiver != null) {
mPowerSaveModeReceiver.destroy(this);
mPowerSaveModeReceiver = null;
}
+
// TODO: Can we ensure that the GL thread has stopped rendering here?
// I've seen crashes that seem to indicate that sometimes it hasn't...
NativeApp.audioShutdown();
@@ -697,6 +691,7 @@ public abstract class NativeActivity extends Activity {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
updateSystemUiVisibility();
}
+
// OK, config should be initialized, we can query for screen rotation.
if (javaGL || Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
updateScreenRotation("onResume");
@@ -991,30 +986,29 @@ public abstract class NativeActivity extends Activity {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && null != data) {
Uri selectedImage = data.getData();
- String[] filePathColumn = {MediaStore.Images.Media.DATA};
- Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
- cursor.moveToFirst();
- int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
- String picturePath = cursor.getString(columnIndex);
- cursor.close();
- NativeApp.sendMessage("bgImage_updated", picturePath);
+ if (selectedImage != null) {
+ String[] filePathColumn = {MediaStore.Images.Media.DATA};
+ Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
+ cursor.moveToFirst();
+ int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
+ String picturePath = cursor.getString(columnIndex);
+ cursor.close();
+ NativeApp.sendMessage("bgImage_updated", picturePath);
+ }
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
- @SuppressWarnings("deprecation")
private AlertDialog.Builder createDialogBuilderWithTheme() {
return new AlertDialog.Builder(this, AlertDialog.THEME_HOLO_DARK);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
- @SuppressWarnings("deprecation")
private AlertDialog.Builder createDialogBuilderWithDeviceTheme() {
return new AlertDialog.Builder(this, AlertDialog.THEME_DEVICE_DEFAULT_DARK);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
- @SuppressWarnings("deprecation")
private AlertDialog.Builder createDialogBuilderWithDeviceThemeAndUiVisibility() {
AlertDialog.Builder bld = new AlertDialog.Builder(this, AlertDialog.THEME_DEVICE_DEFAULT_DARK);
bld.setOnDismissListener(new DialogInterface.OnDismissListener() {
@@ -1065,7 +1059,7 @@ public abstract class NativeActivity extends Activity {
else
bld = createDialogBuilderNew();
- AlertDialog dlg = bld
+ AlertDialog.Builder builder = bld
.setView(fl)
.setTitle(title)
.setPositiveButton(defaultAction, new DialogInterface.OnClickListener() {
@@ -1081,15 +1075,17 @@ public abstract class NativeActivity extends Activity {
NativeApp.sendInputBox(seqID, false, "");
d.cancel();
}
- })
- .setOnDismissListener(new DialogInterface.OnDismissListener() {
+ });
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface d) {
NativeApp.sendInputBox(seqID, false, "");
updateSystemUiVisibility();
}
- })
- .create();
+ });
+ }
+ AlertDialog dlg = builder.create();
dlg.setCancelable(true);
dlg.show();
@@ -1188,11 +1184,10 @@ public abstract class NativeActivity extends Activity {
inputMethodManager.toggleSoftInputFromWindow(surfView.getApplicationWindowToken(), InputMethodManager.SHOW_FORCED, 0);
return true;
} else if (command.equals("inputbox")) {
- String seqID = "";
String title = "Input";
String defString = "";
String[] param = params.split(":@:", 3);
- seqID = param[0];
+ String seqID = param[0];
if (param.length > 1 && param[1].length() > 0)
title = param[1];
if (param.length > 2)
@@ -1202,7 +1197,7 @@ public abstract class NativeActivity extends Activity {
return true;
} else if (command.equals("vibrate")) {
int milliseconds = -1;
- if (params != "") {
+ if (!params.equals("")) {
try {
milliseconds = Integer.parseInt(params);
} catch (NumberFormatException e) {
diff --git a/android/src/org/ppsspp/ppsspp/NativeRenderer.java b/android/src/org/ppsspp/ppsspp/NativeRenderer.java
index 31df096943..58d4de48d8 100644
--- a/android/src/org/ppsspp/ppsspp/NativeRenderer.java
+++ b/android/src/org/ppsspp/ppsspp/NativeRenderer.java
@@ -2,6 +2,7 @@ package org.ppsspp.ppsspp;
import android.opengl.GLSurfaceView;
import android.util.Log;
+import android.widget.Toast;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
@@ -12,7 +13,8 @@ import javax.microedition.khronos.opengles.GL10;
public class NativeRenderer implements GLSurfaceView.Renderer {
private static String TAG = "NativeRenderer";
private NativeActivity mActivity;
- private boolean inFrame;
+ private boolean inFrame = false;
+ private boolean failed = false;
NativeRenderer(NativeActivity act) {
mActivity = act;
@@ -22,6 +24,9 @@ public class NativeRenderer implements GLSurfaceView.Renderer {
return inFrame;
}
+ // TODO: Make use of this somehow.
+ public boolean hasFailedInit() { return failed; }
+
public void onDrawFrame(GL10 unused /*use GLES20*/) {
inFrame = true;
displayRender();
@@ -29,6 +34,7 @@ public class NativeRenderer implements GLSurfaceView.Renderer {
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ failed = false;
Log.i(TAG, "NativeRenderer: onSurfaceCreated");
EGL10 egl = (EGL10)EGLContext.getEGL();
@@ -47,17 +53,18 @@ public class NativeRenderer implements GLSurfaceView.Renderer {
} else {
Log.e(TAG, "egl == null");
}
- // Log.i(TAG, "onSurfaceCreated - EGL context is new or was lost");
- // Actually, it seems that it is here we should recreate lost GL objects.
- displayInit();
+
+ if (!displayInit()) {
+ Log.e(TAG, "Display init failed");
+ Toast.makeText(mActivity, "Failed to initialize rendering. Close the app and try again.", Toast.LENGTH_LONG);
+ failed = true;
+ }
}
- public void onSurfaceChanged(GL10 unused, int width, int height) {
- }
+ public void onSurfaceChanged(GL10 unused, int width, int height) {}
// Note: This also means "device lost" and you should reload
// all buffered objects.
- public native void displayInit();
-
+ public native boolean displayInit();
public native void displayRender();
}
diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h
index 395687f433..2ead85c2e4 100644
--- a/ext/native/thin3d/thin3d.h
+++ b/ext/native/thin3d/thin3d.h
@@ -136,6 +136,7 @@ enum class Primitive {
TRIANGLE_STRIP_ADJ,
UNDEFINED,
+ PRIMITIVE_TYPE_COUNT,
};
enum VertexShaderPreset : int {
diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp
index 6f697b251f..61ce579814 100644
--- a/ext/native/thin3d/thin3d_gl.cpp
+++ b/ext/native/thin3d/thin3d_gl.cpp
@@ -3,7 +3,6 @@
#include
#include
#include