瀏覽代碼

Added Performance Test app for Android (#438)

* Fixed colored screen at end of Android Unit Tests
* Ability to run Unit Tests in release mode on Android (by adding debug signature)
* Removed redundant statements in CMakeLists.txt
* Added generic function to convert configuration settings to a string
Jorrit Rouwe 2 年之前
父節點
當前提交
4837d55f6b

+ 51 - 0
Build/Android/PerformanceTest/build.gradle

@@ -0,0 +1,51 @@
+plugins {
+    id 'com.android.application'
+}
+
+android {
+    compileSdk 33
+    ndkVersion "25.0.8775105"
+
+    defaultConfig {
+        applicationId "com.joltphysics.performancetest"
+        minSdk 24
+        targetSdk 33
+        versionCode 1
+        versionName "1.0"
+        ndk.abiFilters 'arm64-v8a', 'x86_64'
+
+        externalNativeBuild {
+            cmake {
+                cppFlags '-std=c++17 -Wall -Werror -ffp-model=precise -ffp-contract=off -DJPH_PROFILE_ENABLED -DJPH_DEBUG_RENDERER'
+                arguments '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=c++_static', '-DCROSS_PLATFORM_DETERMINISTIC=ON'
+            }
+        }
+        signingConfig signingConfigs.debug
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    externalNativeBuild {
+        cmake {
+            path file('src/main/cpp/CMakeLists.txt')
+            version '3.22.1'
+        }
+    }
+
+    buildFeatures {
+        viewBinding true
+    }
+    namespace 'com.joltphysics.performancetest'
+}
+
+dependencies {
+}

+ 20 - 0
Build/Android/PerformanceTest/src/main/AndroidManifest.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <application
+        android:allowBackup="true"
+        android:label="Jolt Physics Performance Test"
+        android:supportsRtl="false"
+        android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
+        <activity
+            android:name="android.app.NativeActivity"
+            android:exported="true">
+            <meta-data android:name="android.app.lib_name" android:value="PerformanceTest"/>
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>

+ 20 - 0
Build/Android/PerformanceTest/src/main/cpp/CMakeLists.txt

@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.10.2)
+
+project("JoltPhysicsPerformanceTest")
+
+# Make sure we include the app glue sources
+set(APP_GLUE_DIR ${ANDROID_NDK}/sources/android/native_app_glue)
+include_directories(${APP_GLUE_DIR})
+
+# Set repository root
+set(PHYSICS_REPO_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../")
+
+# Make targets
+include(${PHYSICS_REPO_ROOT}/Jolt/Jolt.cmake)
+include(${PHYSICS_REPO_ROOT}/PerformanceTest/PerformanceTest.cmake)
+
+# Link shared native library
+set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
+add_library(PerformanceTest SHARED ${PERFORMANCE_TEST_SRC_FILES} ${APP_GLUE_DIR}/android_native_app_glue.c)
+target_include_directories(PerformanceTest PUBLIC Jolt ${JOLT_PHYSICS_ROOT} ${PERFORMANCE_TEST_ROOT})
+target_link_libraries(PerformanceTest Jolt android log)

+ 1 - 0
Build/Android/UnitTests/build.gradle

@@ -20,6 +20,7 @@ android {
                 arguments '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=c++_static'
             }
         }
+        signingConfig signingConfigs.debug
     }
 
     buildTypes {

+ 0 - 4
Build/Android/UnitTests/src/main/cpp/CMakeLists.txt

@@ -6,10 +6,6 @@ project("JoltPhysicsUnitTests")
 set(APP_GLUE_DIR ${ANDROID_NDK}/sources/android/native_app_glue)
 include_directories(${APP_GLUE_DIR})
 
-if(${ANDROID_ABI} STREQUAL "x86_64")
-	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mlzcnt -mpopcnt")
-endif()
-
 # Set repository root
 set(PHYSICS_REPO_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../")
 

+ 1 - 0
Build/Android/settings.gradle

@@ -7,3 +7,4 @@ dependencyResolutionManagement {
 }
 rootProject.name = "Jolt Physics"
 include ':UnitTests'
+include ':PerformanceTest'

+ 70 - 0
Jolt/ConfigurationString.h

@@ -0,0 +1,70 @@
+// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+JPH_NAMESPACE_BEGIN
+
+/// Construct a string that lists the most important configuration settings
+inline const char *GetConfigurationString()
+{
+	return JPH_IF_SINGLE_PRECISION_ELSE("Single", "Double") " precision "
+#if defined(JPH_CPU_X86)
+		"x86 "
+#elif defined(JPH_CPU_ARM)
+		"ARM "
+#elif defined(JPH_PLATFORM_WASM)
+		"WASM "
+#endif
+#if JPH_CPU_ADDRESS_BITS == 64
+		"64-bit "
+#elif JPH_CPU_ADDRESS_BITS == 32
+		"32-bit "
+#endif
+		"with instructions: "
+#ifdef JPH_USE_NEON
+		"NEON "
+#endif
+#ifdef JPH_USE_SSE
+		"SSE2 "
+#endif
+#ifdef JPH_USE_SSE4_1
+		"SSE4.1 "
+#endif
+#ifdef JPH_USE_SSE4_2
+		"SSE4.2 "
+#endif
+#ifdef JPH_USE_AVX
+		"AVX "
+#endif
+#ifdef JPH_USE_AVX2
+		"AVX2 "
+#endif
+#ifdef JPH_USE_AVX512
+		"AVX512 "
+#endif
+#ifdef JPH_USE_F16C
+		"F16C "
+#endif
+#ifdef JPH_USE_LZCNT
+		"LZCNT "
+#endif
+#ifdef JPH_USE_TZCNT
+		"TZCNT "
+#endif
+#ifdef JPH_USE_FMADD
+		"FMADD "
+#endif
+#ifdef JPH_CROSS_PLATFORM_DETERMINISTIC
+		"(Cross Platform Deterministic) "
+#endif
+#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED
+		"(FP Exceptions) "
+#endif
+#ifdef _DEBUG
+		"(Debug) "
+#endif
+		;
+}
+
+JPH_NAMESPACE_END

+ 54 - 24
PerformanceTest/PerformanceTest.cpp

@@ -3,6 +3,7 @@
 
 // Jolt includes
 #include <Jolt/Jolt.h>
+#include <Jolt/ConfigurationString.h>
 #include <Jolt/RegisterTypes.h>
 #include <Jolt/Core/Factory.h>
 #include <Jolt/Core/TempAllocator.h>
@@ -16,6 +17,10 @@
 	#include <Jolt/Renderer/DebugRendererRecorder.h>
 	#include <Jolt/Core/StreamWrapper.h>
 #endif // JPH_DEBUG_RENDERER
+#ifdef JPH_PLATFORM_ANDROID
+#include <android/log.h>
+#include <android_native_app_glue.h>
+#endif // JPH_PLATFORM_ANDROID
 
 // STL includes
 JPH_SUPPRESS_WARNINGS_STD_BEGIN
@@ -41,7 +46,7 @@ JPH_SUPPRESS_WARNINGS
 constexpr float cDeltaTime = 1.0f / 60.0f;
 
 static void TraceImpl(const char *inFMT, ...)
-{ 
+{
 	// Format the message
 	va_list list;
 	va_start(list, inFMT);
@@ -50,12 +55,19 @@ static void TraceImpl(const char *inFMT, ...)
 	va_end(list);
 
 	// Print to the TTY
+#ifndef JPH_PLATFORM_ANDROID
 	cout << buffer << endl;
+#else
+	__android_log_write(ANDROID_LOG_INFO, "Jolt", buffer);
+#endif
 }
 
 // Program entry point
 int main(int argc, char** argv)
 {
+	// Install callbacks
+	Trace = TraceImpl;
+
 	// Register allocation hook
 	RegisterDefaultAllocator();
 
@@ -87,7 +99,7 @@ int main(int argc, char** argv)
 				scene = unique_ptr<PerformanceTestScene>(new ConvexVsMeshScene);
 			else
 			{
-				cerr << "Invalid scene" << endl;
+				Trace("Invalid scene");
 				return 1;
 			}
 		}
@@ -105,10 +117,15 @@ int main(int argc, char** argv)
 				specified_quality = 1;
 			else
 			{
-				cerr << "Invalid quality" << endl;
+				Trace("Invalid quality");
 				return 1;
 			}
 		}
+		else if (strncmp(arg, "-t=max", 6) == 0)
+		{
+			// Default to number of threads on the system
+			specified_threads = thread::hardware_concurrency();
+		}
 		else if (strncmp(arg, "-t=", 3) == 0)
 		{
 			// Parse threads
@@ -152,26 +169,24 @@ int main(int argc, char** argv)
 		else if (strcmp(arg, "-h") == 0)
 		{
 			// Print usage
-			cerr << "Usage:" << endl
-				 << "-s=<scene>: Select scene (Ragdoll, ConvexVsMesh)" << endl
-				 << "-i=<num physics steps>: Number of physics steps to simulate (default 500)" << endl
-				 << "-q=<quality>: Test only with specified quality (Discrete, LinearCast)" << endl
-				 << "-t=<num threads>: Test only with N threads (default is to iterate over 1 .. num hardware threads)" << endl
-				 << "-p: Write out profiles" << endl
-				 << "-r: Record debug renderer output for JoltViewer" << endl
-				 << "-f: Record per frame timings" << endl
-				 << "-no_sleep: Disable sleeping" << endl
-				 << "-rs: Record state" << endl
-				 << "-vs: Validate state" << endl
-				 << "-validate_hash=<hash>: Validate hash (return 0 if successful, 1 if failed)" << endl
-				 << "-repeat=<num>: Repeat all tests <num> times" << endl;
+			Trace("Usage:\n"
+				  "-s=<scene>: Select scene (Ragdoll, ConvexVsMesh)\n"
+				  "-i=<num physics steps>: Number of physics steps to simulate (default 500)\n"
+				  "-q=<quality>: Test only with specified quality (Discrete, LinearCast)\n"
+				  "-t=<num threads>: Test only with N threads (default is to iterate over 1 .. num hardware threads)\n"
+				  "-t=max: Test with the number of threads available on the system\n"
+				  "-p: Write out profiles\n"
+				  "-r: Record debug renderer output for JoltViewer\n"
+				  "-f: Record per frame timings\n"
+				  "-no_sleep: Disable sleeping\n"
+				  "-rs: Record state\n"
+				  "-vs: Validate state\n"
+				  "-validate_hash=<hash>: Validate hash (return 0 if successful, 1 if failed)\n"
+				  "-repeat=<num>: Repeat all tests <num> times");
 			return 0;
 		}
 	}
 
-	// Install callbacks
-	Trace = TraceImpl;
-
 	// Create a factory
 	Factory::sInstance = new Factory();
 
@@ -187,8 +202,11 @@ int main(int argc, char** argv)
 	if (!scene->Load())
 		return 1;
 
+	// Show used instruction sets
+	Trace(GetConfigurationString());
+
 	// Output scene we're running
-	cout << "Running scene: " << scene->GetName() << endl;
+	Trace("Running scene: %s", scene->GetName());
 
 	// Create mapping table from object layer to broadphase layer
 	BPLayerInterfaceImpl broad_phase_layer_interface;
@@ -203,7 +221,7 @@ int main(int argc, char** argv)
 	JPH_PROFILE_START("Main");
 
 	// Trace header
-	cout << "Motion Quality, Thread Count, Steps / Second, Hash" << endl;
+	Trace("Motion Quality, Thread Count, Steps / Second, Hash");
 
 	// Repeat test
 	for (int r = 0; r < repeat; ++r)
@@ -263,7 +281,7 @@ int main(int argc, char** argv)
 
 				// A tag used to identify the test
 				String tag = ToLower(motion_quality_str) + "_th" + ConvertToString(num_threads + 1);
-					     
+
 			#ifdef JPH_DEBUG_RENDERER
 				// Open renderer output
 				ofstream renderer_file;
@@ -395,12 +413,12 @@ int main(int argc, char** argv)
 				scene->StopTest(physics_system);
 
 				// Trace stat line
-				cout << motion_quality_str << ", " << num_threads + 1 << ", " << double(max_iterations) / (1.0e-9 * total_duration.count()) << ", " << hash_str << endl;
+				Trace("%s, %d, %f, %s", motion_quality_str.c_str(), num_threads + 1, double(max_iterations) / (1.0e-9 * total_duration.count()), hash_str.c_str());
 
 				// Check hash code
 				if (validate_hash != nullptr && hash_str != validate_hash)
 				{
-					cout << "Fail hash validation. Was: " << hash_str << ", expected: " << validate_hash << endl;
+					Trace("Fail hash validation. Was: %s, expected: %s", hash_str.c_str(), validate_hash);
 					return 1;
 				}
 			}
@@ -420,3 +438,15 @@ int main(int argc, char** argv)
 
 	return 0;
 }
+
+#ifdef JPH_PLATFORM_ANDROID
+
+// Main entry point for android
+void android_main(struct android_app *ioApp)
+{
+	// Run the regular main function
+	const char *args[] = { "Unused", "-s=ConvexVsMesh", "-t=max" };
+	main(size(args), (char **)args);
+}
+
+#endif // JPH_PLATFORM_ANDROID

+ 17 - 42
UnitTests/UnitTestFramework.cpp

@@ -2,7 +2,7 @@
 // SPDX-License-Identifier: MIT
 
 #include <Jolt/Jolt.h>
-
+#include <Jolt/ConfigurationString.h>
 #include <Jolt/Core/FPException.h>
 #include <Jolt/Core/Factory.h>
 #include <Jolt/RegisterTypes.h>
@@ -175,47 +175,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine,
 int main(int argc, char** argv)
 {
 	// Show used instruction sets
-	std::cout << JPH_IF_SINGLE_PRECISION_ELSE("Single precision ", "Double precision ") << JPH_CPU_ADDRESS_BITS << "-bit build with instructions: ";
-#ifdef JPH_USE_NEON
-	std::cout << "NEON ";
-#endif
-#ifdef JPH_USE_SSE
-	std::cout << "SSE2 ";
-#endif
-#ifdef JPH_USE_SSE4_1
-	std::cout << "SSE4.1 ";
-#endif
-#ifdef JPH_USE_SSE4_2
-	std::cout << "SSE4.2 ";
-#endif
-#ifdef JPH_USE_AVX
-	std::cout << "AVX ";
-#endif
-#ifdef JPH_USE_AVX2
-	std::cout << "AVX2 ";
-#endif
-#ifdef JPH_USE_AVX512
-	std::cout << "AVX512 ";
-#endif
-#ifdef JPH_USE_F16C
-	std::cout << "F16C ";
-#endif
-#ifdef JPH_USE_LZCNT
-	std::cout << "LZCNT ";
-#endif
-#ifdef JPH_USE_TZCNT
-	std::cout << "TZCNT ";
-#endif
-#ifdef JPH_USE_FMADD
-	std::cout << "FMADD ";
-#endif
-#ifdef JPH_CROSS_PLATFORM_DETERMINISTIC
-	std::cout << "(Cross Platform Deterministic)";
-#endif
-#ifdef JPH_FLOATING_POINT_EXCEPTIONS_ENABLED
-	std::cout << "(FP Exceptions)";
-#endif
-	std::cout << std::endl;
+	std::cout << GetConfigurationString() << std::endl;
 
 	// Register allocation hook
 	RegisterDefaultAllocator();
@@ -290,6 +250,9 @@ DOCTEST_REGISTER_REPORTER("android_log", 0, LogReporter);
 
 void AndroidInitialize(android_app *inApp)
 {
+	// Log configuration
+	__android_log_write(ANDROID_LOG_INFO, "Jolt", GetConfigurationString());
+
 	// Register allocation hook
 	RegisterDefaultAllocator();
 
@@ -333,6 +296,18 @@ void AndroidInitialize(android_app *inApp)
 			break;
 		}
 
+        case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
+        {
+            uint16 color_u16 = (color.b >> 3) + ((color.g >> 2) << 5) + ((color.r >> 3) << 11);
+            for (int y = 0; y < buffer.height; ++y)
+            {
+                uint16 *dest = (uint16 *) ((uint8 *) buffer.bits + y * buffer.stride * sizeof(uint16));
+                for (int x = 0; x < buffer.width; ++x)
+                    *dest++ = color_u16;
+            }
+            break;
+        }
+
 		default:
 			// TODO implement
 			break;