diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index dd3eb2794e..20bddb0d1f 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -803,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); @@ -834,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); @@ -897,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(); @@ -912,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. @@ -1086,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", ""); @@ -1133,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"); @@ -1142,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/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/app-android.cpp b/android/jni/app-android.cpp index 85acaf674f..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 @@ -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) { @@ -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 a06d47fe59..5cc6f64846 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -616,6 +616,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,7 +629,6 @@ public abstract class NativeActivity extends Activity { tries--; } while (nativeRenderer.isRenderingFrame() && tries > 0); } - Log.i(TAG, "onDestroy"); mGLSurfaceView.onDestroy(); mGLSurfaceView = null; } else { diff --git a/android/src/org/ppsspp/ppsspp/NativeRenderer.java b/android/src/org/ppsspp/ppsspp/NativeRenderer.java index 31df096943..5051b3eee0 100644 --- a/android/src/org/ppsspp/ppsspp/NativeRenderer.java +++ b/android/src/org/ppsspp/ppsspp/NativeRenderer.java @@ -13,6 +13,7 @@ public class NativeRenderer implements GLSurfaceView.Renderer { private static String TAG = "NativeRenderer"; private NativeActivity mActivity; private boolean inFrame; + private boolean failed = false; NativeRenderer(NativeActivity act) { mActivity = act; @@ -21,6 +22,7 @@ public class NativeRenderer implements GLSurfaceView.Renderer { public boolean isRenderingFrame() { return inFrame; } + public boolean hasFailedInit() { return failed; } public void onDrawFrame(GL10 unused /*use GLES20*/) { inFrame = true; @@ -29,6 +31,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(); @@ -49,7 +52,10 @@ public class NativeRenderer implements GLSurfaceView.Renderer { } // 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"); + failed = true; + } } public void onSurfaceChanged(GL10 unused, int width, int height) { @@ -57,7 +63,7 @@ public class NativeRenderer implements GLSurfaceView.Renderer { // 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_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index fda3a40f38..946c7f99dc 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "base/logging.h" #include "math/dataconv.h"