diff --git a/.gitignore b/.gitignore index 8697ce0f..567eaa20 100644 --- a/.gitignore +++ b/.gitignore @@ -4,35 +4,8 @@ # Ignore Gradle build output directory build -# IntelliJ IDEA (based on https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore#L2) -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf -.idea/misc.xml - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries -*.iml -.idea/modules.xml -.idea/compiler.xml -.idea/jarRepositories.xml - +build-root/.idea/* +!build-root/.idea/codeStyles/* /samples/mini-games/.import/ diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index e7e9d11d..00000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Default ignored files -/workspace.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 1bec35e5..00000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 79ee123c..00000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 246c3e89..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/build-root/build.gradle.kts b/build-root/build.gradle.kts new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/build-root/build.gradle.kts @@ -0,0 +1 @@ + diff --git a/build-root/gradle/wrapper/gradle-wrapper.jar b/build-root/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..62d4c053 Binary files /dev/null and b/build-root/gradle/wrapper/gradle-wrapper.jar differ diff --git a/build-root/gradle/wrapper/gradle-wrapper.properties b/build-root/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..ac33e994 --- /dev/null +++ b/build-root/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/build-root/gradlew b/build-root/gradlew new file mode 100755 index 00000000..fbd7c515 --- /dev/null +++ b/build-root/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/build-root/gradlew.bat b/build-root/gradlew.bat new file mode 100644 index 00000000..5093609d --- /dev/null +++ b/build-root/gradlew.bat @@ -0,0 +1,104 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/build-root/settings.gradle.kts b/build-root/settings.gradle.kts new file mode 100644 index 00000000..13181057 --- /dev/null +++ b/build-root/settings.gradle.kts @@ -0,0 +1,27 @@ +includeBuild("../") { + dependencySubstitution { + substitute(module("com.utopia-rise:godot-build-props")).with(project(":godot-build-props")) + substitute(module("com.utopia-rise:godot-annotation-processor")).with(project(":godot-annotation-processor")) + substitute(module("com.utopia-rise:godot-compiler-plugin")).with(project(":godot-compiler-plugin")) + substitute(module("com.utopia-rise:godot-compiler-plugin-common")).with(project(":godot-compiler-plugin-common")) + substitute(module("com.utopia-rise:godot-entry-generator")).with(project(":godot-entry-generator")) + substitute(module("com.utopia-rise:godot-gradle-plugin")).with(project(":godot-gradle-plugin")) + substitute(module("com.utopia-rise:godot-library")).with(project(":godot-library")) + // gradle doesn't support targeting a specific configuration (i.e shadow) when substituting a dependency in a composite build. + // composite-build-support depends on project(":godot-compiler-native-plugin", configuration = "shadow") + substitute(module("com.utopia-rise:godot-compiler-native-plugin")).with(project(":composite-build-support")) + } +} + +pluginManagement { + resolutionStrategy.eachPlugin { + when (requested.id.id) { + "com.utopia-rise.godot-kotlin" -> useModule("com.utopia-rise:godot-gradle-plugin:${requested.version}") + } + } +} + +includeBuild("../samples/3d-platformer") +includeBuild("../samples/mini-games") +includeBuild("../samples/benchmarks/bunnymark") +includeBuild("../samples/benchmarks/gamelife") diff --git a/samples/benchmarks/bunnymark/.gitignore b/samples/benchmarks/bunnymark/.gitignore new file mode 100644 index 00000000..fe2fe926 --- /dev/null +++ b/samples/benchmarks/bunnymark/.gitignore @@ -0,0 +1,63 @@ +.idea/ +# Created by https://www.toptal.com/developers/gitignore/api/godot,kotlin,gradle +# Edit at https://www.toptal.com/developers/gitignore?templates=godot,kotlin,gradle + +### Godot ### + +# Godot-specific ignores +.import/ +export.cfg +export_presets.cfg + +# Imported translations (automatically generated from CSV files) +*.translation + +# Mono-specific ignores +.mono/ +data_*/ + +### Kotlin ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Gradle ### +.gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +### Gradle Patch ### +**/build/ + +# End of https://www.toptal.com/developers/gitignore/api/godot,kotlin,gradle \ No newline at end of file diff --git a/samples/benchmarks/bunnymark/Benchmarker.gd b/samples/benchmarks/bunnymark/Benchmarker.gd new file mode 100644 index 00000000..4aa1a97a --- /dev/null +++ b/samples/benchmarks/bunnymark/Benchmarker.gd @@ -0,0 +1,119 @@ +extends Control + +var fps_update_interval = 1.0 +var elapsed_time = 0.0 +var fps_label = null +var benchmark_container = null +var benchmark_node = null +var output_path = "user://benchmark_results.json" +var arg_bench = "--bench=" +var arg_lang = "--lang=" + +# bunnymark +var bunnymark_target = 60.0 +var bunnymark_target_error = 0.5 +var benchmark_is_bunnymark = false +var bunnymark_update_interval = 2.0 +var stable_updates_required = 3 +var bunnymark_update_elapsed_time = 0.0 +var stable_updates = 0 + +var nativescript_languages = { + "kot": true +} + +export(String, "BunnymarkV2", "BunnymarkV1Sprites", "BunnymarkV1DrawTexture") var benchmark: String = "BunnymarkV2" +export(String, "gd", "kot") var language: String = "gd" + +func _ready(): + set_process(false) + fps_label = get_node("Panel/FPS") + benchmark_container = get_node("BenchmarkContainer") + + var args = OS.get_cmdline_args() + for arg in args: + if arg.substr(0, arg_bench.length()) == arg_bench: + benchmark = arg.split("=")[1] + elif arg.substr(0, arg_lang.length()) == arg_lang: + language = arg.split("=")[1] + + start_benchmark(benchmark, language) + +func _process(delta): + elapsed_time += delta + if elapsed_time >= fps_update_interval: + fps_label.text = "FPS: " + str(Engine.get_frames_per_second()) + elapsed_time = 0.0 + if benchmark_is_bunnymark: + update_bunnymark(delta) + +func start_benchmark(benchmark_name, language): + var language_extension = language + if nativescript_languages.has(language) and nativescript_languages[language]: + language_extension = "gdns" + var script_path = "res://benchmarks/" + benchmark_name + "/" + language + "/" + benchmark_name + "." + language_extension + benchmark_is_bunnymark = benchmark_name.begins_with("Bunnymark") + bunnymark_update_elapsed_time = bunnymark_update_interval + var script = load(script_path) + benchmark_node = Node2D.new() + benchmark_node.set_script(script) + benchmark_node.add_user_signal("benchmark_finished", ["output"]) + benchmark_node.connect("benchmark_finished", self, "benchmark_finished") + benchmark_container.add_child(benchmark_node) + if benchmark_node.has_method("add_bunny"): + set_process(true) + else: + benchmark_finished(0) + +func benchmark_finished(output): + print("benchmark output: ", output) + benchmark_container.remove_child(benchmark_node) + write_result(output) + get_tree().quit() + +func write_result(output): + print("written ", output) + var file = File.new() + file.open(output_path, File.READ) + var parse_result = JSON.parse(file.get_as_text()) + var benchmark_file = null + if parse_result.get_error() == 0: + benchmark_file = parse_result.get_result() + if benchmark_file == null or typeof(benchmark_file) != TYPE_DICTIONARY: + benchmark_file = { + "benchmark_results": {} + } + file.close() + benchmark_file["benchmark_results"][benchmark + "_" + language] = output + var dir = Directory.new() + dir.remove(output_path) + file = File.new() + file.open(output_path, File.WRITE) + benchmark_file["run_date"] = OS.get_datetime() + file.store_string(JSON.print(benchmark_file)) + +func update_bunnymark(delta): + bunnymark_update_elapsed_time += delta + if bunnymark_update_elapsed_time > bunnymark_update_interval: + var fps = Engine.get_frames_per_second() + var difference = fps - bunnymark_target + var bunny_difference = 0 + if difference > bunnymark_target_error: + bunny_difference = min(1000, max(1, floor(20 * difference))) + elif difference < -bunnymark_target_error: + bunny_difference = max(-1000, min(-1, -1*ceil(20 * difference))) + if abs(difference) < bunnymark_target_error: + stable_updates += 1 + if stable_updates == stable_updates_required: + benchmark_node.finish() + else: + if bunny_difference > 0: + for i in range(bunny_difference): + benchmark_node.add_bunny() + else: + for i in range(-1*bunny_difference): + benchmark_node.remove_bunny() + + stable_updates = 0 + + bunnymark_update_elapsed_time = 0.0 diff --git a/samples/benchmarks/bunnymark/Benchmarker.tscn b/samples/benchmarks/bunnymark/Benchmarker.tscn new file mode 100644 index 00000000..948638ce --- /dev/null +++ b/samples/benchmarks/bunnymark/Benchmarker.tscn @@ -0,0 +1,23 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://Benchmarker.gd" type="Script" id=1] + +[node name="Benchmarker" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} +language = "kot" + +[node name="BenchmarkContainer" type="Node2D" parent="."] + +[node name="Panel" type="Panel" parent="."] +margin_right = 83.0 +margin_bottom = 14.0 + +[node name="FPS" type="Label" parent="Panel"] +anchor_right = 1.0 +anchor_bottom = 1.0 +text = "FPS:" diff --git a/samples/benchmarks/bunnymark/LICENSE b/samples/benchmarks/bunnymark/LICENSE new file mode 100644 index 00000000..992a1071 --- /dev/null +++ b/samples/benchmarks/bunnymark/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Michael "Carter" Anderson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/samples/benchmarks/bunnymark/README.md b/samples/benchmarks/bunnymark/README.md new file mode 100644 index 00000000..abd8ec15 --- /dev/null +++ b/samples/benchmarks/bunnymark/README.md @@ -0,0 +1,54 @@ +# Games sample + +Go in the kotlin directory and run `gradlew build` to build this sample. +You can add a platform variable inside kotlin/gradle.properties (platform=windows/linux/macos/android/ios) +Open the project in Godot and inspect the root node. +You can choose between GdScript or Kotlin and the benchmark as well. + +![Godot Bunnymark](images/banner.png) + +Renders an increasing number of bunny sprites until a stable 60fps is hit. This is a decent test of real world usage as it combines Godot api usage with raw computation. + +## Benchmark Run - March 05, 2020 + +### BunnymarkV2 + +Attempts to draw as many sprites as possible using Sprite nodes. It calls GetChildren() to iterate over a list of Sprites and sets their positions. It also updates a Label's text once per frame. This test aims to be a better emulation of real world api usage than the V1 tests. + +| Language | Bunnies Rendered | +|----------------------|------------------| +| Kotlin | 35 | +| GDScript | 9600 | + +### BunnymarkV1 - DrawTexture + +Attempts to draw as many sprites to the screen as possible by drawing textures directly with VisualServer. This test focuses on compute / render performance and avoids making godot api calls. + +| Language | Bunnies Rendered | +|----------------------|------------------| +| Kotlin | 1103 | +| GDScript | 10986 | + +### BunnymarkV1 - Sprites + +Attempts to draw as many sprites to the screen as possible by adding Sprite nodes. This test focuses on compute / render performance and avoids making godot api calls. + +| Language | Bunnies Rendered | +|----------------------|------------------| +| Kotlin | 1564 | +| GDScript | 9015 | + +### Hardware: + +* CPU: Intel i5 4690k 3.9GHz +* GPU: Nvidia GeForce GTX 970 +* RAM: 16GB DDR3 + +### Build Info: +* OS: Windows 10 +* Official Godot 3.2 release +* Kotlin 1.3 + + +Original work: [@Carter Anderson](https://github.com/cart/godot3-bunnymark). Thanks to him. + diff --git a/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1DrawTexture/Description.md b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1DrawTexture/Description.md new file mode 100644 index 00000000..3323dab1 --- /dev/null +++ b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1DrawTexture/Description.md @@ -0,0 +1,3 @@ +# BunnymarkV1DrawTexture + +Attempts to draw as many sprites to the screen as possible by drawing textures directly with VisualServer. This test focuses on compute / render performance and avoids making godot api calls. \ No newline at end of file diff --git a/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1DrawTexture/gd/BunnymarkV1DrawTexture.gd b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1DrawTexture/gd/BunnymarkV1DrawTexture.gd new file mode 100644 index 00000000..4be579bf --- /dev/null +++ b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1DrawTexture/gd/BunnymarkV1DrawTexture.gd @@ -0,0 +1,57 @@ +extends Node2D + +var bunnies = [] +var bunny_texture = load("res://images/godot_bunny.png") +var grav = 500 +var screen_size + +func _draw(): + for bunny in bunnies: + draw_texture(bunny_texture, bunny[0]) + +func _process(delta): + screen_size = get_viewport_rect().size + + for bunny in bunnies: + var pos = bunny[0] + var newPosition = bunny[1] + + pos.x += newPosition.x * delta + pos.y += newPosition.y * delta + + newPosition.y += grav * delta + + if pos.x > screen_size.x: + newPosition.x *= -1 + pos.x = screen_size.x + + if pos.x < 0: + newPosition.x *= -1 + pos.x = 0 + + if pos.y > screen_size.y: + pos.y = screen_size.y + if randf() > 0.5: + newPosition.y = -(randi() % 1100 + 50) + else: + newPosition.y *= -0.85 + + if pos.y < 0: + newPosition.y = 0 + pos.y = 0 + + bunny[0] = pos + bunny[1] = newPosition + update() + +func add_bunny(): + bunnies.append([Vector2(screen_size.x / 2, screen_size.y / 2), Vector2(randi() % 200 + 50, randi() % 200 + 50)]) + +func remove_bunny(): + if bunnies.size() == 0: + return + + bunnies.pop_back() + +func finish(): + emit_signal("benchmark_finished", bunnies.size()) \ No newline at end of file diff --git a/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1DrawTexture/kot/BunnymarkV1DrawTexture.gdns b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1DrawTexture/kot/BunnymarkV1DrawTexture.gdns new file mode 100644 index 00000000..f544c461 --- /dev/null +++ b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1DrawTexture/kot/BunnymarkV1DrawTexture.gdns @@ -0,0 +1,8 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://bunnymark.gdnlib" type="GDNativeLibrary" id=1] + +[resource] +resource_name = "BunnymarkV1DrawTexture" +class_name = "godot.samples.benchmark.bunnymark.BunnymarkV1DrawTexture" +library = ExtResource( 1 ) \ No newline at end of file diff --git a/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1Sprites/Description.md b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1Sprites/Description.md new file mode 100644 index 00000000..1763fde5 --- /dev/null +++ b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1Sprites/Description.md @@ -0,0 +1,3 @@ +# BunnymarkV1DrawTexture + +Attempts to draw as many sprites to the screen as possible by adding Sprite nodes. This test focuses on compute / render performance and avoids making godot api calls. \ No newline at end of file diff --git a/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1Sprites/gd/BunnymarkV1Sprites.gd b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1Sprites/gd/BunnymarkV1Sprites.gd new file mode 100644 index 00000000..5a5010ee --- /dev/null +++ b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1Sprites/gd/BunnymarkV1Sprites.gd @@ -0,0 +1,57 @@ +extends Node2D + +var bunnies = [] +var grav = 500 +var bunny_texture = load("res://images/godot_bunny.png") +var screen_size + +func _process(delta): + screen_size = get_viewport_rect().size + + for bunny in bunnies: + var pos = bunny[0].position + var newPosition = bunny[1] + + pos.x += newPosition.x * delta + pos.y += newPosition.y * delta + + newPosition.y += grav * delta + + if pos.x > screen_size.x: + newPosition.x *= -1 + pos.x = screen_size.x + + if pos.x < 0: + newPosition.x *= -1 + pos.x = 0 + + if pos.y > screen_size.y: + pos.y = screen_size.y + if randf() > 0.5: + newPosition.y = -(randi() % 1100 + 50) + else: + newPosition.y *= -0.85 + + if pos.y < 0: + newPosition.y = 0 + pos.y = 0 + + bunny[0].position = pos + bunny[1] = newPosition + +func add_bunny(): + var bunny = Sprite.new() + bunny.set_texture(bunny_texture) + add_child(bunny) + bunny.position = Vector2(screen_size.x / 2, screen_size.y / 2) + bunnies.append([bunny, Vector2(randi() % 200 + 50, randi() % 200 + 50)]) + +func remove_bunny(): + if bunnies.size() == 0: + return + var bunny = bunnies[bunnies.size() - 1] + remove_child(bunny[0]) + bunnies.pop_back() + +func finish(): + emit_signal("benchmark_finished", bunnies.size()) \ No newline at end of file diff --git a/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1Sprites/kot/BunnymarkV1Sprites.gdns b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1Sprites/kot/BunnymarkV1Sprites.gdns new file mode 100644 index 00000000..ef015c08 --- /dev/null +++ b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV1Sprites/kot/BunnymarkV1Sprites.gdns @@ -0,0 +1,8 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://bunnymark.gdnlib" type="GDNativeLibrary" id=1] + +[resource] +resource_name = "BunnymarkV1Sprites" +class_name = "godot.samples.benchmark.bunnymark.BunnymarkV1Sprites" +library = ExtResource( 1 ) \ No newline at end of file diff --git a/samples/benchmarks/bunnymark/benchmarks/BunnymarkV2/Description.md b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV2/Description.md new file mode 100644 index 00000000..f42136f1 --- /dev/null +++ b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV2/Description.md @@ -0,0 +1,3 @@ +# BunnymarkV2 + +Attempts to draw as many sprites as possible using Sprite nodes. It calls GetChildren() to iterate over a list of Sprites and sets their positions. It also updates a Label's text once per frame. This test aims to be a better emulation of real world api usage than the V1 tests. \ No newline at end of file diff --git a/samples/benchmarks/bunnymark/benchmarks/BunnymarkV2/gd/BunnymarkV2.gd b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV2/gd/BunnymarkV2.gd new file mode 100644 index 00000000..feb4215a --- /dev/null +++ b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV2/gd/BunnymarkV2.gd @@ -0,0 +1,69 @@ +extends Node2D + +var grav = 500 +var bunny_texture = load("res://images/godot_bunny.png") +var bunny_speeds = [] +var label = Label.new() +var bunnies = Node2D.new() +var screen_size + +func _ready(): + add_child(bunnies) + + label.rect_position = Vector2(0, 20) + add_child(label) + +func _process(delta): + screen_size = get_viewport_rect().size + label.text = "Bunnies: " + str(bunnies.get_child_count()) + + var bunny_children = bunnies.get_children() + for i in range(0, bunny_children.size()): + var bunny = bunny_children[i] + var pos = bunny.position + var speed = bunny_speeds[i] + + pos.x += speed.x * delta + pos.y += speed.y * delta + + speed.y += grav * delta + + if pos.x > screen_size.x: + speed.x *= -1 + pos.x = screen_size.x + + if pos.x < 0: + speed.x *= -1 + pos.x = 0 + + if pos.y > screen_size.y: + pos.y = screen_size.y + if randf() > 0.5: + speed.y = -(randi() % 1100 + 50) + else: + speed.y *= -0.85 + + if pos.y < 0: + speed.y = 0 + pos.y = 0 + + bunny.position = pos + bunny_speeds[i] = speed + +func add_bunny(): + var bunny = Sprite.new() + bunny.set_texture(bunny_texture) + bunnies.add_child(bunny) + bunny.position = Vector2(screen_size.x / 2, screen_size.y / 2) + bunny_speeds.push_back(Vector2(randi() % 200 + 50, randi() % 200 + 50)) + +func remove_bunny(): + var child_count = bunnies.get_child_count() + if child_count == 0: + return + var bunny = bunnies.get_child(child_count - 1) + bunny_speeds.pop_back() + bunnies.remove_child(bunny) + +func finish(): + emit_signal("benchmark_finished", bunnies.get_child_count()) \ No newline at end of file diff --git a/samples/benchmarks/bunnymark/benchmarks/BunnymarkV2/kot/BunnymarkV2.gdns b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV2/kot/BunnymarkV2.gdns new file mode 100644 index 00000000..f2633c45 --- /dev/null +++ b/samples/benchmarks/bunnymark/benchmarks/BunnymarkV2/kot/BunnymarkV2.gdns @@ -0,0 +1,8 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://bunnymark.gdnlib" type="GDNativeLibrary" id=1] + +[resource] +resource_name = "BunnymarkV2" +class_name = "godot.samples.benchmark.bunnymark.BunnymarkV2" +library = ExtResource( 1 ) \ No newline at end of file diff --git a/samples/benchmarks/bunnymark/build.gradle.kts b/samples/benchmarks/bunnymark/build.gradle.kts new file mode 100644 index 00000000..f886424c --- /dev/null +++ b/samples/benchmarks/bunnymark/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + kotlin("multiplatform") version "1.3.72" + id("com.utopia-rise.godot-kotlin") version "0.1.0-3.2" +} + +repositories { + jcenter() +} + + +godot { + debug.set(true) + defaultPlatforms() +} diff --git a/samples/benchmarks/bunnymark/bunnymark.gdnlib b/samples/benchmarks/bunnymark/bunnymark.gdnlib new file mode 100644 index 00000000..63ebef5a --- /dev/null +++ b/samples/benchmarks/bunnymark/bunnymark.gdnlib @@ -0,0 +1,18 @@ +[entry] + +X11.64="res://build/bin/linuxX64/debugShared/libbunnymark.so" +Windows.64="res://build/bin/windowsX64/debugShared/bunnymark.dll" +OSX.64="res://build/bin/macosX64/debugShared/libbunnymark.dylib" + +[dependencies] + +X11.64=[ ] +Windows.64=[ ] +OSX.64=[ ] + +[general] + +singleton=false +load_once=true +symbol_prefix="godot_" +reloadable=true diff --git a/samples/benchmarks/bunnymark/gradle.properties b/samples/benchmarks/bunnymark/gradle.properties new file mode 100644 index 00000000..659b74c0 --- /dev/null +++ b/samples/benchmarks/bunnymark/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx3G \ No newline at end of file diff --git a/samples/benchmarks/bunnymark/gradle/wrapper/gradle-wrapper.jar b/samples/benchmarks/bunnymark/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..f3d88b1c Binary files /dev/null and b/samples/benchmarks/bunnymark/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/benchmarks/bunnymark/gradle/wrapper/gradle-wrapper.properties b/samples/benchmarks/bunnymark/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..ac33e994 --- /dev/null +++ b/samples/benchmarks/bunnymark/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/samples/benchmarks/bunnymark/gradlew b/samples/benchmarks/bunnymark/gradlew new file mode 100644 index 00000000..2fe81a7d --- /dev/null +++ b/samples/benchmarks/bunnymark/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/samples/benchmarks/bunnymark/gradlew.bat b/samples/benchmarks/bunnymark/gradlew.bat new file mode 100644 index 00000000..62bd9b9c --- /dev/null +++ b/samples/benchmarks/bunnymark/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/samples/benchmarks/bunnymark/icon.png b/samples/benchmarks/bunnymark/icon.png new file mode 100644 index 00000000..6e343676 Binary files /dev/null and b/samples/benchmarks/bunnymark/icon.png differ diff --git a/samples/benchmarks/bunnymark/images/banner.png b/samples/benchmarks/bunnymark/images/banner.png new file mode 100644 index 00000000..9037506c Binary files /dev/null and b/samples/benchmarks/bunnymark/images/banner.png differ diff --git a/samples/benchmarks/bunnymark/images/bunny.svg b/samples/benchmarks/bunnymark/images/bunny.svg new file mode 100644 index 00000000..72e6a986 --- /dev/null +++ b/samples/benchmarks/bunnymark/images/bunny.svg @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Godot BunnyMark + + diff --git a/samples/benchmarks/bunnymark/images/godot_bunny.png b/samples/benchmarks/bunnymark/images/godot_bunny.png new file mode 100644 index 00000000..8cafbf9a Binary files /dev/null and b/samples/benchmarks/bunnymark/images/godot_bunny.png differ diff --git a/samples/benchmarks/bunnymark/project.godot b/samples/benchmarks/bunnymark/project.godot new file mode 100644 index 00000000..23b9f1ed --- /dev/null +++ b/samples/benchmarks/bunnymark/project.godot @@ -0,0 +1,32 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=4 + +_global_script_classes=[ ] +_global_script_class_icons={ + +} + +[application] + +config/name="Bunnymark" +run/main_scene="res://Benchmarker.tscn" +config/icon="res://icon.png" + +[display] + +window/vsync/use_vsync=false + +[gdnative] + +singletons=[ ] + +[rendering] + +environment/default_environment="res://default_env.tres" diff --git a/samples/benchmarks/bunnymark/settings.gradle.kts b/samples/benchmarks/bunnymark/settings.gradle.kts new file mode 100644 index 00000000..7f3666f5 --- /dev/null +++ b/samples/benchmarks/bunnymark/settings.gradle.kts @@ -0,0 +1,24 @@ +// The following is used for configuring composite builds, which we use for developing this binding. +// Normally as a user, you should not need it. +includeBuild("../../../") { + dependencySubstitution { + substitute(module("com.utopia-rise:godot-build-props")).with(project(":godot-build-props")) + substitute(module("com.utopia-rise:godot-annotation-processor")).with(project(":godot-annotation-processor")) + substitute(module("com.utopia-rise:godot-compiler-plugin")).with(project(":godot-compiler-plugin")) + substitute(module("com.utopia-rise:godot-compiler-plugin-common")).with(project(":godot-compiler-plugin-common")) + substitute(module("com.utopia-rise:godot-entry-generator")).with(project(":godot-entry-generator")) + substitute(module("com.utopia-rise:godot-gradle-plugin")).with(project(":godot-gradle-plugin")) + substitute(module("com.utopia-rise:godot-library")).with(project(":godot-library")) + // gradle doesn't support targeting a specific configuration (i.e shadow) when substituting a dependency in a composite build. + // composite-build-support depends on project(":godot-compiler-native-plugin", configuration = "shadow") + substitute(module("com.utopia-rise:godot-compiler-native-plugin")).with(project(":composite-build-support")) + } +} + +pluginManagement { + resolutionStrategy.eachPlugin { + when (requested.id.id) { + "com.utopia-rise.godot-kotlin" -> useModule("com.utopia-rise:godot-gradle-plugin:${requested.version}") + } + } +} diff --git a/samples/benchmarks/bunnymark/src/godotMain/kotlin/godot/samples/benchmark/bunnymark/BunnymarkV1DrawTexture.kt b/samples/benchmarks/bunnymark/src/godotMain/kotlin/godot/samples/benchmark/bunnymark/BunnymarkV1DrawTexture.kt new file mode 100644 index 00000000..4eee8f3e --- /dev/null +++ b/samples/benchmarks/bunnymark/src/godotMain/kotlin/godot/samples/benchmark/bunnymark/BunnymarkV1DrawTexture.kt @@ -0,0 +1,91 @@ +package godot.samples.benchmark.bunnymark + +@RegisterClass("benchmarks/BunnymarkV1DrawTexture/kot") +class BunnymarkV1DrawTexture : Node2D() { + + data class Bunny(var position: Vector2, var speed: Vector2) + + private var bunnies = ArrayList() + private var gravity = 500 + private var bunnyTexture = Texture from ResourceLoader.load("res://images/godot_bunny.png") + private val randomNumberGenerator = RandomNumberGenerator() + + lateinit var screenSize: Vector2 + + @RegisterFunction + override fun _ready() { + randomNumberGenerator.randomize() + } + + @RegisterFunction + override fun _draw() { + for (bunny in bunnies) { + drawTexture(bunnyTexture, bunny.position) + } + } + + @RegisterFunction + override fun _process(delta: Double) { + screenSize = getViewportRect().size + + for (bunny in bunnies) { + val pos = bunny.position + val speed = bunny.speed + + pos.x += speed.x * delta + pos.y += speed.y * delta + + speed.y += gravity * delta + + if (pos.x > screenSize.x) { + speed.x *= -1 + pos.x = screenSize.x + } + + if (pos.x < 0) { + speed.x *= -1.0 + pos.x = 0.0 + } + + if (pos.y > screenSize.y) { + pos.y = screenSize.y + if (randomNumberGenerator.randf() > 0.5) { + speed.y = -(randomNumberGenerator.randi() % 1100 + 50).toDouble() + } else { + speed.y *= -0.85 + } + } + + + + if (pos.y < 0) { + speed.y = 0.0 + pos.y = 0.0 + } + + bunny.position = pos + bunny.speed = speed + } + update() + } + + @RegisterFunction + fun add_bunny() { + bunnies.add(Bunny( + Vector2(screenSize.x / 2, screenSize.y / 2), + Vector2(randomNumberGenerator.randi() % 200 + 50, randomNumberGenerator.randi() % 200 + 50) + + )) + } + + @RegisterFunction + fun remove_bunny() { + if (bunnies.size == 0) return + bunnies.removeAt(bunnies.size - 1) + } + + @RegisterFunction + fun finish() { + emitSignal("benchmark_finished", bunnies.size) + } +} diff --git a/samples/benchmarks/bunnymark/src/godotMain/kotlin/godot/samples/benchmark/bunnymark/BunnymarkV1Sprites.kt b/samples/benchmarks/bunnymark/src/godotMain/kotlin/godot/samples/benchmark/bunnymark/BunnymarkV1Sprites.kt new file mode 100644 index 00000000..1acce884 --- /dev/null +++ b/samples/benchmarks/bunnymark/src/godotMain/kotlin/godot/samples/benchmark/bunnymark/BunnymarkV1Sprites.kt @@ -0,0 +1,90 @@ +package godot.samples.benchmark.bunnymark + +import godot.core.* +import godot.* +import org.godotengine.kotlin.annotation.RegisterClass +import org.godotengine.kotlin.annotation.RegisterFunction + +@RegisterClass("benchmarks/BunnymarkV1Sprites/kot") +class BunnymarkV1Sprites : Node2D() { + + private data class Bunny(var sprite: Sprite, var speed: Vector2) + + private var bunnies = ArrayList() + private var gravity = 500 + private var bunnyTexture = Texture from ResourceLoader.load("res://images/godot_bunny.png") + private val randomNumberGenerator = RandomNumberGenerator() + + lateinit var screenSize: Vector2 + + @RegisterFunction + override fun _process(delta: Double) { + screenSize = getViewportRect().size + + for (bunny in bunnies) { + val pos = bunny.sprite.position + val speed = bunny.speed + + pos.x += speed.x * delta + pos.y += speed.y * delta + + speed.y += gravity * delta + + if (pos.x > screenSize.x) { + speed.x *= -1 + pos.x = screenSize.x + } + + if (pos.x < 0) { + speed.x *= -1.0 + pos.x = 0.0 + } + + if (pos.y > screenSize.y) { + pos.y = screenSize.y + if (randomNumberGenerator.randf() > 0.5) { + speed.y = -(randomNumberGenerator.randi() % 1100 + 50).toDouble() + } else { + speed.y *= -0.85 + } + } + + + + if (pos.y < 0) { + speed.y = 0.0 + pos.y = 0.0 + } + + bunny.sprite.position = pos + bunny.speed = speed + } + } + + @RegisterFunction + fun add_bunny() { + val bunny = Sprite() + bunny.setTexture(bunnyTexture) + addChild(bunny) + bunny.position = Vector2(screenSize.x / 2, screenSize.y / 2) + bunnies.add( + Bunny( + bunny, + Vector2(randomNumberGenerator.randi() % 200 + 50, randomNumberGenerator.randi() % 200 + 50) + ) + ) + } + + @RegisterFunction + fun remove_bunny() { + if (bunnies.size == 0) return + val bunny = bunnies[bunnies.size - 1] + removeChild(bunny.sprite) + bunnies.removeAt(bunnies.size - 1) + } + + @RegisterFunction + fun finish() { + emitSignal("benchmark_finished", bunnies.size) + } +} \ No newline at end of file diff --git a/samples/benchmarks/bunnymark/src/godotMain/kotlin/godot/samples/benchmark/bunnymark/BunnymarkV2.kt b/samples/benchmarks/bunnymark/src/godotMain/kotlin/godot/samples/benchmark/bunnymark/BunnymarkV2.kt new file mode 100644 index 00000000..11f0e587 --- /dev/null +++ b/samples/benchmarks/bunnymark/src/godotMain/kotlin/godot/samples/benchmark/bunnymark/BunnymarkV2.kt @@ -0,0 +1,97 @@ +package godot.samples.benchmark.bunnymark + +import godot.core.* +import godot.* +import org.godotengine.kotlin.annotation.RegisterClass +import org.godotengine.kotlin.annotation.RegisterFunction + +@RegisterClass("benchmarks/BunnymarkV2/kot") +class BunnymarkV2 : Node2D() { + + + private var gravity = 500 + private var bunnySpeeds = ArrayList() + private var label = Label() + private var bunnies = Node2D() + private var bunnyTexture = Texture from ResourceLoader.load("res://images/godot_bunny.png") + private val randomNumberGenerator = RandomNumberGenerator() + + lateinit var screenSize: Vector2 + + @RegisterFunction + override fun _ready() { + addChild(bunnies) + label.setPosition(Vector2(0, 20)) + addChild(label) + } + + @RegisterFunction + override fun _process(delta: Double) { + screenSize = getViewportRect().size + label.text = "Bunnies: " + bunnies.getChildCount().toString() + + val bunny_children = bunnies.getChildren() + for (i in 0 until bunny_children.size()) { + val bunny = Sprite from bunny_children[i]!! + val pos = bunny.position + val speed = bunnySpeeds[i] + + pos.x += speed.x * delta + pos.y += speed.y * delta + + speed.y += gravity * delta + + if (pos.x > screenSize.x) { + speed.x *= -1 + pos.x = screenSize.x + } + + if (pos.x < 0) { + speed.x *= -1.0 + pos.x = 0.0 + } + + if (pos.y > screenSize.y) { + pos.y = screenSize.y + if (randomNumberGenerator.randf() > 0.5) { + speed.y = -(randomNumberGenerator.randi() % 1100 + 50).toDouble() + } else { + speed.y *= -0.85 + } + } + + if (pos.y < 0) { + speed.y = 0.0 + pos.y = 0.0 + } + + bunny.position = pos + bunnySpeeds[i] = speed + } + } + + @RegisterFunction + fun add_bunny() { + val bunny = Sprite() + bunny.setTexture(bunnyTexture) + bunnies.addChild(bunny) + bunny.position = Vector2(screenSize.x / 2, screenSize.y / 2) + bunnySpeeds.add( + Vector2(randomNumberGenerator.randi() % 200 + 50, randomNumberGenerator.randi() % 200 + 50) + ) + } + + @RegisterFunction + fun remove_bunny() { + val child_count = bunnies.getChildCount() + if (child_count == 0L) return + val bunny = bunnies.getChild(child_count - 1) + bunnies.removeChild(bunny) + bunnySpeeds.removeAt(child_count.toInt() - 1) + } + + @RegisterFunction + fun finish() { + emitSignal("benchmark_finished", bunnySpeeds.size) + } +} \ No newline at end of file diff --git a/samples/benchmarks/gamelife/.gitignore b/samples/benchmarks/gamelife/.gitignore new file mode 100644 index 00000000..fe2fe926 --- /dev/null +++ b/samples/benchmarks/gamelife/.gitignore @@ -0,0 +1,63 @@ +.idea/ +# Created by https://www.toptal.com/developers/gitignore/api/godot,kotlin,gradle +# Edit at https://www.toptal.com/developers/gitignore?templates=godot,kotlin,gradle + +### Godot ### + +# Godot-specific ignores +.import/ +export.cfg +export_presets.cfg + +# Imported translations (automatically generated from CSV files) +*.translation + +# Mono-specific ignores +.mono/ +data_*/ + +### Kotlin ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Gradle ### +.gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +### Gradle Patch ### +**/build/ + +# End of https://www.toptal.com/developers/gitignore/api/godot,kotlin,gradle \ No newline at end of file diff --git a/samples/benchmarks/gamelife/LICENSE b/samples/benchmarks/gamelife/LICENSE new file mode 100644 index 00000000..af20dbd5 --- /dev/null +++ b/samples/benchmarks/gamelife/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Marcin Zawiejski + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/samples/benchmarks/gamelife/README.md b/samples/benchmarks/gamelife/README.md new file mode 100644 index 00000000..703fd136 --- /dev/null +++ b/samples/benchmarks/gamelife/README.md @@ -0,0 +1,37 @@ +# Games sample + +Go in the kotlin directory and run `gradlew build` to build this sample. +You can add a platform variable inside kotlin/gradle.properties (platform=windows/linux/macos/android/ios) +Open the project in Godot and inspect the root node. +You can choose between GdScript or Kotlin, the size of the grid and the length of the benchmark (Number of cycles). + + +## Benchmark Run - March 05, 2020 + +### Game of life + +A Game of life benchmark where each cell is a 3D Cube. Their colors are updated based on their state. +The default grid size is 50x50 = 2500 cells. +Each cell checks the state of its surrounding (8 others cells) to update its own. 2500 x 8 = 20000 checks each cycle. +The benchmark measure the time it takes for the script to complete 1000 cycles. + +| Language | Elapsed seconds | +|----------------------|-----------------------| +| Kotlin | 458 | +| GDScript | 13 | + + +### Hardware: + +* CPU: Intel i5 4690k 3.9GHz +* GPU: Nvidia GeForce GTX 970 +* RAM: 16GB DDR3 + +### Build Info: +* OS: Windows 10 +* Official Godot 3.2 release +* Kotlin 1.3 + + +Original work: [@Marcin Zawiejski](https://github.com/dragmz/gdlife). Thanks to him. + diff --git a/samples/benchmarks/gamelife/build.gradle.kts b/samples/benchmarks/gamelife/build.gradle.kts new file mode 100644 index 00000000..f886424c --- /dev/null +++ b/samples/benchmarks/gamelife/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + kotlin("multiplatform") version "1.3.72" + id("com.utopia-rise.godot-kotlin") version "0.1.0-3.2" +} + +repositories { + jcenter() +} + + +godot { + debug.set(true) + defaultPlatforms() +} diff --git a/samples/benchmarks/gamelife/default_env.tres b/samples/benchmarks/gamelife/default_env.tres new file mode 100644 index 00000000..20207a4a --- /dev/null +++ b/samples/benchmarks/gamelife/default_env.tres @@ -0,0 +1,7 @@ +[gd_resource type="Environment" load_steps=2 format=2] + +[sub_resource type="ProceduralSky" id=1] + +[resource] +background_mode = 2 +background_sky = SubResource( 1 ) diff --git a/samples/benchmarks/gamelife/game.tscn b/samples/benchmarks/gamelife/game.tscn new file mode 100644 index 00000000..951a7ec4 --- /dev/null +++ b/samples/benchmarks/gamelife/game.tscn @@ -0,0 +1,19 @@ +[gd_scene load_steps=4 format=2] + +[sub_resource type="SpatialMaterial" id=1] +vertex_color_use_as_albedo = true + +[sub_resource type="CubeMesh" id=2] +material = SubResource( 1 ) +size = Vector3( 1, 1, 1 ) + +[sub_resource type="MultiMesh" id=3] +color_format = 2 +transform_format = 1 +instance_count = 1 +mesh = SubResource( 2 ) +transform_array = PoolVector3Array( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 ) +color_array = PoolColorArray( 1, 1, 1, 1 ) + +[node name="game" type="MultiMeshInstance"] +multimesh = SubResource( 3 ) diff --git a/samples/benchmarks/gamelife/gameoflife.gdnlib b/samples/benchmarks/gamelife/gameoflife.gdnlib new file mode 100644 index 00000000..bb053485 --- /dev/null +++ b/samples/benchmarks/gamelife/gameoflife.gdnlib @@ -0,0 +1,19 @@ +[general] + +singleton=false +load_once=true +symbol_prefix="godot_" +reloadable=true + +[entry] + +OSX.64="res://kotlin/build/bin/macos/debugShared/libkotlin.dylib" +Windows.64="res://kotlin/build/bin/windows/debugShared/kotlin.dll" +X11.64="res://kotlin/build/bin/linux/debugShared/libkotlin.so" +Android.arm64-v8a="res://kotlin/build/bin/androidArm64/debugShared/libkotlin.so" + +[dependencies] + +OSX.64=[ ] +Windows.64=[ ] +X11.64=[ ] diff --git a/samples/benchmarks/gamelife/gradle.properties b/samples/benchmarks/gamelife/gradle.properties new file mode 100644 index 00000000..659b74c0 --- /dev/null +++ b/samples/benchmarks/gamelife/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx3G \ No newline at end of file diff --git a/samples/benchmarks/gamelife/gradle/wrapper/gradle-wrapper.jar b/samples/benchmarks/gamelife/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..f3d88b1c Binary files /dev/null and b/samples/benchmarks/gamelife/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/benchmarks/gamelife/gradle/wrapper/gradle-wrapper.properties b/samples/benchmarks/gamelife/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..ac33e994 --- /dev/null +++ b/samples/benchmarks/gamelife/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/samples/benchmarks/gamelife/gradlew b/samples/benchmarks/gamelife/gradlew new file mode 100644 index 00000000..2fe81a7d --- /dev/null +++ b/samples/benchmarks/gamelife/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/samples/benchmarks/gamelife/gradlew.bat b/samples/benchmarks/gamelife/gradlew.bat new file mode 100644 index 00000000..62bd9b9c --- /dev/null +++ b/samples/benchmarks/gamelife/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/samples/benchmarks/gamelife/icon.png b/samples/benchmarks/gamelife/icon.png new file mode 100644 index 00000000..2e42096c Binary files /dev/null and b/samples/benchmarks/gamelife/icon.png differ diff --git a/samples/benchmarks/gamelife/logo.png b/samples/benchmarks/gamelife/logo.png new file mode 100644 index 00000000..98ad1c6c Binary files /dev/null and b/samples/benchmarks/gamelife/logo.png differ diff --git a/samples/benchmarks/gamelife/main.gd b/samples/benchmarks/gamelife/main.gd new file mode 100644 index 00000000..4f036ed9 --- /dev/null +++ b/samples/benchmarks/gamelife/main.gd @@ -0,0 +1,19 @@ +extends Spatial + +export(String, "GdScript", "Kotlin") var language: String = "GdScript" +export(int, 100, 10000, 100) var limit = 1000 +export(int, 10, 200, 10) var size = 50 + +var game = preload("res://game.tscn").instance() + +# Called when the node enters the scene tree for the first time. +func _ready(): + if language == "GdScript": + var script = load("res://scripts/gamelife/game.gd") + game.set_script(script) + else: + var script = load("res://scripts/gamelife/Game.gdns") + game.set_script(script) + game.limit = limit + game.size = size + add_child(game) diff --git a/samples/benchmarks/gamelife/main.tscn b/samples/benchmarks/gamelife/main.tscn new file mode 100644 index 00000000..312a0d7a --- /dev/null +++ b/samples/benchmarks/gamelife/main.tscn @@ -0,0 +1,9 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://main.gd" type="Script" id=1] + +[node name="main" type="Spatial"] +script = ExtResource( 1 ) + +[node name="camera" type="Camera" parent="."] +transform = Transform( 1, 0, 0, 0, -1.62921e-07, 1, 0, -1, -1.62921e-07, 25, 50, 25 ) diff --git a/samples/benchmarks/gamelife/project.godot b/samples/benchmarks/gamelife/project.godot new file mode 100644 index 00000000..aece1b5c --- /dev/null +++ b/samples/benchmarks/gamelife/project.godot @@ -0,0 +1,30 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=4 + +_global_script_classes=[ ] +_global_script_class_icons={ + +} + +[application] + +config/name="gdlife" +run/main_scene="res://main.tscn" +config/icon="res://icon.png" + +[display] + +window/vsync/use_vsync=false + +[rendering] + +vram_compression/import_etc=true +vram_compression/import_etc2=false +environment/default_environment="res://default_env.tres" diff --git a/samples/benchmarks/gamelife/scripts/gamelife/Game.gdns b/samples/benchmarks/gamelife/scripts/gamelife/Game.gdns new file mode 100644 index 00000000..eb473ad8 --- /dev/null +++ b/samples/benchmarks/gamelife/scripts/gamelife/Game.gdns @@ -0,0 +1,8 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://gameoflife.gdnlib" type="GDNativeLibrary" id=1] + +[resource] +resource_name = "Game" +class_name = "godot.samples.benchmark.gamelife.Game" +library = ExtResource( 1 ) \ No newline at end of file diff --git a/samples/benchmarks/gamelife/scripts/gamelife/game.gd b/samples/benchmarks/gamelife/scripts/gamelife/game.gd new file mode 100644 index 00000000..0c4048bd --- /dev/null +++ b/samples/benchmarks/gamelife/scripts/gamelife/game.gd @@ -0,0 +1,143 @@ +extends MultiMeshInstance + +class Tile: + var v : bool + var life : int + var others : Array + + func _init(): + self.v = false + self.life = 0 + self.others = Array() + + func get_total() -> int: + var result : int = 0 + + for other in others: + result += other.get_value() + + return result + + func get_value(): + if v: + return 1 + + return 0 + +class Map: + var data : Array + var next : Array + var empty : Tile + + var w : int + var h : int + + func _init(width : int, height : int): + self.empty = Tile.new() + + self.w = width + self.h = height + + data = Array() + next = Array() + + for i in self.w * self.h: + data.append(Tile.new()) + next.append(false) + + var i = 0 + for y in self.h: + for x in self.w: + var t = data[i] + t.others = [ + at(x - 1, y - 1), at(x, y - 1), at(x + 1, y - 1), + at(x - 1, y), at(x + 1, y), + at(x - 1, y + 1), at(x, y + 1), at(x + 1, y + 1)] + i += 1 + + func get_index(x, y): + if x < 0 || x >= w || y < 0 || y >= h: + return -1 + + return x + y * w + + func put(x, y, v): + var index = get_index(x, y) + if index == -1: + return + + data[index].v = v + + func at(x, y): + var index = get_index(x, y) + if index == -1: + return empty + + return data[index] + + func process(): + for i in w * h: + var t = data[i] + var v = t.get_total() + + if data[i].v: + next[i] = v == 2 || v == 3 + else: + next[i] = v == 3 + + for i in w * h: + data[i].v = next[i] + + if data[i].v: + data[i].life = 20 + + data[i].life = max(0, data[i].life - 1) + +var map : Map +var started +var size = 50 + +func _ready(): + started = OS.get_system_time_msecs() + map = Map.new(size, size) + self.multimesh.instance_count = size*size + + var index : int = 0 + + var offset = (size - 50) * 0.5 + for y in size: + for x in size: + self.multimesh.set_instance_transform(index, Transform(Basis(), Vector3(x - offset, randi() % 10 * 0.1, y - offset))) + self.multimesh.set_instance_color(index, Color(255, x, y)) + + index += 1 + +var counter : int +var frame : int +var limit = 1000 + +func _process(_delta): + frame += 1 + if frame == limit: + var elapsed = OS.get_system_time_msecs() - started + print("Result: ", elapsed) + get_tree().quit() + return + + counter += 1 + if counter == 10: + var center = size/2 + map.put(center, center, true) + map.put(center-1, center, true) + map.put(center+1, center, true) + map.put(center, center-1, true) + counter = 0 + + map.process() + + for i in self.multimesh.instance_count: + var tile = map.data[i] + if tile.v: + self.multimesh.set_instance_color(i, Color(1.0, 0, 0)) + else: + self.multimesh.set_instance_color(i, Color(0, tile.life / 50.0, 0)) diff --git a/samples/benchmarks/gamelife/settings.gradle.kts b/samples/benchmarks/gamelife/settings.gradle.kts new file mode 100644 index 00000000..7f3666f5 --- /dev/null +++ b/samples/benchmarks/gamelife/settings.gradle.kts @@ -0,0 +1,24 @@ +// The following is used for configuring composite builds, which we use for developing this binding. +// Normally as a user, you should not need it. +includeBuild("../../../") { + dependencySubstitution { + substitute(module("com.utopia-rise:godot-build-props")).with(project(":godot-build-props")) + substitute(module("com.utopia-rise:godot-annotation-processor")).with(project(":godot-annotation-processor")) + substitute(module("com.utopia-rise:godot-compiler-plugin")).with(project(":godot-compiler-plugin")) + substitute(module("com.utopia-rise:godot-compiler-plugin-common")).with(project(":godot-compiler-plugin-common")) + substitute(module("com.utopia-rise:godot-entry-generator")).with(project(":godot-entry-generator")) + substitute(module("com.utopia-rise:godot-gradle-plugin")).with(project(":godot-gradle-plugin")) + substitute(module("com.utopia-rise:godot-library")).with(project(":godot-library")) + // gradle doesn't support targeting a specific configuration (i.e shadow) when substituting a dependency in a composite build. + // composite-build-support depends on project(":godot-compiler-native-plugin", configuration = "shadow") + substitute(module("com.utopia-rise:godot-compiler-native-plugin")).with(project(":composite-build-support")) + } +} + +pluginManagement { + resolutionStrategy.eachPlugin { + when (requested.id.id) { + "com.utopia-rise.godot-kotlin" -> useModule("com.utopia-rise:godot-gradle-plugin:${requested.version}") + } + } +} diff --git a/samples/benchmarks/gamelife/src/godotMain/kotlin/godot/samples/benchmark/gamelife/Game.kt b/samples/benchmarks/gamelife/src/godotMain/kotlin/godot/samples/benchmark/gamelife/Game.kt new file mode 100644 index 00000000..856c8276 --- /dev/null +++ b/samples/benchmarks/gamelife/src/godotMain/kotlin/godot/samples/benchmark/gamelife/Game.kt @@ -0,0 +1,73 @@ +package godot.samples.benchmark.gamelife + +import godot.* +import godot.core.* +import org.godotengine.kotlin.annotation.RegisterClass +import org.godotengine.kotlin.annotation.RegisterFunction +import org.godotengine.kotlin.annotation.RegisterProperty +import kotlin.math.sqrt + +@RegisterClass("scripts/gamelife") +class Game : MultiMeshInstance() { + + private lateinit var map: Map + private var started: Long = 0 + private var counter: Int = 0 + private var frame: Int = 0 + private val randomNumberGenerator = RandomNumberGenerator() + + @RegisterProperty(false, "50") + var size = 50 + @RegisterProperty(false, "1000") + var limit = 1000 + + @RegisterFunction + override fun _ready() { + started = OS.getSystemTimeMsecs() + map = Map(size, size) + multimesh.instanceCount = (size * size).toLong() + + var index = 0L + + val offset = (size - 50) * 0.5 + for (y in 0 until size) { + for (x in 0 until size) { + multimesh.setInstanceTransform(index, Transform(Basis(), Vector3(x - offset, randomNumberGenerator.randi() % 10 * 0.1, y - offset))) + multimesh.setInstanceColor(index, Color(255, x, y)) + index += 1 + } + } + } + + @RegisterFunction + override fun _process(delta: Double) { + frame += 1 + if (frame == limit) { + val elapsed = OS.getSystemTimeMsecs() - started + GD.print("Result: $elapsed") + getTree().quit() + return + } + + counter += 1 + if (counter == 10) { + val center = size / 2 + map.put(center, center, true) + map.put(center - 1, center, true) + map.put(center + 1, center, true) + map.put(center, center - 1, true) + counter = 0 + } + + map.process() + + for (i in 0 until multimesh.instanceCount) { + val tile = map.tileData[i.toInt()] + if (tile.isAlive) + multimesh.setInstanceColor(i, Color(1.0, 0, 0)) + else + multimesh.setInstanceColor(i, Color(0, tile.life / 50.0, 0)) + } + } +} + diff --git a/samples/benchmarks/gamelife/src/godotMain/kotlin/godot/samples/benchmark/gamelife/Map.kt b/samples/benchmarks/gamelife/src/godotMain/kotlin/godot/samples/benchmark/gamelife/Map.kt new file mode 100644 index 00000000..4315dedc --- /dev/null +++ b/samples/benchmarks/gamelife/src/godotMain/kotlin/godot/samples/benchmark/gamelife/Map.kt @@ -0,0 +1,76 @@ +package godot.samples.benchmark.gamelife + +import kotlin.math.max + +class Map(var width: Int, + var height: Int) { + + private var nextState: MutableList = ArrayList() + private var empty: Tile = Tile() + + var tileData: MutableList = ArrayList() + + init { + for (i in 0 until width * height) { + tileData.add(Tile()) + nextState.add(false) + } + + var i = 0 + for (y in 0 until height) { + for (x in 0 until width) { + val t = tileData[i] + t.neighbors.addAll(listOf( + at(x - 1, y - 1), at(x, y - 1), at(x + 1, y - 1), + at(x - 1, y), at(x + 1, y), + at(x - 1, y + 1), at(x, y + 1), at(x + 1, y + 1))) + i += 1 + } + } + } + + private fun at(x: Int, y: Int): Tile { + val index = getIndex(x, y) + if (index == -1) + return empty + + return tileData[index] + } + + private fun getIndex(x: Int, y: Int): Int { + if (x < 0 || x >= width || y < 0 || y >= height) + return -1 + + return x + y * width + } + + fun put(x: Int, y: Int, v: Boolean) { + val index = getIndex(x, y) + if (index == -1) + return + + tileData[index].isAlive = v + } + + fun process() { + for (i in 0 until width * height) { + val tile = tileData[i] + val value = tile.getTotal() + + if (tileData[i].isAlive) { + nextState[i] = value == 2 || value == 3 + } else { + nextState[i] = value == 3 + } + } + + for (i in 0 until width * height) { + tileData[i].isAlive = nextState[i] + + if (tileData[i].isAlive) + tileData[i].life = 20 + + tileData[i].life = max(0, tileData[i].life - 1) + } + } +} \ No newline at end of file diff --git a/samples/benchmarks/gamelife/src/godotMain/kotlin/godot/samples/benchmark/gamelife/Tile.kt b/samples/benchmarks/gamelife/src/godotMain/kotlin/godot/samples/benchmark/gamelife/Tile.kt new file mode 100644 index 00000000..9b289a0e --- /dev/null +++ b/samples/benchmarks/gamelife/src/godotMain/kotlin/godot/samples/benchmark/gamelife/Tile.kt @@ -0,0 +1,22 @@ +package godot.samples.benchmark.gamelife + +data class Tile(var isAlive: Boolean = false, + var life: Int = 0, + var neighbors: MutableList = ArrayList(8)) { + + fun getTotal(): Int { + var total = 0 + + for (neighbor in neighbors) { + total += neighbor.getValue() + } + + return total + } + + fun getValue(): Int { + if (isAlive) + return 1 + return 0 + } +} \ No newline at end of file