diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp
index 2b485d9d..c440a586 100644
--- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp
+++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp
@@ -260,6 +260,8 @@ void VulkanRenderer::GetDeviceFeatures()
physicalDeviceFeatures2.pNext = prevStruct;
vkGetPhysicalDeviceFeatures2(m_physicalDevice, &physicalDeviceFeatures2);
+ m_featureControl.deviceFeatures.geometry_shader = physicalDeviceFeatures2.features.geometryShader;
+ m_featureControl.deviceFeatures.logic_op = physicalDeviceFeatures2.features.logicOp;
/* Get Vulkan device properties and limits */
VkPhysicalDeviceFloatControlsPropertiesKHR pfcp{};
@@ -451,10 +453,8 @@ VulkanRenderer::VulkanRenderer()
deviceFeatures.independentBlend = VK_TRUE;
deviceFeatures.samplerAnisotropy = VK_TRUE;
deviceFeatures.imageCubeArray = VK_TRUE;
-#if !BOOST_OS_MACOS
- deviceFeatures.geometryShader = VK_TRUE;
-// deviceFeatures.logicOp = VK_TRUE;
-#endif
+ deviceFeatures.geometryShader = m_featureControl.deviceFeatures.geometry_shader;
+ deviceFeatures.logicOp = m_featureControl.deviceFeatures.logic_op;
deviceFeatures.occlusionQueryPrecise = VK_TRUE;
deviceFeatures.depthClamp = VK_TRUE;
deviceFeatures.depthBiasClamp = VK_TRUE;
diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h
index 162dc85a..e318c910 100644
--- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h
+++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h
@@ -457,6 +457,12 @@ private:
bool shader_float_controls = false; // VK_KHR_shader_float_controls
}deviceExtensions;
+ struct
+ {
+ bool geometry_shader;
+ bool logic_op;
+ } deviceFeatures;
+
struct
{
bool shaderRoundingModeRTEFloat32{ false };
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index d83049f8..814563c2 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -6,6 +6,11 @@
+
+
& fn)
+{
+ try
+ {
+ fn();
+ } catch (const std::exception& exception)
+ {
+ jclass exceptionClass = env->FindClass("info/cemu/Cemu/nativeinterface/NativeException");
+ env->ThrowNew(exceptionClass, exception.what());
+ } catch (...)
+ {
+ jclass exceptionClass = env->FindClass("info/cemu/Cemu/nativeinterface/NativeException");
+ env->ThrowNew(exceptionClass, "Unknown native exception");
+ }
+}
+
jobject JNIUtils::createJavaStringArrayList(JNIEnv* env, const std::vector& strings)
{
jclass arrayListClass = env->FindClass("java/util/ArrayList");
diff --git a/src/android/app/src/main/cpp/JNIUtils.h b/src/android/app/src/main/cpp/JNIUtils.h
index 05cb5334..4c966978 100644
--- a/src/android/app/src/main/cpp/JNIUtils.h
+++ b/src/android/app/src/main/cpp/JNIUtils.h
@@ -13,6 +13,8 @@ namespace JNIUtils
jobject createJavaStringArrayList(JNIEnv* env, const std::vector& stringList);
+ void handleNativeException(JNIEnv* env, const std::function& fn);
+
class ScopedJNIENV
{
public:
diff --git a/src/android/app/src/main/cpp/native-lib.cpp b/src/android/app/src/main/cpp/native-lib.cpp
index 8b69978e..cf5afd6f 100644
--- a/src/android/app/src/main/cpp/native-lib.cpp
+++ b/src/android/app/src/main/cpp/native-lib.cpp
@@ -16,7 +16,9 @@ extern "C" [[maybe_unused]] JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, [[mayb
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
Java_info_cemu_Cemu_NativeLibrary_setSurface(JNIEnv* env, [[maybe_unused]] jclass clazz, jobject surface, jboolean is_main_canvas)
{
- s_emulationState.setSurface(env, surface, is_main_canvas);
+ JNIUtils::handleNativeException(env, [&]() {
+ s_emulationState.setSurface(env, surface, is_main_canvas);
+ });
}
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
@@ -86,7 +88,9 @@ Java_info_cemu_Cemu_NativeLibrary_initializeEmulation([[maybe_unused]] JNIEnv* e
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
Java_info_cemu_Cemu_NativeLibrary_initializerRenderer(JNIEnv* env, [[maybe_unused]] jclass clazz, jobject testSurface)
{
- s_emulationState.initializeRenderer(env, testSurface);
+ JNIUtils::handleNativeException(env, [&]() {
+ s_emulationState.initializeRenderer(env, testSurface);
+ });
}
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
diff --git a/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationFragment.java b/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationFragment.java
index 29657d4a..4b79e576 100644
--- a/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationFragment.java
+++ b/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationFragment.java
@@ -1,7 +1,5 @@
package info.cemu.Cemu.emulation;
-import static androidx.core.app.ActivityCompat.finishAffinity;
-
import android.annotation.SuppressLint;
import android.content.res.Configuration;
import android.graphics.SurfaceTexture;
@@ -24,14 +22,13 @@ import androidx.appcompat.widget.PopupMenu;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
import info.cemu.Cemu.NativeLibrary;
import info.cemu.Cemu.R;
import info.cemu.Cemu.databinding.FragmentEmulationBinding;
import info.cemu.Cemu.input.SensorManager;
import info.cemu.Cemu.inputoverlay.InputOverlaySettingsProvider;
import info.cemu.Cemu.inputoverlay.InputOverlaySurfaceView;
+import info.cemu.Cemu.nativeinterface.NativeException;
@SuppressLint("ClickableViewAccessibility")
public class EmulationFragment extends Fragment implements PopupMenu.OnMenuItemClickListener {
@@ -71,7 +68,7 @@ public class EmulationFragment extends Fragment implements PopupMenu.OnMenuItemC
}
}
- private static class SurfaceHolderCallback implements SurfaceHolder.Callback {
+ private class SurfaceHolderCallback implements SurfaceHolder.Callback {
final boolean isMainCanvas;
boolean surfaceSet;
@@ -85,12 +82,16 @@ public class EmulationFragment extends Fragment implements PopupMenu.OnMenuItemC
@Override
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int format, int width, int height) {
- NativeLibrary.setSurfaceSize(width, height, isMainCanvas);
- if (surfaceSet) {
- return;
+ try {
+ NativeLibrary.setSurfaceSize(width, height, isMainCanvas);
+ if (surfaceSet) {
+ return;
+ }
+ NativeLibrary.setSurface(surfaceHolder.getSurface(), isMainCanvas);
+ surfaceSet = true;
+ } catch (NativeException exception) {
+ onEmulationError(getString(R.string.failed_create_surface_error, exception.getMessage()));
}
- surfaceSet = true;
- NativeLibrary.setSurface(surfaceHolder.getSurface(), isMainCanvas);
}
@Override
@@ -112,6 +113,7 @@ public class EmulationFragment extends Fragment implements PopupMenu.OnMenuItemC
private InputOverlaySurfaceView inputOverlaySurfaceView;
private SensorManager sensorManager;
private EmulationViewModel viewModel;
+ private boolean hasEmulationError;
public EmulationFragment(String launchPath) {
this.launchPath = launchPath;
@@ -229,14 +231,10 @@ public class EmulationFragment extends Fragment implements PopupMenu.OnMenuItemC
return false;
}
- @Override
- public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- viewModel = new ViewModelProvider(requireActivity()).get(EmulationViewModel.class);
- }
-
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ viewModel = new ViewModelProvider(requireActivity()).get(EmulationViewModel.class);
+
binding = FragmentEmulationBinding.inflate(inflater, container, false);
inputOverlaySurfaceView = binding.inputOverlay;
@@ -271,8 +269,12 @@ public class EmulationFragment extends Fragment implements PopupMenu.OnMenuItemC
inputOverlaySurfaceView.setVisibility(View.GONE);
}
SurfaceView mainCanvas = binding.mainCanvas;
-
- NativeLibrary.initializerRenderer(testSurface);
+ try {
+ NativeLibrary.initializerRenderer(testSurface);
+ } catch (NativeException exception) {
+ onEmulationError(getString(R.string.failed_initialize_renderer_error, exception.getMessage()));
+ return binding.getRoot();
+ }
var mainCanvasHolder = mainCanvas.getHolder();
mainCanvasHolder.addCallback(new SurfaceHolderCallback(true));
@@ -283,6 +285,8 @@ public class EmulationFragment extends Fragment implements PopupMenu.OnMenuItemC
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
+ if (hasEmulationError)
+ return;
if (!isGameRunning) {
isGameRunning = true;
startGame();
@@ -312,6 +316,7 @@ public class EmulationFragment extends Fragment implements PopupMenu.OnMenuItemC
}
private void onEmulationError(String errorMessage) {
+ hasEmulationError = true;
if (viewModel == null)
return;
viewModel.setEmulationError(new EmulationError(errorMessage));
diff --git a/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/NativeException.java b/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/NativeException.java
new file mode 100644
index 00000000..483c8459
--- /dev/null
+++ b/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/NativeException.java
@@ -0,0 +1,8 @@
+package info.cemu.Cemu.nativeinterface;
+
+@SuppressWarnings("unused")
+public final class NativeException extends RuntimeException {
+ public NativeException(String message) {
+ super(message);
+ }
+}
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 20d4b87c..d0fa0699 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -174,4 +174,6 @@
Could not decrypt title. Make sure that keys.txt contains the correct disc key for this title.
Could not decrypt title because title.tik is missing.
Unable to launch game because the base files were not found.
+ Failed creating surface: %1$s
+ Failed to initialize renderer: %1$s
\ No newline at end of file