Add Swappy & Pre-Transformed Swapchain
- Adds Swappy for Android for stable frame pacing - Implements pre-transformed Swapchain so that Godot's compositor is in charge of rotating the screen instead of Android's compositor (performance optimization for phones that don't have HW rotator) ============================ The work was performed by collaboration of TheForge and Google. I am merely splitting it up into smaller PRs and cleaning it up. Changes from original PR: - Removed "display/window/frame_pacing/android/target_frame_rate" option to use Engine::get_max_fps instead. - Target framerate can be changed at runtime using Engine::set_max_fps. - Swappy is enabled by default. - Added documentation. - enable_auto_swap setting is replaced with swappy_mode.
This commit is contained in:
@@ -95,6 +95,15 @@ def install_ndk_if_needed(env: "SConsEnvironment"):
|
||||
env["ANDROID_NDK_ROOT"] = get_android_ndk_root(env)
|
||||
|
||||
|
||||
def detect_swappy():
|
||||
archs = ["arm64-v8a", "armeabi-v7a", "x86", "x86_64"]
|
||||
has_swappy = True
|
||||
for arch in archs:
|
||||
if not os.path.isfile("thirdparty/swappy-frame-pacing/" + arch + "/libswappy_static.a"):
|
||||
has_swappy = False
|
||||
return has_swappy
|
||||
|
||||
|
||||
def configure(env: "SConsEnvironment"):
|
||||
# Validate arch.
|
||||
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
|
||||
@@ -176,19 +185,42 @@ def configure(env: "SConsEnvironment"):
|
||||
)
|
||||
)
|
||||
|
||||
has_swappy = detect_swappy()
|
||||
if not has_swappy:
|
||||
print_warning(
|
||||
"Swappy Frame Pacing not detected! It is strongly recommended you download it from https://github.com/darksylinc/godot-swappy/releases and extract it so that the following files can be found:\n"
|
||||
+ " thirdparty/swappy-frame-pacing/arm64-v8a/libswappy_static.a\n"
|
||||
+ " thirdparty/swappy-frame-pacing/armeabi-v7a/libswappy_static.a\n"
|
||||
+ " thirdparty/swappy-frame-pacing/x86/libswappy_static.a\n"
|
||||
+ " thirdparty/swappy-frame-pacing/x86_64/libswappy_static.a\n"
|
||||
+ "Without Swappy, Godot apps on Android will inevitable suffer stutter and struggle to keep consistent 30/60/90/120 fps. Though Swappy cannot guarantee your app will be stutter-free, not having Swappy will guarantee there will be stutter even on the best phones and the most simple of scenes."
|
||||
)
|
||||
if env["swappy"]:
|
||||
print_error("Use build option `swappy=no` to ignore missing Swappy dependency and build without it.")
|
||||
sys.exit(255)
|
||||
|
||||
if get_min_sdk_version(env["ndk_platform"]) >= 24:
|
||||
env.Append(CPPDEFINES=[("_FILE_OFFSET_BITS", 64)])
|
||||
|
||||
if env["arch"] == "x86_32":
|
||||
# The NDK adds this if targeting API < 24, so we can drop it when Godot targets it at least
|
||||
env.Append(CCFLAGS=["-mstackrealign"])
|
||||
if has_swappy:
|
||||
env.Append(LIBPATH=["../../thirdparty/swappy-frame-pacing/x86"])
|
||||
elif env["arch"] == "x86_64":
|
||||
if has_swappy:
|
||||
env.Append(LIBPATH=["../../thirdparty/swappy-frame-pacing/x86_64"])
|
||||
elif env["arch"] == "arm32":
|
||||
env.Append(CCFLAGS="-march=armv7-a -mfloat-abi=softfp".split())
|
||||
env.Append(CPPDEFINES=["__ARM_ARCH_7__", "__ARM_ARCH_7A__"])
|
||||
env.Append(CPPDEFINES=["__ARM_NEON__"])
|
||||
if has_swappy:
|
||||
env.Append(LIBPATH=["../../thirdparty/swappy-frame-pacing/armeabi-v7a"])
|
||||
elif env["arch"] == "arm64":
|
||||
env.Append(CCFLAGS=["-mfix-cortex-a53-835769"])
|
||||
env.Append(CPPDEFINES=["__ARM_ARCH_8A__"])
|
||||
if has_swappy:
|
||||
env.Append(LIBPATH=["../../thirdparty/swappy-frame-pacing/arm64-v8a"])
|
||||
|
||||
env.Append(CCFLAGS=["-ffp-contract=off"])
|
||||
|
||||
@@ -203,6 +235,9 @@ def configure(env: "SConsEnvironment"):
|
||||
|
||||
if env["vulkan"]:
|
||||
env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
|
||||
if has_swappy:
|
||||
env.Append(CPPDEFINES=["SWAPPY_FRAME_PACING_ENABLED"])
|
||||
env.Append(LIBS=["swappy_static"])
|
||||
if not env["use_volk"]:
|
||||
env.Append(LIBS=["vulkan"])
|
||||
|
||||
|
||||
@@ -216,6 +216,14 @@ DisplayServer::ScreenOrientation DisplayServerAndroid::screen_get_orientation(in
|
||||
return (ScreenOrientation)orientation;
|
||||
}
|
||||
|
||||
int DisplayServerAndroid::screen_get_internal_current_rotation(int p_screen) const {
|
||||
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
|
||||
ERR_FAIL_NULL_V(godot_io_java, 0);
|
||||
|
||||
const int rotation = godot_io_java->get_internal_current_screen_rotation();
|
||||
return rotation;
|
||||
}
|
||||
|
||||
int DisplayServerAndroid::get_screen_count() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -124,6 +124,7 @@ public:
|
||||
|
||||
virtual void screen_set_orientation(ScreenOrientation p_orientation, int p_screen = SCREEN_OF_MAIN_WINDOW) override;
|
||||
virtual ScreenOrientation screen_get_orientation(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual int screen_get_internal_current_rotation(int p_screen) const override;
|
||||
|
||||
virtual int get_screen_count() const override;
|
||||
virtual int get_primary_screen() const override;
|
||||
|
||||
@@ -47,6 +47,7 @@ import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.Surface;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
@@ -295,6 +296,28 @@ public class GodotIO {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
This function is used by DisplayServer::screen_get_internal_current_rotation (C++)
|
||||
and is used to implement a performance optimization in devices that do not offer
|
||||
a HW rotator.
|
||||
@return
|
||||
Rotation in degrees, in multiples of 90°
|
||||
*/
|
||||
public int getInternalCurrentScreenRotation() {
|
||||
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
|
||||
|
||||
switch (rotation) {
|
||||
case Surface.ROTATION_90:
|
||||
return 90;
|
||||
case Surface.ROTATION_180:
|
||||
return 180;
|
||||
case Surface.ROTATION_270:
|
||||
return 270;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void setEdit(GodotEditText _edit) {
|
||||
edit = _edit;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc
|
||||
_has_hardware_keyboard = p_env->GetMethodID(cls, "hasHardwareKeyboard", "()Z");
|
||||
_set_screen_orientation = p_env->GetMethodID(cls, "setScreenOrientation", "(I)V");
|
||||
_get_screen_orientation = p_env->GetMethodID(cls, "getScreenOrientation", "()I");
|
||||
_get_internal_current_screen_rotation = p_env->GetMethodID(cls, "getInternalCurrentScreenRotation", "()I");
|
||||
_get_system_dir = p_env->GetMethodID(cls, "getSystemDir", "(IZ)Ljava/lang/String;");
|
||||
}
|
||||
}
|
||||
@@ -267,6 +268,16 @@ int GodotIOJavaWrapper::get_screen_orientation() {
|
||||
}
|
||||
}
|
||||
|
||||
int GodotIOJavaWrapper::get_internal_current_screen_rotation() {
|
||||
if (_get_internal_current_screen_rotation) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
ERR_FAIL_NULL_V(env, 0);
|
||||
return env->CallIntMethod(godot_io_instance, _get_internal_current_screen_rotation);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
String GodotIOJavaWrapper::get_system_dir(int p_dir, bool p_shared_storage) {
|
||||
if (_get_system_dir) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
|
||||
@@ -61,6 +61,7 @@ private:
|
||||
jmethodID _has_hardware_keyboard = 0;
|
||||
jmethodID _set_screen_orientation = 0;
|
||||
jmethodID _get_screen_orientation = 0;
|
||||
jmethodID _get_internal_current_screen_rotation = 0;
|
||||
jmethodID _get_system_dir = 0;
|
||||
|
||||
public:
|
||||
@@ -88,6 +89,7 @@ public:
|
||||
void set_vk_height(int p_height);
|
||||
void set_screen_orientation(int p_orient);
|
||||
int get_screen_orientation();
|
||||
int get_internal_current_screen_rotation();
|
||||
String get_system_dir(int p_dir, bool p_shared_storage);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user