mirror of
https://github.com/godotengine/godot-benchmarks.git
synced 2026-01-06 14:10:20 +03:00
Add web interface for tracking benchmark results over time (#59)
This provides a web interface generated with Hugo for tracking benchmark results over time. Automatic dark theme support is provided using water.css. Benchmarks are run daily on a dedicated server with the `master` branch of Godot compiled from source on that server, so that build time and memory usage during the build can also be tracked over time. Binary size as well as startup/shutdown time and memory usage from an empty project are also measured.
This commit is contained in:
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
web/static/thirdparty/* linguist-vendored
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -15,3 +15,6 @@ mono_crash.*.json
|
|||||||
# System/tool-specific ignores
|
# System/tool-specific ignores
|
||||||
.directory
|
.directory
|
||||||
*~
|
*~
|
||||||
|
|
||||||
|
# Godot Git repository clone (run-benchmarks.sh)
|
||||||
|
/godot/
|
||||||
|
|||||||
@@ -5,5 +5,6 @@
|
|||||||
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'ios' ">net8.0</TargetFramework>
|
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'ios' ">net8.0</TargetFramework>
|
||||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||||
<RootNamespace>GodotBenchmarks</RootNamespace>
|
<RootNamespace>GodotBenchmarks</RootNamespace>
|
||||||
|
<DefaultItemExcludes>$(DefaultItemExcludes);godot/**</DefaultItemExcludes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ You can save the results JSON to a file using `--save-json="path/to/file.json"`
|
|||||||
godot -- --run-benchmarks
|
godot -- --run-benchmarks
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`--json-results-prefix=<string>` can be used to nest individual results within a
|
||||||
|
dictionary that has the name `<string>`. This can be used for easier merging of
|
||||||
|
separate result runs with `jq`.
|
||||||
|
|
||||||
#### Run a single benchmark
|
#### Run a single benchmark
|
||||||
|
|
||||||
The `--include-benchmarks` CLI argument can be used to specify the name.
|
The `--include-benchmarks` CLI argument can be used to specify the name.
|
||||||
|
|||||||
@@ -17,18 +17,6 @@ func benchmark_generate_1g_random_bytes_1k_at_a_time() -> void:
|
|||||||
crypto.generate_random_bytes(1000)
|
crypto.generate_random_bytes(1000)
|
||||||
|
|
||||||
|
|
||||||
func benchmark_generate_1g_random_bytes_1m_at_a_time() -> void:
|
|
||||||
var iterations = BYTES / 1_000_000
|
|
||||||
for i in iterations:
|
|
||||||
crypto.generate_random_bytes(1_000_000)
|
|
||||||
|
|
||||||
|
|
||||||
func benchmark_generate_1g_random_bytes_at_once() -> void:
|
|
||||||
var iterations = BYTES / 1_000_000_000
|
|
||||||
for i in iterations:
|
|
||||||
crypto.generate_random_bytes(1_000_000_000)
|
|
||||||
|
|
||||||
|
|
||||||
func benchmark_generate_rsa_2048() -> void:
|
func benchmark_generate_rsa_2048() -> void:
|
||||||
crypto.generate_rsa(2048)
|
crypto.generate_rsa(2048)
|
||||||
|
|
||||||
|
|||||||
2
main.gd
2
main.gd
@@ -6,6 +6,7 @@ var items := []
|
|||||||
var arg_include_benchmarks := ""
|
var arg_include_benchmarks := ""
|
||||||
var arg_exclude_benchmarks := ""
|
var arg_exclude_benchmarks := ""
|
||||||
var arg_save_json := ""
|
var arg_save_json := ""
|
||||||
|
var arg_json_results_prefix := ""
|
||||||
var arg_run_benchmarks := false
|
var arg_run_benchmarks := false
|
||||||
|
|
||||||
@onready var tree := $Tree as Tree
|
@onready var tree := $Tree as Tree
|
||||||
@@ -90,6 +91,7 @@ func _ready() -> void:
|
|||||||
|
|
||||||
if arg_save_json:
|
if arg_save_json:
|
||||||
Manager.save_json_to_path = arg_save_json
|
Manager.save_json_to_path = arg_save_json
|
||||||
|
Manager.json_results_prefix = arg_json_results_prefix
|
||||||
if arg_run_benchmarks:
|
if arg_run_benchmarks:
|
||||||
_on_Run_pressed()
|
_on_Run_pressed()
|
||||||
|
|
||||||
|
|||||||
60
manager.gd
60
manager.gd
@@ -37,7 +37,7 @@ func test_ids_from_path(path: String) -> Array[TestID]:
|
|||||||
for method in bench_script.get_method_list():
|
for method in bench_script.get_method_list():
|
||||||
if not method.name.begins_with(languages[extension]["test_prefix"]):
|
if not method.name.begins_with(languages[extension]["test_prefix"]):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# This method is a runnable test. Push it onto the result
|
# This method is a runnable test. Push it onto the result
|
||||||
var test_id := TestID.new()
|
var test_id := TestID.new()
|
||||||
test_id.name = method.name.trim_prefix(languages[extension]["test_prefix"])
|
test_id.name = method.name.trim_prefix(languages[extension]["test_prefix"])
|
||||||
@@ -45,7 +45,7 @@ func test_ids_from_path(path: String) -> Array[TestID]:
|
|||||||
test_id.language = extension
|
test_id.language = extension
|
||||||
rv.push_back(test_id)
|
rv.push_back(test_id)
|
||||||
|
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
|
|
||||||
# List of supported languages and their styles.
|
# List of supported languages and their styles.
|
||||||
@@ -55,6 +55,7 @@ var languages := {".gd": {"test_prefix": "benchmark_"}}
|
|||||||
var test_results := {}
|
var test_results := {}
|
||||||
|
|
||||||
var save_json_to_path := ""
|
var save_json_to_path := ""
|
||||||
|
var json_results_prefix := ""
|
||||||
|
|
||||||
|
|
||||||
## Recursively walks the given directory and returns all files found
|
## Recursively walks the given directory and returns all files found
|
||||||
@@ -114,18 +115,21 @@ func benchmark(test_ids: Array[TestID], return_path: String) -> void:
|
|||||||
|
|
||||||
print("Results JSON:")
|
print("Results JSON:")
|
||||||
print("----------------")
|
print("----------------")
|
||||||
print(JSON.stringify(get_results_dict()))
|
print(JSON.stringify(get_results_dict(json_results_prefix)))
|
||||||
print("----------------")
|
print("----------------")
|
||||||
|
|
||||||
if not save_json_to_path.is_empty():
|
if not save_json_to_path.is_empty():
|
||||||
print("Saving JSON output to: %s" % save_json_to_path)
|
print("Saving JSON output to: %s" % save_json_to_path)
|
||||||
|
print("Using prefix for results: %s" % json_results_prefix)
|
||||||
var file := FileAccess.open(save_json_to_path, FileAccess.WRITE)
|
var file := FileAccess.open(save_json_to_path, FileAccess.WRITE)
|
||||||
file.store_string(JSON.stringify(get_results_dict()))
|
file.store_string(JSON.stringify(get_results_dict(json_results_prefix)))
|
||||||
|
|
||||||
if return_path:
|
if return_path:
|
||||||
get_tree().change_scene_to_file(return_path)
|
get_tree().change_scene_to_file(return_path)
|
||||||
else:
|
else:
|
||||||
get_tree().queue_delete(get_tree())
|
# FIXME: The line below crashes the engine. Commenting it results in a
|
||||||
|
# "ObjectDB instances leaked at exit" warning (but no crash).
|
||||||
|
#get_tree().queue_delete(get_tree())
|
||||||
get_tree().quit()
|
get_tree().quit()
|
||||||
|
|
||||||
|
|
||||||
@@ -198,9 +202,13 @@ func get_result_as_string(test_id: TestID) -> String:
|
|||||||
|
|
||||||
return JSON.stringify(rd)
|
return JSON.stringify(rd)
|
||||||
|
|
||||||
func get_test_result_as_dict(test_id: TestID) -> Dictionary:
|
func get_test_result_as_dict(test_id: TestID, results_prefix: String = "") -> Dictionary:
|
||||||
var result : Results = test_results[test_id]
|
var result : Results = test_results[test_id]
|
||||||
var rv := {}
|
var rv := {}
|
||||||
|
if not results_prefix.is_empty():
|
||||||
|
# Nest the results dictionary with a prefix for easier merging of multiple unrelated runs with `jq`.
|
||||||
|
# For example, this is used on the benchmarks server to merge runs on several GPU vendors into a single JSON file.
|
||||||
|
rv = { results_prefix: {} }
|
||||||
if not result:
|
if not result:
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
@@ -208,11 +216,16 @@ func get_test_result_as_dict(test_id: TestID) -> Dictionary:
|
|||||||
if metric.type == TYPE_FLOAT:
|
if metric.type == TYPE_FLOAT:
|
||||||
var m : float = result.get(metric.name)
|
var m : float = result.get(metric.name)
|
||||||
const sig_figs = 4
|
const sig_figs = 4
|
||||||
rv[metric.name] = snapped(m, pow(10,floor(log(m)/log(10))-sig_figs+1))
|
if not is_zero_approx(m):
|
||||||
|
# Only store metrics if not 0 to reduce JSON size.
|
||||||
|
if not results_prefix.is_empty():
|
||||||
|
rv[results_prefix][metric.name] = snapped(m, pow(10,floor(log(m)/log(10))-sig_figs+1))
|
||||||
|
else:
|
||||||
|
rv[metric.name] = snapped(m, pow(10,floor(log(m)/log(10))-sig_figs+1))
|
||||||
|
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
func get_results_dict() -> Dictionary:
|
func get_results_dict(results_prefix: String = "") -> Dictionary:
|
||||||
var version_info := Engine.get_version_info()
|
var version_info := Engine.get_version_info()
|
||||||
var version_string: String
|
var version_string: String
|
||||||
if version_info.patch >= 1:
|
if version_info.patch >= 1:
|
||||||
@@ -220,17 +233,13 @@ func get_results_dict() -> Dictionary:
|
|||||||
else:
|
else:
|
||||||
version_string = "v%d.%d.%s.%s" % [version_info.major, version_info.minor, version_info.status, version_info.build]
|
version_string = "v%d.%d.%s.%s" % [version_info.major, version_info.minor, version_info.status, version_info.build]
|
||||||
|
|
||||||
var engine_binary := FileAccess.open(OS.get_executable_path(), FileAccess.READ)
|
# Only list information that doesn't change across benchmark runs on different GPUs,
|
||||||
|
# as JSON files are merged together. Otherwise, the fields would overwrite each other
|
||||||
|
# with different information.
|
||||||
var dict := {
|
var dict := {
|
||||||
engine = {
|
engine = {
|
||||||
version = version_string,
|
version = version_string,
|
||||||
version_hash = version_info.hash,
|
version_hash = version_info.hash,
|
||||||
build_type = (
|
|
||||||
"editor" if OS.has_feature("editor")
|
|
||||||
else "template_debug" if OS.is_debug_build()
|
|
||||||
else "template_release"
|
|
||||||
),
|
|
||||||
binary_size = engine_binary.get_length(),
|
|
||||||
},
|
},
|
||||||
system = {
|
system = {
|
||||||
os = OS.get_name(),
|
os = OS.get_name(),
|
||||||
@@ -243,18 +252,25 @@ func get_results_dict() -> Dictionary:
|
|||||||
else "unknown"
|
else "unknown"
|
||||||
),
|
),
|
||||||
cpu_count = OS.get_processor_count(),
|
cpu_count = OS.get_processor_count(),
|
||||||
gpu_name = RenderingServer.get_video_adapter_name(),
|
|
||||||
gpu_vendor = RenderingServer.get_video_adapter_vendor(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var benchmarks := []
|
var benchmarks := []
|
||||||
for test_id in get_test_ids():
|
for test_id in get_test_ids():
|
||||||
benchmarks.push_back({
|
var result_dict := get_test_result_as_dict(test_id, results_prefix)
|
||||||
category = test_id.pretty_category(),
|
# Only write a dictionary if a benchmark was run for it.
|
||||||
name = test_id.pretty_name(),
|
var should_write_dict := false
|
||||||
results = get_test_result_as_dict(test_id),
|
if results_prefix.is_empty():
|
||||||
})
|
should_write_dict = not result_dict.is_empty()
|
||||||
|
else:
|
||||||
|
should_write_dict = not result_dict[results_prefix].is_empty()
|
||||||
|
|
||||||
|
if should_write_dict:
|
||||||
|
benchmarks.push_back({
|
||||||
|
category = test_id.pretty_category(),
|
||||||
|
name = test_id.pretty_name(),
|
||||||
|
results = result_dict,
|
||||||
|
})
|
||||||
|
|
||||||
dict.benchmarks = benchmarks
|
dict.benchmarks = benchmarks
|
||||||
|
|
||||||
|
|||||||
53
merge_json.gd
Normal file
53
merge_json.gd
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# Usage: godot -s merge_json.gd -- json1.md json2.md [...] --output-path output.md
|
||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
func _init() -> void:
|
||||||
|
if OS.get_cmdline_user_args().is_empty():
|
||||||
|
print("Usage: godot -s merge_json.gd -- json1.md json2.md [...] --output-path output.md")
|
||||||
|
quit(1)
|
||||||
|
|
||||||
|
var output_path_idx := OS.get_cmdline_user_args().find("--output-path") + 1
|
||||||
|
if output_path_idx == OS.get_cmdline_user_args().size():
|
||||||
|
push_error("`--output-path` requires an argument. Aborting.")
|
||||||
|
quit(1)
|
||||||
|
|
||||||
|
var output_path := OS.get_cmdline_user_args()[output_path_idx]
|
||||||
|
if "--output-path" not in OS.get_cmdline_user_args():
|
||||||
|
push_error("`--output-path path/to/output.md` must be used at the end of the command line (preceded by paths of individual JSON benchmark run files). Aborting.")
|
||||||
|
quit(1)
|
||||||
|
|
||||||
|
var jsons: Array[Dictionary] = []
|
||||||
|
for file_idx in OS.get_cmdline_user_args().size():
|
||||||
|
if file_idx < output_path_idx - 1:
|
||||||
|
jsons.push_back(JSON.parse_string(FileAccess.get_file_as_string(OS.get_cmdline_user_args()[file_idx])))
|
||||||
|
|
||||||
|
print("Saving merged JSON to: %s" % output_path)
|
||||||
|
|
||||||
|
var benchmarks_list := []
|
||||||
|
|
||||||
|
# Gather list of all benchmarks, without any results for now.
|
||||||
|
for json in jsons:
|
||||||
|
for benchmark in json.benchmarks:
|
||||||
|
var new_dict := {
|
||||||
|
category = benchmark.category,
|
||||||
|
name = benchmark.name,
|
||||||
|
results = {}, # We'll add results later once we've gathered all benchmark names.
|
||||||
|
}
|
||||||
|
if not benchmarks_list.has(new_dict):
|
||||||
|
benchmarks_list.push_back(new_dict)
|
||||||
|
|
||||||
|
# Populate results in all benchmarks.
|
||||||
|
for benchmark_idx in benchmarks_list.size():
|
||||||
|
var benchmark: Dictionary = benchmarks_list[benchmark_idx]
|
||||||
|
for json in jsons:
|
||||||
|
for json_benchmark in json.benchmarks:
|
||||||
|
if json_benchmark.category == benchmark.category and json_benchmark.name == benchmark.name:
|
||||||
|
benchmark.results.merge(json_benchmark.results)
|
||||||
|
|
||||||
|
var result: Dictionary = jsons[0]
|
||||||
|
result.benchmarks = benchmarks_list
|
||||||
|
|
||||||
|
var file_access := FileAccess.open(output_path, FileAccess.WRITE)
|
||||||
|
file_access.store_string(JSON.stringify(result))
|
||||||
|
|
||||||
|
quit()
|
||||||
225
run-benchmarks.sh
Executable file
225
run-benchmarks.sh
Executable file
@@ -0,0 +1,225 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Run all benchmarks on the server in various configurations.
|
||||||
|
#
|
||||||
|
# NOTE: This script is tailored for the dedicated benchmarking server.
|
||||||
|
# It is not meant for local usage or experimentation.
|
||||||
|
# `sudo` must be able to work non-interactively for the script to succeed.
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=$'\n\t'
|
||||||
|
|
||||||
|
export DIR
|
||||||
|
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
# Reduce log spam and avoid issues with self-compiled builds crashing:
|
||||||
|
# https://github.com/godotengine/godot/issues/75409
|
||||||
|
export MANGOHUD=0
|
||||||
|
|
||||||
|
# Set X11 display for headless usage (with a X server running separately).
|
||||||
|
export DISPLAY=":0"
|
||||||
|
|
||||||
|
# Make the command line argument optional without tripping up `set -u`.
|
||||||
|
ARG1="${1:-''}"
|
||||||
|
|
||||||
|
if ! command -v git &> /dev/null; then
|
||||||
|
echo "ERROR: git must be installed and in PATH."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$ARG1" == "--help" || "$ARG1" == "-h" ]]; then
|
||||||
|
echo "Usage: $0 [--skip-build]"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
GODOT_REPO_DIR="$DIR/godot"
|
||||||
|
|
||||||
|
if [[ ! -d "$GODOT_REPO_DIR/.git" ]]; then
|
||||||
|
git clone https://github.com/godotengine/godot.git "$GODOT_REPO_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
GODOT_EMPTY_PROJECT_DIR="$DIR/web/godot-empty-project"
|
||||||
|
|
||||||
|
if [[ "$ARG1" != "--skip-build" ]]; then
|
||||||
|
cd "$GODOT_REPO_DIR"
|
||||||
|
|
||||||
|
git reset --hard
|
||||||
|
git clean -qdfx --exclude bin
|
||||||
|
git pull
|
||||||
|
|
||||||
|
if command -v ccache &> /dev/null; then
|
||||||
|
# Clear ccache to avoid skewing the build time results.
|
||||||
|
ccache --clear
|
||||||
|
fi
|
||||||
|
touch .gdignore
|
||||||
|
|
||||||
|
# Measure clean build times for debug and release builds (in milliseconds).
|
||||||
|
# Also create a `.gdignore` file to prevent Godot from importing resources
|
||||||
|
# within the Godot Git clone.
|
||||||
|
# WARNING: Any untracked and ignored files included in the repository will be removed!
|
||||||
|
BEGIN="$(date +%s%3N)"
|
||||||
|
PEAK_MEMORY_BUILD_DEBUG=$( (/usr/bin/time -f "%M" scons platform=linuxbsd target=editor optimize=debug module_mono_enabled=no progress=no debug_symbols=yes -j$(nproc) 2>&1 || true) | tail -1)
|
||||||
|
END="$(date +%s%3N)"
|
||||||
|
TIME_TO_BUILD_DEBUG="$((END - BEGIN))"
|
||||||
|
|
||||||
|
git clean -qdfx --exclude bin
|
||||||
|
if command -v ccache &> /dev/null; then
|
||||||
|
# Clear ccache to avoid skewing the build time results.
|
||||||
|
ccache --clear
|
||||||
|
fi
|
||||||
|
touch .gdignore
|
||||||
|
|
||||||
|
BEGIN="$(date +%s%3N)"
|
||||||
|
PEAK_MEMORY_BUILD_RELEASE=$( (/usr/bin/time -f "%M" scons platform=linuxbsd target=template_release optimize=speed lto=full module_mono_enabled=no progress=no debug_symbols=yes -j$(nproc) 2>&1 || true) | tail -1)
|
||||||
|
END="$(date +%s%3N)"
|
||||||
|
TIME_TO_BUILD_RELEASE="$((END - BEGIN))"
|
||||||
|
|
||||||
|
# FIXME: C# is disabled because the engine crashes on exit after running benchmarks.
|
||||||
|
#
|
||||||
|
# Generate Mono glue for C# build to work.
|
||||||
|
# echo "Generating .NET glue."
|
||||||
|
# bin/godot.linuxbsd.editor.x86_64.mono --headless --generate-mono-glue modules/mono/glue
|
||||||
|
# echo "Building .NET assemblies."
|
||||||
|
# # https://docs.godotengine.org/en/stable/contributing/development/compiling/compiling_with_dotnet.html#nuget-packages
|
||||||
|
# mkdir -p "$HOME/MyLocalNugetSource"
|
||||||
|
# # Source may already exist, so allow failure for the command below.
|
||||||
|
# dotnet nuget add source "$HOME/MyLocalNugetSource" --name MyLocalNugetSource || true
|
||||||
|
# modules/mono/build_scripts/build_assemblies.py --godot-output-dir=./bin --push-nupkgs-local "$HOME/MyLocalNugetSource"
|
||||||
|
|
||||||
|
cd "$DIR"
|
||||||
|
else
|
||||||
|
echo "run-benchmarks: Skipping engine build as requested on the command line."
|
||||||
|
TIME_TO_BUILD_DEBUG=1
|
||||||
|
TIME_TO_BUILD_RELEASE=1
|
||||||
|
PEAK_MEMORY_BUILD_DEBUG=1
|
||||||
|
PEAK_MEMORY_BUILD_RELEASE=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Path to the Godot debug binary to run. Used for CPU debug benchmarks.
|
||||||
|
GODOT_DEBUG="$GODOT_REPO_DIR/bin/godot.linuxbsd.editor.x86_64"
|
||||||
|
|
||||||
|
# Path to the Godot release binary to run. Used for CPU release and GPU benchmarks.
|
||||||
|
# The release binary is assumed to be the same commit as the debug build.
|
||||||
|
# Things will break if this is not the case.
|
||||||
|
GODOT_RELEASE="$GODOT_REPO_DIR/bin/godot.linuxbsd.template_release.x86_64"
|
||||||
|
|
||||||
|
COMMIT_HASH="$($GODOT_DEBUG --version | rev | cut --delimiter="." --field="1" | rev)"
|
||||||
|
DATE="$(date +'%Y-%m-%d')"
|
||||||
|
|
||||||
|
# Measure average engine startup + shutdown times over 20 runs (in milliseconds),
|
||||||
|
# as well as peak memory usage.
|
||||||
|
|
||||||
|
# Perform a warmup run first.
|
||||||
|
echo "Performing debug warmup run."
|
||||||
|
$GODOT_DEBUG --audio-driver Dummy --path "$GODOT_EMPTY_PROJECT_DIR" --quit || true
|
||||||
|
TOTAL=0
|
||||||
|
for _ in {0..19}; do
|
||||||
|
BEGIN="$(date +%s%3N)"
|
||||||
|
echo "Performing benchmark debug startup/shutdown run."
|
||||||
|
$GODOT_DEBUG --audio-driver Dummy --path "$GODOT_EMPTY_PROJECT_DIR" --quit || true
|
||||||
|
END="$(date +%s%3N)"
|
||||||
|
TOTAL="$((TOTAL + END - BEGIN))"
|
||||||
|
done
|
||||||
|
TIME_TO_STARTUP_SHUTDOWN_DEBUG="$((TOTAL / 20))"
|
||||||
|
|
||||||
|
echo "Performing benchmark debug peak memory usage run."
|
||||||
|
PEAK_MEMORY_STARTUP_SHUTDOWN_DEBUG=$(/usr/bin/time -f "%M" "$GODOT_DEBUG" --audio-driver Dummy --path "$GODOT_EMPTY_PROJECT_DIR" --quit 2>&1 | tail -1)
|
||||||
|
|
||||||
|
# Perform a warmup run first.
|
||||||
|
echo "Performing release warmup run."
|
||||||
|
$GODOT_RELEASE --audio-driver Dummy --path "$GODOT_EMPTY_PROJECT_DIR" --quit || true
|
||||||
|
TOTAL=0
|
||||||
|
for _ in {0..19}; do
|
||||||
|
BEGIN="$(date +%s%3N)"
|
||||||
|
echo "Performing benchmark release startup/shutdown run."
|
||||||
|
$GODOT_RELEASE --audio-driver Dummy --path "$GODOT_EMPTY_PROJECT_DIR" --quit || true
|
||||||
|
END="$(date +%s%3N)"
|
||||||
|
TOTAL="$((TOTAL + END - BEGIN))"
|
||||||
|
done
|
||||||
|
TIME_TO_STARTUP_SHUTDOWN_RELEASE="$((TOTAL / 20))"
|
||||||
|
|
||||||
|
echo "Performing benchmark release peak memory usage run."
|
||||||
|
PEAK_MEMORY_STARTUP_SHUTDOWN_RELEASE=$(/usr/bin/time -f "%M" "$GODOT_RELEASE" --audio-driver Dummy --path "$GODOT_EMPTY_PROJECT_DIR" --quit 2>&1 | tail -1)
|
||||||
|
|
||||||
|
# Import resources and build C# solutions in the project (required to run it).
|
||||||
|
echo "Performing resource importing and C# solution building."
|
||||||
|
$GODOT_DEBUG --headless --editor --build-solutions --quit-after 2
|
||||||
|
|
||||||
|
# Run CPU benchmarks.
|
||||||
|
|
||||||
|
echo "Running CPU benchmarks."
|
||||||
|
$GODOT_DEBUG --audio-driver Dummy -- --run-benchmarks --exclude-benchmarks="rendering/*" --save-json="/tmp/cpu_debug.md" --json-results-prefix="cpu_debug"
|
||||||
|
$GODOT_RELEASE --audio-driver Dummy -- --run-benchmarks --exclude-benchmarks="rendering/*" --save-json="/tmp/cpu_release.md" --json-results-prefix="cpu_release"
|
||||||
|
|
||||||
|
# Run GPU benchmarks.
|
||||||
|
# TODO: Run on different GPUs.
|
||||||
|
echo "Running GPU benchmarks."
|
||||||
|
$GODOT_RELEASE --audio-driver Dummy -- --run-benchmarks --include-benchmarks="rendering/*" --save-json="/tmp/amd.md" --json-results-prefix="amd"
|
||||||
|
#$GODOT_RELEASE --audio-driver Dummy -- --run-benchmarks --include-benchmarks="rendering/*" --save-json="/tmp/intel.md" --json-results-prefix="intel"
|
||||||
|
#$GODOT_RELEASE --audio-driver Dummy -- --run-benchmarks --include-benchmarks="rendering/*" --save-json="/tmp/nvidia.md" --json-results-prefix="nvidia"
|
||||||
|
|
||||||
|
rm -rf /tmp/godot-benchmarks-results/
|
||||||
|
# Clone a copy of the repository so we can push the new JSON files to it.
|
||||||
|
# The website build is performed by GitHub Actions on the `main` branch of the repository below,
|
||||||
|
# so we only push files to it and do nothing else.
|
||||||
|
git clone git@github.com:godotengine/godot-benchmarks-results.git /tmp/godot-benchmarks-results/
|
||||||
|
|
||||||
|
cd /tmp/godot-benchmarks-results/
|
||||||
|
|
||||||
|
# Merge benchmark run JSONs together.
|
||||||
|
# Use editor build as release build errors due to missing PCK file.
|
||||||
|
echo "Merging JSON files together."
|
||||||
|
$GODOT_DEBUG --path "$DIR" --script merge_json.gd -- /tmp/cpu_debug.md /tmp/cpu_release.md /tmp/amd.md --output-path /tmp/merged.md
|
||||||
|
#$GODOT_DEBUG --path "$DIR" --script merge_json.gd -- /tmp/cpu_debug.md /tmp/cpu_release.md /tmp/amd.md /tmp/intel.md /tmp/nvidia.md --output-path /tmp/merged.md
|
||||||
|
|
||||||
|
OUTPUT_PATH="/tmp/godot-benchmarks-results/${DATE}_${COMMIT_HASH}.md"
|
||||||
|
rm -f "$OUTPUT_PATH"
|
||||||
|
|
||||||
|
# Strip debugging symbols for fair binary size comparison.
|
||||||
|
# Do this after Godot is run so we can have useful crash backtraces
|
||||||
|
# if the engine crashes while running benchmarks.
|
||||||
|
strip "$GODOT_DEBUG" "$GODOT_RELEASE"
|
||||||
|
|
||||||
|
BINARY_SIZE_DEBUG="$(stat --printf="%s" "$GODOT_DEBUG")"
|
||||||
|
BINARY_SIZE_RELEASE="$(stat --printf="%s" "$GODOT_RELEASE")"
|
||||||
|
|
||||||
|
# Add extra JSON at the end of the merged JSON. We assume the merged JSON has no
|
||||||
|
# newline at the end of file, as Godot writes it. To append more data to the
|
||||||
|
# JSON dictionary, we remove the last `}` character and add a `,` instead.
|
||||||
|
echo "Appending extra JSON at the end of the merged JSON."
|
||||||
|
EXTRA_JSON=$(cat << EOF
|
||||||
|
"binary_size": {
|
||||||
|
"debug": $BINARY_SIZE_DEBUG,
|
||||||
|
"release": $BINARY_SIZE_RELEASE
|
||||||
|
},
|
||||||
|
"build_time": {
|
||||||
|
"debug": $TIME_TO_BUILD_DEBUG,
|
||||||
|
"release": $TIME_TO_BUILD_RELEASE
|
||||||
|
},
|
||||||
|
"build_peak_memory_usage": {
|
||||||
|
"debug": $PEAK_MEMORY_BUILD_DEBUG,
|
||||||
|
"release": $PEAK_MEMORY_BUILD_RELEASE
|
||||||
|
},
|
||||||
|
"empty_project_startup_shutdown_time": {
|
||||||
|
"debug": $TIME_TO_STARTUP_SHUTDOWN_DEBUG,
|
||||||
|
"release": $TIME_TO_STARTUP_SHUTDOWN_RELEASE
|
||||||
|
},
|
||||||
|
"empty_project_startup_shutdown_peak_memory_usage": {
|
||||||
|
"debug": $PEAK_MEMORY_STARTUP_SHUTDOWN_DEBUG,
|
||||||
|
"release": $PEAK_MEMORY_STARTUP_SHUTDOWN_RELEASE
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
echo "$(head -c -1 /tmp/merged.md),$EXTRA_JSON}" > "$OUTPUT_PATH"
|
||||||
|
|
||||||
|
# Build website files after running all benchmarks, so that benchmarks
|
||||||
|
# appear on the web interface.
|
||||||
|
echo "Pushing results to godot-benchmarks repository."
|
||||||
|
git add .
|
||||||
|
git config --local user.name "Godot Benchmarks"
|
||||||
|
git config --local user.email "godot-benchmarks@example.com"
|
||||||
|
git commit --no-gpg-sign --message "Deploy benchmark results of $COMMIT_HASH (master at $DATE)
|
||||||
|
|
||||||
|
https://github.com/godotengine/godot/commit/$COMMIT_HASH"
|
||||||
|
git push
|
||||||
|
|
||||||
|
cd "$DIR"
|
||||||
|
echo "Success."
|
||||||
4
server/README.md
Normal file
4
server/README.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
This folder contains configuration files used on the benchmarking server. This
|
||||||
|
can be used for reference purposes or to set up a new benchmarking server.
|
||||||
|
|
||||||
|
A Linux username of `godot` on the system is assumed in these scripts.
|
||||||
27
server/etc/X11/xorg.conf.d/01-amd.conf
Normal file
27
server/etc/X11/xorg.conf.d/01-amd.conf
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
Section "ServerLayout"
|
||||||
|
Identifier "default-layout"
|
||||||
|
Screen 0 "screen"
|
||||||
|
EndSection
|
||||||
|
|
||||||
|
Section "Device"
|
||||||
|
Identifier "amd"
|
||||||
|
Driver "amdgpu"
|
||||||
|
BusID "PCI:1:0:0"
|
||||||
|
EndSection
|
||||||
|
|
||||||
|
Section "Monitor"
|
||||||
|
Identifier "monitor"
|
||||||
|
Modeline "1920x1200_60.00" 193.25 1920 2056 2256 2592 1200 1203 1209 1245 -hsync +vsync
|
||||||
|
Option "PreferredMode" "1920x1080_60.00"
|
||||||
|
EndSection
|
||||||
|
|
||||||
|
Section "Screen"
|
||||||
|
Identifier "screen"
|
||||||
|
Device "amd"
|
||||||
|
Monitor "monitor"
|
||||||
|
DefaultDepth 24
|
||||||
|
SubSection "Display"
|
||||||
|
Depth 24
|
||||||
|
Modes "1920x1080"
|
||||||
|
EndSubSection
|
||||||
|
EndSection
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[Service]
|
||||||
|
ExecStartPre=/usr/bin/git -C /home/godot/godot-benchmarks/ reset --hard
|
||||||
|
ExecStartPre=/usr/bin/git -C /home/godot/godot-benchmarks/ clean -qdfx
|
||||||
|
ExecStartPre=/usr/bin/git -C /home/godot/godot-benchmarks/ pull
|
||||||
|
ExecStart=/home/godot/godot-benchmarks/run-benchmarks.sh
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Run godot-benchmarks
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=daily
|
||||||
|
Persistent=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
6
server/home/godot/.config/systemd/user/xorg.service
Normal file
6
server/home/godot/.config/systemd/user/xorg.service
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[Service]
|
||||||
|
ExecStart=xinit
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
0
web/.gdignore
Normal file
0
web/.gdignore
Normal file
1
web/.gitattributes
vendored
Normal file
1
web/.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
static/thirdparty/* linguist-vendored
|
||||||
10
web/.gitignore
vendored
Normal file
10
web/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Output HTML files
|
||||||
|
public/
|
||||||
|
|
||||||
|
# Temporary lock file while building
|
||||||
|
.hugo_build.lock
|
||||||
|
|
||||||
|
# Result JSON files
|
||||||
|
# (should be committed to https://github.com/godotengine/godot-benchmarks-results instead)
|
||||||
|
content/*.md
|
||||||
|
!_content/_index.md
|
||||||
38
web/README.md
Normal file
38
web/README.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Web interface for benchmark results
|
||||||
|
|
||||||
|
This website is built with the [Hugo](https://gohugo.io/) static site generator.
|
||||||
|
It's designed to work **exclusively** with the [`run-benchmarks.sh` script](../run-benchmarks.sh),
|
||||||
|
which runs benchmarks on a dedicated server with various GPU models.
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
- Using the [`run-benchmarks.sh` script](../run-benchmarks.sh), benchmark data
|
||||||
|
is collected and saved to a single JSON file for each engine commit, with 5 runs:
|
||||||
|
- CPU (debug template)
|
||||||
|
- CPU (release template)
|
||||||
|
- GPU (AMD, release template)
|
||||||
|
<!--
|
||||||
|
- GPU (Intel, release template)
|
||||||
|
- GPU (NVIDIA, release template)
|
||||||
|
-->
|
||||||
|
- [Hugo](https://gohugo.io/) is used to create a homepage listing recent
|
||||||
|
benchmarked commits, plus a single page per engine commit. This allows linking
|
||||||
|
to individual benchmarked commit in a future-proof way.
|
||||||
|
- [ApexCharts](https://apexcharts.com/) is used to draw graphs on the homepage.
|
||||||
|
- [Water.css](https://watercss.kognise.dev/) is used to provide styling,
|
||||||
|
including automatic dark theme support.
|
||||||
|
|
||||||
|
### Building
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
- Create JSON data or fetch existing JSON data from the live website.
|
||||||
|
- Save this JSON data to the `content` folder with the following naming convention:
|
||||||
|
`YYYY-MM-DD_hash.json` where `hash` is a 9-character Git commit hash of the
|
||||||
|
Godot build used (truncated from a full commit hash).
|
||||||
|
- Run `hugo server`.
|
||||||
|
|
||||||
|
## Production
|
||||||
|
|
||||||
|
- Follow the same steps as in the **Development** section above.
|
||||||
|
- Run `hugo --minify`.
|
||||||
0
web/content/_index.md
Normal file
0
web/content/_index.md
Normal file
14
web/godot-empty-project/project.godot
Normal file
14
web/godot-empty-project/project.godot
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
; 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=5
|
||||||
|
|
||||||
|
[application]
|
||||||
|
|
||||||
|
run/main_scene="res://test.tscn"
|
||||||
|
config/features=PackedStringArray("4.2")
|
||||||
3
web/godot-empty-project/test.tscn
Normal file
3
web/godot-empty-project/test.tscn
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[gd_scene format=3 uid="uid://drtld8p78sdv6"]
|
||||||
|
|
||||||
|
[node name="Node" type="Node"]
|
||||||
6
web/hugo.toml
Normal file
6
web/hugo.toml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
baseURL = 'https://benchmarks.godotengine.org/'
|
||||||
|
languageCode = 'en-us'
|
||||||
|
title = 'Godot Benchmarks'
|
||||||
|
|
||||||
|
# Disable generation of pages not relevant for godot-benchmarks.
|
||||||
|
disableKinds = ['taxonomy', 'term', 'sitemap', 'RSS']
|
||||||
35
web/layouts/_default/baseof.html
Normal file
35
web/layouts/_default/baseof.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" con tent="width=device-width, initial-scale=1">
|
||||||
|
<meta name="theme-color" content="#3d8fcc">
|
||||||
|
<title>{{ block "title" . }}{{ .Site.Title }}{{ end }}</title>
|
||||||
|
<link rel="canonical" href="{{ .Permalink }}">
|
||||||
|
<link rel="stylesheet" href="{{ .Site.BaseURL }}thirdparty/apexcharts/apexcharts.css">
|
||||||
|
<link rel="stylesheet" href="{{ .Site.BaseURL }}thirdparty/water.css">
|
||||||
|
<link rel="stylesheet" href="{{ .Site.BaseURL }}main.css">
|
||||||
|
<link rel="manifest" href="{{ .Site.BaseURL }}site.webmanifest">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<a class="site-title" href="{{ .Site.BaseURL }}">Godot Benchmarks</a>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{{ block "main" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
© 2022-present Godot Engine contributors – <a href="https://github.com/godotengine/godot-benchmarks/tree/main/web">Website source code</a>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="{{ .Site.BaseURL }}thirdparty/apexcharts/apexcharts.min.js"></script>
|
||||||
|
<script>
|
||||||
|
if (window.location.href.includes("://godotengine.github.io/godot-benchmarks-results/")) {
|
||||||
|
// Perform redirect.
|
||||||
|
window.location.href = window.location.href.replace('://godotengine.github.io/godot-benchmarks-results/','://benchmarks.godotengine.org/');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{{ block "javascript" . }}{{end}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
170
web/layouts/_default/single.html
Normal file
170
web/layouts/_default/single.html
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
|
||||||
|
<h1>{{ index (split (path.BaseName .Permalink) "_") 0 }}
|
||||||
|
<a href="https://github.com/godotengine/godot/commit/{{ .Params.engine.version_hash }}"><code>{{ slicestr .Params.engine.version_hash 0 9 }}</code></a></h1>
|
||||||
|
|
||||||
|
<div style="display: grid; grid-template-columns: repeat(2, 1fr); grid-gap: 10px;">
|
||||||
|
{{/* Order is inverted for this site. */}}
|
||||||
|
<div>
|
||||||
|
{{with .Site.RegularPages.Next . }}
|
||||||
|
<a href="{{ .RelPermalink }}">« Previous: {{ index (split (path.BaseName .Permalink) "_") 0 }}
|
||||||
|
<code>{{ slicestr .Params.engine.version_hash 0 9 }}</code></a>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
<div style="text-align: right">
|
||||||
|
{{with .Site.RegularPages.Prev . }}
|
||||||
|
<a href="{{ .RelPermalink }}">Next: {{ index (split (path.BaseName .Permalink) "_") 0 }}
|
||||||
|
<code>{{ slicestr .Params.engine.version_hash 0 9 }}</code> »</a>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>System information</h2>
|
||||||
|
<table class="table-first-column-align-right">
|
||||||
|
<tr>
|
||||||
|
<td>CPU</td>
|
||||||
|
<td>Intel Core i5-12400</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>GPUs</td>
|
||||||
|
<td>
|
||||||
|
🔴 AMD Radeon RX 550
|
||||||
|
<!-- 🔵 Intel UHD Graphics 730 – 🔴 AMD Radeon RX 550 – 🟢 NVIDIA GeForce GT 1030 -->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Operating system</td>
|
||||||
|
<td>Fedora 38 64-bit</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>Engine information</h2>
|
||||||
|
<table class="table-first-column-align-right">
|
||||||
|
<thead>
|
||||||
|
<td>Build type</td>
|
||||||
|
<td>
|
||||||
|
<span style="opacity: 0.65"><sub><abbr title="SCons flags: target=editor optimize=debug">Debug</abbr></sub> Debug editor<br></span>
|
||||||
|
<sub><abbr title="SCons flags: target=template_release optimize=speed lto=full">Release</abbr></sub> Release export template
|
||||||
|
</td>
|
||||||
|
</thead>
|
||||||
|
<tr>
|
||||||
|
<td><abbr title="Using GCC compiler with all caches cleared">Time to build</abbr></td>
|
||||||
|
<td>
|
||||||
|
<span style="opacity: 0.65"><sub>Debug</sub> {{ mul .Params.build_time.debug 0.001 | lang.FormatNumber 0 }} seconds<br></span>
|
||||||
|
<sub>Release</sub> {{ mul .Params.build_time.release 0.001 | lang.FormatNumber 0 }} seconds
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><abbr title="Using GCC compiler with all caches cleared">Build peak memory usage</abbr></td>
|
||||||
|
<td>
|
||||||
|
<span style="opacity: 0.65"><sub>Debug</sub> {{ mul .Params.build_peak_memory_usage.debug 0.001 | lang.FormatNumber 2 }} MB<br></span>
|
||||||
|
<sub>Release</sub> {{ mul .Params.build_peak_memory_usage.release 0.001 | lang.FormatNumber 2 }} MB
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><abbr title="Measured on an empty project">Startup + shutdown time</abbr></td>
|
||||||
|
<td>
|
||||||
|
<span style="opacity: 0.65"><sub>Debug</sub> {{ .Params.empty_project_startup_shutdown_time.debug | lang.FormatNumber 0 }} ms<br></span>
|
||||||
|
<sub>Release</sub> {{ .Params.empty_project_startup_shutdown_time.release | lang.FormatNumber 0 }} ms
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><abbr title="Measured on an empty project">Startup + shutdown peak memory usage</abbr></td>
|
||||||
|
<td>
|
||||||
|
<span style="opacity: 0.65"><sub>Debug</sub> {{ mul .Params.empty_project_startup_shutdown_peak_memory_usage.debug 0.001 | lang.FormatNumber 2 }} MB<br></span>
|
||||||
|
<sub>Release</sub> {{ mul .Params.empty_project_startup_shutdown_peak_memory_usage.release 0.001 | lang.FormatNumber 2 }} MB
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><abbr title="Measured after stripping debug symbols">Binary size</abbr></td>
|
||||||
|
<td>
|
||||||
|
<span style="opacity: 0.65"><sub>Debug</sub> {{ mul .Params.binary_size.debug 0.001 | lang.FormatNumber 0 }} KB<br></span>
|
||||||
|
<sub>Release</sub> {{ mul .Params.binary_size.release 0.001 | lang.FormatNumber 0 }} KB
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>Benchmark results</h2>
|
||||||
|
<em>For all values, lower is better.</em>
|
||||||
|
<details open>
|
||||||
|
<summary><strong>CPU</strong></summary>
|
||||||
|
<table class="table-first-column-align-right">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>Name</td>
|
||||||
|
<td>Idle</td>
|
||||||
|
<td>Physics</td>
|
||||||
|
<td><abbr title="Time spent setting up the scene or executing the script">Main Thread Time</abbr></td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{/* Check CPU debug data only, but also get data from release CPU runs. */}}
|
||||||
|
{{/* These runs are expected to have the same number of results available. */}}
|
||||||
|
{{ range .Params.benchmarks }}
|
||||||
|
{{ if gt .results.cpu_debug.time 0 }}
|
||||||
|
<tr>
|
||||||
|
<td><sub style="opacity: 0.65">{{ .category }}</sub><br><strong>{{ .name }}</strong></td>
|
||||||
|
<td>
|
||||||
|
{{ if gt .results.cpu_debug.idle 0 }}
|
||||||
|
<span style="opacity: 0.65"><sub>Debug</sub> {{ .results.cpu_debug.idle }} <sub>mspf</sub><br></span>
|
||||||
|
<sub>Release</sub> {{ .results.cpu_release.idle }} <sub>mspf</sub>
|
||||||
|
{{ end }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ if gt .results.cpu_debug.physics 0 }}
|
||||||
|
<span style="opacity: 0.65"><sub>Debug</sub> {{ .results.cpu_debug.physics }} <sub>mspf</sub><br></span>
|
||||||
|
<sub>Release</sub> {{ .results.cpu_debug.physics }} <sub>mspf</sub>
|
||||||
|
{{ end }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ if gt .results.cpu_debug.time 0 }}
|
||||||
|
<span style="opacity: 0.65"><sub>Debug</sub> {{ .results.cpu_debug.time }} <sub>ms</sub><br></span>
|
||||||
|
<sub>Release</sub> {{ .results.cpu_release.time }} <sub>ms</sub>
|
||||||
|
{{ end }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details open>
|
||||||
|
<summary><strong>GPU</strong></summary>
|
||||||
|
<table class="table-first-column-align-right">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>Name</td>
|
||||||
|
<td>Render CPU</td>
|
||||||
|
<td>Render GPU</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{/* Check GPU AMD data only, but also get data from Intel and NVIDIA GPU runs. */}}
|
||||||
|
{{/* These runs are expected to have the same number of results available. */}}
|
||||||
|
{{ range .Params.benchmarks }}
|
||||||
|
{{ if gt .results.amd.render_cpu 0 }}
|
||||||
|
<tr>
|
||||||
|
<td><sub style="opacity: 0.65">{{ .category }}</sub><br><strong>{{ .name }}</strong></td>
|
||||||
|
<td>
|
||||||
|
{{ if gt .results.amd.render_cpu 0 }}
|
||||||
|
<!-- <span title="Intel HD Graphics">🔵</span> {{ .results.intel.render_cpu }} <sub>mspf</sub><br> -->
|
||||||
|
<span title="AMD Radeon RX 550">🔴</span> {{ .results.amd.render_cpu }} <sub>mspf</sub><!-- -->
|
||||||
|
<!-- span title="NVIDIA GeForce GT 1030">🟢</span> {{ .results.nvidia.render_cpu }} <sub>mspf</sub> -->
|
||||||
|
{{ end }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ if gt .results.amd.render_gpu 0 }}
|
||||||
|
<!-- <span title="Intel HD Graphics">🔵</span> {{ .results.intel.render_gpu }} <sub>mspf</sub><br> -->
|
||||||
|
<span title="AMD Radeon RX 550">🔴</span> {{ .results.amd.render_gpu }} <sub>mspf</sub><!-- -->
|
||||||
|
<!-- <span title="NVIDIA GeForce GT 1030">🟢</span> {{ .results.nvidia.render_gpu }} <sub>mspf</sub> -->
|
||||||
|
{{ end }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
78
web/layouts/index.html
Normal file
78
web/layouts/index.html
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<main aria-role="main">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This page tracks <a href="https://godotengine.org/">Godot Engine</a> performance running on a
|
||||||
|
<a href="https://github.com/godotengine/godot-benchmarks">benchmark suite</a>.
|
||||||
|
Benchmarks are run on a daily basis to track performance improvements and
|
||||||
|
regressions over time.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Latest benchmark runs</h2>
|
||||||
|
<ul>
|
||||||
|
{{ range first 100 .Pages.Reverse }}
|
||||||
|
<li><a href="{{ .Permalink }}">{{ index (split (path.BaseName .Permalink) "_") 0 }}
|
||||||
|
<code>{{ slicestr .Params.engine.version_hash 0 9 }}</code></a></li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h2>Benchmarking machine</h2>
|
||||||
|
<p>
|
||||||
|
To allow for direct GPU access and avoid performance fluctuations due to the
|
||||||
|
use of a shared host, benchmarks are run on a dedicated server with the
|
||||||
|
following hardware:
|
||||||
|
</p>
|
||||||
|
<table class="table-first-column-align-right">
|
||||||
|
<tr>
|
||||||
|
<td>CPU</td>
|
||||||
|
<td>Intel Core i5-12400</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>RAM</td>
|
||||||
|
<td>16 GB (2×8 GB DDR4-3000 CL16)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>SSD</td>
|
||||||
|
<td>Samsung 980 500 GB (M.2)</td>
|
||||||
|
</tr>
|
||||||
|
<!--
|
||||||
|
<tr>
|
||||||
|
<td>GPU 1 (integrated)</td>
|
||||||
|
<td>🔵 Intel UHD Graphics 730</td>
|
||||||
|
</tr>
|
||||||
|
-->
|
||||||
|
<tr>
|
||||||
|
<td>GPU (dedicated)</td>
|
||||||
|
<td>🔴 AMD Radeon RX 550</td>
|
||||||
|
</tr>
|
||||||
|
<!--
|
||||||
|
<tr>
|
||||||
|
<td>GPU 3 (dedicated)</td>
|
||||||
|
<td>🟢 NVIDIA GeForce GT 1030</td>
|
||||||
|
</tr>
|
||||||
|
-->
|
||||||
|
<tr>
|
||||||
|
<td>Operating system</td>
|
||||||
|
<td>Fedora 39 x86_64</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<p>
|
||||||
|
All core and memory clocks are kept at stock frequencies. When running
|
||||||
|
benchmarks (except for compiling the engine), The CPU governor is set to
|
||||||
|
<code>performance</code> to improve result consistency. The OS is kept in a
|
||||||
|
default configuration as closely as posible. The machine is not running any
|
||||||
|
other tasks in parallel – this page is served from GitHub Pages.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Benchmarks that make use of the GPU are run 3 times (once on each GPU with a release build).
|
||||||
|
Benchmarks that don't make use of the GPU are run twice (once with a debug build, once with a release build).
|
||||||
|
</p>
|
||||||
|
</main>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "javascript" }}
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
|
{{ end }}
|
||||||
39
web/static/main.css
Normal file
39
web/static/main.css
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/* See `thirdparty/water.css` for CSS variables that can be used here. */
|
||||||
|
|
||||||
|
p, li {
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: var(--background);
|
||||||
|
border-radius: 0 0 6px 6px;
|
||||||
|
padding: .75rem 2rem;
|
||||||
|
margin-top: -1.25rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.site-title {
|
||||||
|
color: var(--text-bright);
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Automatically resize the left column to take as little space as required. */
|
||||||
|
.table-first-column-align-right {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Align first column to the right to improve readability in some situations. */
|
||||||
|
.table-first-column-align-right tr td:first-of-type {
|
||||||
|
text-align: right;
|
||||||
|
width: 1%;
|
||||||
|
white-space: nowrap;
|
||||||
|
/* Style the first column as headings. */
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
12
web/static/site.webmanifest
Normal file
12
web/static/site.webmanifest
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"short_name": "Godot Benchmarks",
|
||||||
|
"name": "Godot Benchmarks",
|
||||||
|
"icons": [{
|
||||||
|
"src": "icon.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
}],
|
||||||
|
"start_url": "/godot-benchmarks/",
|
||||||
|
"background_color": "#223",
|
||||||
|
"theme_color": "#3d8fcc"
|
||||||
|
}
|
||||||
688
web/static/thirdparty/apexcharts/apexcharts.css
vendored
Normal file
688
web/static/thirdparty/apexcharts/apexcharts.css
vendored
Normal file
@@ -0,0 +1,688 @@
|
|||||||
|
.apexcharts-canvas {
|
||||||
|
position: relative;
|
||||||
|
user-select: none;
|
||||||
|
/* cannot give overflow: hidden as it will crop tooltips which overflow outside chart area */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* scrollbar is not visible by default for legend, hence forcing the visibility */
|
||||||
|
.apexcharts-canvas ::-webkit-scrollbar {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-canvas ::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: rgba(0, 0, 0, .5);
|
||||||
|
box-shadow: 0 0 1px rgba(255, 255, 255, .5);
|
||||||
|
-webkit-box-shadow: 0 0 1px rgba(255, 255, 255, .5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.apexcharts-inner {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-text tspan {
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend-mouseover-inactive {
|
||||||
|
transition: 0.15s ease all;
|
||||||
|
opacity: 0.20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-series-collapsed {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip {
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 2px 2px 6px -4px #999;
|
||||||
|
cursor: default;
|
||||||
|
font-size: 14px;
|
||||||
|
left: 62px;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 12;
|
||||||
|
transition: 0.15s ease all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip.apexcharts-active {
|
||||||
|
opacity: 1;
|
||||||
|
transition: 0.15s ease all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip.apexcharts-theme-light {
|
||||||
|
border: 1px solid #e3e3e3;
|
||||||
|
background: rgba(255, 255, 255, 0.96);
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip.apexcharts-theme-dark {
|
||||||
|
color: #fff;
|
||||||
|
background: rgba(30, 30, 30, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip * {
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.apexcharts-tooltip-title {
|
||||||
|
padding: 6px;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip.apexcharts-theme-light .apexcharts-tooltip-title {
|
||||||
|
background: #ECEFF1;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip.apexcharts-theme-dark .apexcharts-tooltip-title {
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
border-bottom: 1px solid #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-text-y-value,
|
||||||
|
.apexcharts-tooltip-text-goals-value,
|
||||||
|
.apexcharts-tooltip-text-z-value {
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-title:empty,
|
||||||
|
.apexcharts-tooltip-text-y-label:empty,
|
||||||
|
.apexcharts-tooltip-text-y-value:empty,
|
||||||
|
.apexcharts-tooltip-text-goals-label:empty,
|
||||||
|
.apexcharts-tooltip-text-goals-value:empty,
|
||||||
|
.apexcharts-tooltip-text-z-value:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-text-y-value,
|
||||||
|
.apexcharts-tooltip-text-goals-value,
|
||||||
|
.apexcharts-tooltip-text-z-value {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-text-goals-label,
|
||||||
|
.apexcharts-tooltip-text-goals-value {
|
||||||
|
padding: 6px 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-goals-group,
|
||||||
|
.apexcharts-tooltip-text-goals-label,
|
||||||
|
.apexcharts-tooltip-text-goals-value {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.apexcharts-tooltip-text-goals-label:not(:empty),
|
||||||
|
.apexcharts-tooltip-text-goals-value:not(:empty) {
|
||||||
|
margin-top: -6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-marker {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
position: relative;
|
||||||
|
top: 0px;
|
||||||
|
margin-right: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-series-group {
|
||||||
|
padding: 0 10px;
|
||||||
|
display: none;
|
||||||
|
text-align: left;
|
||||||
|
justify-content: left;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-series-group.apexcharts-active .apexcharts-tooltip-marker {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-series-group.apexcharts-active,
|
||||||
|
.apexcharts-tooltip-series-group:last-child {
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-series-group-hidden {
|
||||||
|
opacity: 0;
|
||||||
|
height: 0;
|
||||||
|
line-height: 0;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-y-group {
|
||||||
|
padding: 6px 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-box, .apexcharts-custom-tooltip {
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-boxPlot {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-box>div {
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-box span.value {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-rangebar {
|
||||||
|
padding: 5px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-rangebar .category {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-tooltip-rangebar .series-name {
|
||||||
|
font-weight: bold;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xaxistooltip {
|
||||||
|
opacity: 0;
|
||||||
|
padding: 9px 10px;
|
||||||
|
pointer-events: none;
|
||||||
|
color: #373d3f;
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 2px;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
|
background: #ECEFF1;
|
||||||
|
border: 1px solid #90A4AE;
|
||||||
|
transition: 0.15s ease all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xaxistooltip.apexcharts-theme-dark {
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xaxistooltip:after,
|
||||||
|
.apexcharts-xaxistooltip:before {
|
||||||
|
left: 50%;
|
||||||
|
border: solid transparent;
|
||||||
|
content: " ";
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xaxistooltip:after {
|
||||||
|
border-color: rgba(236, 239, 241, 0);
|
||||||
|
border-width: 6px;
|
||||||
|
margin-left: -6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xaxistooltip:before {
|
||||||
|
border-color: rgba(144, 164, 174, 0);
|
||||||
|
border-width: 7px;
|
||||||
|
margin-left: -7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xaxistooltip-bottom:after,
|
||||||
|
.apexcharts-xaxistooltip-bottom:before {
|
||||||
|
bottom: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xaxistooltip-top:after,
|
||||||
|
.apexcharts-xaxistooltip-top:before {
|
||||||
|
top: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xaxistooltip-bottom:after {
|
||||||
|
border-bottom-color: #ECEFF1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xaxistooltip-bottom:before {
|
||||||
|
border-bottom-color: #90A4AE;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:after {
|
||||||
|
border-bottom-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:before {
|
||||||
|
border-bottom-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xaxistooltip-top:after {
|
||||||
|
border-top-color: #ECEFF1
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xaxistooltip-top:before {
|
||||||
|
border-top-color: #90A4AE;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xaxistooltip-top.apexcharts-theme-dark:after {
|
||||||
|
border-top-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xaxistooltip-top.apexcharts-theme-dark:before {
|
||||||
|
border-top-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xaxistooltip.apexcharts-active {
|
||||||
|
opacity: 1;
|
||||||
|
transition: 0.15s ease all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip {
|
||||||
|
opacity: 0;
|
||||||
|
padding: 4px 10px;
|
||||||
|
pointer-events: none;
|
||||||
|
color: #373d3f;
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 2px;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
|
background: #ECEFF1;
|
||||||
|
border: 1px solid #90A4AE;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip.apexcharts-theme-dark {
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip:after,
|
||||||
|
.apexcharts-yaxistooltip:before {
|
||||||
|
top: 50%;
|
||||||
|
border: solid transparent;
|
||||||
|
content: " ";
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip:after {
|
||||||
|
border-color: rgba(236, 239, 241, 0);
|
||||||
|
border-width: 6px;
|
||||||
|
margin-top: -6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip:before {
|
||||||
|
border-color: rgba(144, 164, 174, 0);
|
||||||
|
border-width: 7px;
|
||||||
|
margin-top: -7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip-left:after,
|
||||||
|
.apexcharts-yaxistooltip-left:before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip-right:after,
|
||||||
|
.apexcharts-yaxistooltip-right:before {
|
||||||
|
right: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip-left:after {
|
||||||
|
border-left-color: #ECEFF1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip-left:before {
|
||||||
|
border-left-color: #90A4AE;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip-left.apexcharts-theme-dark:after {
|
||||||
|
border-left-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip-left.apexcharts-theme-dark:before {
|
||||||
|
border-left-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip-right:after {
|
||||||
|
border-right-color: #ECEFF1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip-right:before {
|
||||||
|
border-right-color: #90A4AE;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip-right.apexcharts-theme-dark:after {
|
||||||
|
border-right-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip-right.apexcharts-theme-dark:before {
|
||||||
|
border-right-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip.apexcharts-active {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-yaxistooltip-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xcrosshairs,
|
||||||
|
.apexcharts-ycrosshairs {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
transition: 0.15s ease all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-xcrosshairs.apexcharts-active,
|
||||||
|
.apexcharts-ycrosshairs.apexcharts-active {
|
||||||
|
opacity: 1;
|
||||||
|
transition: 0.15s ease all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-ycrosshairs-hidden {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-selection-rect {
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg_select_boundingRect, .svg_select_points_rot {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.apexcharts-selection-rect + g .svg_select_boundingRect,
|
||||||
|
.apexcharts-selection-rect + g .svg_select_points_rot {
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-selection-rect + g .svg_select_points_l,
|
||||||
|
.apexcharts-selection-rect + g .svg_select_points_r {
|
||||||
|
cursor: ew-resize;
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg_select_points {
|
||||||
|
fill: #efefef;
|
||||||
|
stroke: #333;
|
||||||
|
rx: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-svg.apexcharts-zoomable.hovering-zoom {
|
||||||
|
cursor: crosshair
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-svg.apexcharts-zoomable.hovering-pan {
|
||||||
|
cursor: move
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-zoom-icon,
|
||||||
|
.apexcharts-zoomin-icon,
|
||||||
|
.apexcharts-zoomout-icon,
|
||||||
|
.apexcharts-reset-icon,
|
||||||
|
.apexcharts-pan-icon,
|
||||||
|
.apexcharts-selection-icon,
|
||||||
|
.apexcharts-menu-icon,
|
||||||
|
.apexcharts-toolbar-custom-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 24px;
|
||||||
|
color: #6E8192;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-zoom-icon svg,
|
||||||
|
.apexcharts-zoomin-icon svg,
|
||||||
|
.apexcharts-zoomout-icon svg,
|
||||||
|
.apexcharts-reset-icon svg,
|
||||||
|
.apexcharts-menu-icon svg {
|
||||||
|
fill: #6E8192;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-selection-icon svg {
|
||||||
|
fill: #444;
|
||||||
|
transform: scale(0.76)
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-theme-dark .apexcharts-zoom-icon svg,
|
||||||
|
.apexcharts-theme-dark .apexcharts-zoomin-icon svg,
|
||||||
|
.apexcharts-theme-dark .apexcharts-zoomout-icon svg,
|
||||||
|
.apexcharts-theme-dark .apexcharts-reset-icon svg,
|
||||||
|
.apexcharts-theme-dark .apexcharts-pan-icon svg,
|
||||||
|
.apexcharts-theme-dark .apexcharts-selection-icon svg,
|
||||||
|
.apexcharts-theme-dark .apexcharts-menu-icon svg,
|
||||||
|
.apexcharts-theme-dark .apexcharts-toolbar-custom-icon svg {
|
||||||
|
fill: #f3f4f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-canvas .apexcharts-zoom-icon.apexcharts-selected svg,
|
||||||
|
.apexcharts-canvas .apexcharts-selection-icon.apexcharts-selected svg,
|
||||||
|
.apexcharts-canvas .apexcharts-reset-zoom-icon.apexcharts-selected svg {
|
||||||
|
fill: #008FFB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-theme-light .apexcharts-selection-icon:not(.apexcharts-selected):hover svg,
|
||||||
|
.apexcharts-theme-light .apexcharts-zoom-icon:not(.apexcharts-selected):hover svg,
|
||||||
|
.apexcharts-theme-light .apexcharts-zoomin-icon:hover svg,
|
||||||
|
.apexcharts-theme-light .apexcharts-zoomout-icon:hover svg,
|
||||||
|
.apexcharts-theme-light .apexcharts-reset-icon:hover svg,
|
||||||
|
.apexcharts-theme-light .apexcharts-menu-icon:hover svg {
|
||||||
|
fill: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-selection-icon,
|
||||||
|
.apexcharts-menu-icon {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-reset-icon {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-zoom-icon,
|
||||||
|
.apexcharts-reset-icon,
|
||||||
|
.apexcharts-menu-icon {
|
||||||
|
transform: scale(0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-zoomin-icon,
|
||||||
|
.apexcharts-zoomout-icon {
|
||||||
|
transform: scale(0.7)
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-zoomout-icon {
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-pan-icon {
|
||||||
|
transform: scale(0.62);
|
||||||
|
position: relative;
|
||||||
|
left: 1px;
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-pan-icon svg {
|
||||||
|
fill: #fff;
|
||||||
|
stroke: #6E8192;
|
||||||
|
stroke-width: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-pan-icon.apexcharts-selected svg {
|
||||||
|
stroke: #008FFB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-pan-icon:not(.apexcharts-selected):hover svg {
|
||||||
|
stroke: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-toolbar {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 11;
|
||||||
|
max-width: 176px;
|
||||||
|
text-align: right;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 0px 6px 2px 6px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-menu {
|
||||||
|
background: #fff;
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 3px;
|
||||||
|
right: 10px;
|
||||||
|
opacity: 0;
|
||||||
|
min-width: 110px;
|
||||||
|
transition: 0.15s ease all;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-menu.apexcharts-menu-open {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: all;
|
||||||
|
transition: 0.15s ease all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-menu-item {
|
||||||
|
padding: 6px 7px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-theme-light .apexcharts-menu-item:hover {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-theme-dark .apexcharts-menu {
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 768px) {
|
||||||
|
.apexcharts-canvas:hover .apexcharts-toolbar {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-datalabel.apexcharts-element-hidden {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-pie-label,
|
||||||
|
.apexcharts-datalabels,
|
||||||
|
.apexcharts-datalabel,
|
||||||
|
.apexcharts-datalabel-label,
|
||||||
|
.apexcharts-datalabel-value {
|
||||||
|
cursor: default;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-pie-label-delay {
|
||||||
|
opacity: 0;
|
||||||
|
animation-name: opaque;
|
||||||
|
animation-duration: 0.3s;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
animation-timing-function: ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-canvas .apexcharts-element-hidden {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-hide .apexcharts-series-points {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apexcharts-gridline,
|
||||||
|
.apexcharts-annotation-rect,
|
||||||
|
.apexcharts-tooltip .apexcharts-marker,
|
||||||
|
.apexcharts-area-series .apexcharts-area,
|
||||||
|
.apexcharts-line,
|
||||||
|
.apexcharts-zoom-rect,
|
||||||
|
.apexcharts-toolbar svg,
|
||||||
|
.apexcharts-area-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,
|
||||||
|
.apexcharts-line-series .apexcharts-series-markers .apexcharts-marker.no-pointer-events,
|
||||||
|
.apexcharts-radar-series path,
|
||||||
|
.apexcharts-radar-series polygon {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* markers */
|
||||||
|
|
||||||
|
.apexcharts-marker {
|
||||||
|
transition: 0.15s ease all;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes opaque {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Resize generated styles */
|
||||||
|
|
||||||
|
@keyframes resizeanim {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-triggers {
|
||||||
|
animation: 1ms resizeanim;
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-triggers,
|
||||||
|
.resize-triggers>div,
|
||||||
|
.contract-trigger:before {
|
||||||
|
content: " ";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-triggers>div {
|
||||||
|
background: #eee;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contract-trigger:before {
|
||||||
|
width: 200%;
|
||||||
|
height: 200%;
|
||||||
|
}
|
||||||
14
web/static/thirdparty/apexcharts/apexcharts.min.js
vendored
Normal file
14
web/static/thirdparty/apexcharts/apexcharts.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1689
web/static/thirdparty/water.css
vendored
Normal file
1689
web/static/thirdparty/water.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user