A lot
This commit is contained in:
parent
c07be8ad62
commit
2891fd73ed
110 changed files with 38 additions and 22492 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -1,8 +1,7 @@
|
||||||
.gradle
|
.gradle
|
||||||
|
gradle/
|
||||||
build/
|
build/
|
||||||
!gradle/wrapper/gradle-wrapper.jar
|
|
||||||
!**/src/main/**/build/
|
|
||||||
!**/src/test/**/build/
|
|
||||||
|
|
||||||
### IntelliJ IDEA ###
|
### IntelliJ IDEA ###
|
||||||
/.idea/workspace.xml
|
/.idea/workspace.xml
|
||||||
|
|
2
.idea/compiler.xml
vendored
2
.idea/compiler.xml
vendored
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<bytecodeTargetLevel target="16" />
|
<bytecodeTargetLevel target="22" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
2
.idea/gradle.xml
vendored
2
.idea/gradle.xml
vendored
|
@ -6,7 +6,7 @@
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="/var/home/snoweuph/Workspace/GameEngineJava" />
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
</GradleProjectSettings>
|
</GradleProjectSettings>
|
||||||
|
|
8
.idea/misc.xml
vendored
8
.idea/misc.xml
vendored
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ComposerSettings">
|
||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
<execution />
|
||||||
<file type="web" url="file://$PROJECT_DIR$" />
|
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_16" project-jdk-name="corretto-16" project-jdk-type="JavaSDK" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_22" default="true" project-jdk-name="22" project-jdk-type="JavaSDK" />
|
||||||
</project>
|
</project>
|
|
@ -1,66 +0,0 @@
|
||||||
val lwjglVersion = "3.3.3"
|
|
||||||
val jomlVersion = "1.10.5"
|
|
||||||
|
|
||||||
group = "dev.euph"
|
|
||||||
version = "1.0-SNAPSHOT"
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
main {
|
|
||||||
java.srcDirs("src")
|
|
||||||
resources.srcDir("res")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
java
|
|
||||||
application
|
|
||||||
}
|
|
||||||
|
|
||||||
val lwjglNatives = Pair(
|
|
||||||
System.getProperty("os.name")!!,
|
|
||||||
System.getProperty("os.arch")!!
|
|
||||||
).let { (name, arch) ->
|
|
||||||
when {
|
|
||||||
arrayOf("Linux", "FreeBSD", "SunOS", "Unit").any { name.startsWith(it) } ->
|
|
||||||
if (arrayOf("arm", "aarch64").any { arch.startsWith(it) })
|
|
||||||
"natives-linux${if (arch.contains("64") || arch.startsWith("armv8")) "-arm64" else "-arm32"}"
|
|
||||||
else if (arch.startsWith("ppc"))
|
|
||||||
"natives-linux-ppc64le"
|
|
||||||
else if (arch.startsWith("riscv"))
|
|
||||||
"natives-linux-riscv64"
|
|
||||||
else
|
|
||||||
"natives-linux"
|
|
||||||
arrayOf("Mac OS X", "Darwin").any { name.startsWith(it) } ->
|
|
||||||
"natives-macos${if (arch.startsWith("aarch64")) "-arm64" else ""}"
|
|
||||||
arrayOf("Windows").any { name.startsWith(it) } ->
|
|
||||||
if (arch.contains("64"))
|
|
||||||
"natives-windows${if (arch.startsWith("aarch64")) "-arm64" else ""}"
|
|
||||||
else
|
|
||||||
"natives-windows-x86"
|
|
||||||
else -> throw Error("Unrecognized or unsupported platform. Please set \"lwjglNatives\" manually")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(platform("org.lwjgl:lwjgl-bom:$lwjglVersion"))
|
|
||||||
|
|
||||||
implementation("org.lwjgl", "lwjgl")
|
|
||||||
implementation("org.lwjgl", "lwjgl-assimp")
|
|
||||||
implementation("org.lwjgl", "lwjgl-glfw")
|
|
||||||
implementation("org.lwjgl", "lwjgl-openal")
|
|
||||||
implementation("org.lwjgl", "lwjgl-opengl")
|
|
||||||
implementation("org.lwjgl", "lwjgl-remotery")
|
|
||||||
implementation("org.lwjgl", "lwjgl-stb")
|
|
||||||
runtimeOnly("org.lwjgl", "lwjgl", classifier = lwjglNatives)
|
|
||||||
runtimeOnly("org.lwjgl", "lwjgl-assimp", classifier = lwjglNatives)
|
|
||||||
runtimeOnly("org.lwjgl", "lwjgl-glfw", classifier = lwjglNatives)
|
|
||||||
runtimeOnly("org.lwjgl", "lwjgl-openal", classifier = lwjglNatives)
|
|
||||||
runtimeOnly("org.lwjgl", "lwjgl-opengl", classifier = lwjglNatives)
|
|
||||||
runtimeOnly("org.lwjgl", "lwjgl-remotery", classifier = lwjglNatives)
|
|
||||||
runtimeOnly("org.lwjgl", "lwjgl-stb", classifier = lwjglNatives)
|
|
||||||
implementation("org.joml", "joml", jomlVersion)
|
|
||||||
}
|
|
23
game/build.gradle.kts
Normal file
23
game/build.gradle.kts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
plugins {
|
||||||
|
application
|
||||||
|
java
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion.set(JavaLanguageVersion.of(22))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
application {
|
||||||
|
mainClass.set("dev.euph.game.Main")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(path = ":engine", configuration = "shadow"))
|
||||||
|
}
|
||||||
|
|
3
game/settings.gradle.kts
Normal file
3
game/settings.gradle.kts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
rootProject.name = "game"
|
||||||
|
include(":engine")
|
||||||
|
project(":engine").projectDir = file("../")
|
|
@ -19,13 +19,16 @@ import dev.euph.engine.util.Path;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
Window window = new Window(1200, 720, "Test Window");
|
Window window = new Window(1200, 720, "Test Window");
|
||||||
Scene scene = new Scene();
|
Scene scene = new Scene();
|
||||||
|
|
||||||
ShaderManager shaderManager = new ShaderManager();
|
InputStream fragmentShaderStream = Main.class.getResourceAsStream("/dev/euph/game/shader/default.fs.glsl");
|
||||||
|
InputStream vertexShaderStream = Main.class.getResourceAsStream("/dev/euph/game/shader/default.vs.glsl");
|
||||||
|
ShaderManager shaderManager = new ShaderManager(vertexShaderStream, fragmentShaderStream);
|
||||||
|
|
||||||
//Creating a Camera
|
//Creating a Camera
|
||||||
Entity cameraEntity = new Entity("Camera");
|
Entity cameraEntity = new Entity("Camera");
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
7
gradle/wrapper/gradle-wrapper.properties
vendored
7
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,7 +0,0 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
|
||||||
distributionPath=wrapper/dists
|
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
|
||||||
networkTimeout=10000
|
|
||||||
validateDistributionUrl=true
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
|
248
gradlew
vendored
248
gradlew
vendored
|
@ -1,248 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
#
|
|
||||||
# Copyright © 2015-2021 the original 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 POSIX generated by Gradle.
|
|
||||||
#
|
|
||||||
# Important for running:
|
|
||||||
#
|
|
||||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
|
||||||
# noncompliant, but you have some other compliant shell such as ksh or
|
|
||||||
# bash, then to run this script, type that shell name before the whole
|
|
||||||
# command line, like:
|
|
||||||
#
|
|
||||||
# ksh Gradle
|
|
||||||
#
|
|
||||||
# Busybox and similar reduced shells will NOT work, because this script
|
|
||||||
# requires all of these POSIX shell features:
|
|
||||||
# * functions;
|
|
||||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
|
||||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
|
||||||
# * compound commands having a testable exit status, especially «case»;
|
|
||||||
# * various built-in commands including «command», «set», and «ulimit».
|
|
||||||
#
|
|
||||||
# Important for patching:
|
|
||||||
#
|
|
||||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
|
||||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
|
||||||
#
|
|
||||||
# The "traditional" practice of packing multiple parameters into a
|
|
||||||
# space-separated string is a well documented source of bugs and security
|
|
||||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
|
||||||
# options in "$@", and eventually passing that to Java.
|
|
||||||
#
|
|
||||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
|
||||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
|
||||||
# see the in-line comments for details.
|
|
||||||
#
|
|
||||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
|
||||||
# Darwin, MinGW, and NonStop.
|
|
||||||
#
|
|
||||||
# (3) This script is generated from the Groovy template
|
|
||||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
|
||||||
# within the Gradle project.
|
|
||||||
#
|
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
|
||||||
#
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
|
||||||
|
|
||||||
# Resolve links: $0 may be a link
|
|
||||||
app_path=$0
|
|
||||||
|
|
||||||
# Need this for daisy-chained symlinks.
|
|
||||||
while
|
|
||||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
|
||||||
[ -h "$app_path" ]
|
|
||||||
do
|
|
||||||
ls=$( ls -ld "$app_path" )
|
|
||||||
link=${ls#*' -> '}
|
|
||||||
case $link in #(
|
|
||||||
/*) app_path=$link ;; #(
|
|
||||||
*) app_path=$APP_HOME$link ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# This is normally unused
|
|
||||||
# shellcheck disable=SC2034
|
|
||||||
APP_BASE_NAME=${0##*/}
|
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
|
||||||
MAX_FD=maximum
|
|
||||||
|
|
||||||
warn () {
|
|
||||||
echo "$*"
|
|
||||||
} >&2
|
|
||||||
|
|
||||||
die () {
|
|
||||||
echo
|
|
||||||
echo "$*"
|
|
||||||
echo
|
|
||||||
exit 1
|
|
||||||
} >&2
|
|
||||||
|
|
||||||
# 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 ;; #(
|
|
||||||
MSYS* | 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
|
|
||||||
if ! command -v java >/dev/null 2>&1
|
|
||||||
then
|
|
||||||
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
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
|
||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|
||||||
case $MAX_FD in #(
|
|
||||||
max*)
|
|
||||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
|
||||||
# shellcheck disable=SC3045
|
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
|
||||||
warn "Could not query maximum file descriptor limit"
|
|
||||||
esac
|
|
||||||
case $MAX_FD in #(
|
|
||||||
'' | soft) :;; #(
|
|
||||||
*)
|
|
||||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
|
||||||
# shellcheck disable=SC3045
|
|
||||||
ulimit -n "$MAX_FD" ||
|
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Collect all arguments for the java command, stacking in reverse order:
|
|
||||||
# * args from the command line
|
|
||||||
# * the main class name
|
|
||||||
# * -classpath
|
|
||||||
# * -D...appname settings
|
|
||||||
# * --module-path (only if needed)
|
|
||||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
|
||||||
|
|
||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
|
||||||
if "$cygwin" || "$msys" ; then
|
|
||||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
|
||||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
|
||||||
|
|
||||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
|
||||||
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
for arg do
|
|
||||||
if
|
|
||||||
case $arg in #(
|
|
||||||
-*) false ;; # don't mess with options #(
|
|
||||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
|
||||||
[ -e "$t" ] ;; #(
|
|
||||||
*) false ;;
|
|
||||||
esac
|
|
||||||
then
|
|
||||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
|
||||||
fi
|
|
||||||
# Roll the args list around exactly as many times as the number of
|
|
||||||
# args, so each arg winds up back in the position where it started, but
|
|
||||||
# possibly modified.
|
|
||||||
#
|
|
||||||
# NB: a `for` loop captures its iteration list before it begins, so
|
|
||||||
# changing the positional parameters here affects neither the number of
|
|
||||||
# iterations, nor the values presented in `arg`.
|
|
||||||
shift # remove old arg
|
|
||||||
set -- "$@" "$arg" # push replacement arg
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
# 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"'
|
|
||||||
|
|
||||||
# Collect all arguments for the java command;
|
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
|
||||||
# double quotes to make sure that they get re-expanded; and
|
|
||||||
# * put everything else in single quotes, so that it's not re-expanded.
|
|
||||||
|
|
||||||
set -- \
|
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
|
||||||
-classpath "$CLASSPATH" \
|
|
||||||
org.gradle.wrapper.GradleWrapperMain \
|
|
||||||
"$@"
|
|
||||||
|
|
||||||
# Stop when "xargs" is not available.
|
|
||||||
if ! command -v xargs >/dev/null 2>&1
|
|
||||||
then
|
|
||||||
die "xargs is not available"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Use "xargs" to parse quoted args.
|
|
||||||
#
|
|
||||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
|
||||||
#
|
|
||||||
# In Bash we could simply go:
|
|
||||||
#
|
|
||||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
|
||||||
# set -- "${ARGS[@]}" "$@"
|
|
||||||
#
|
|
||||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
|
||||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
|
||||||
# character that might be a shell metacharacter, then use eval to reverse
|
|
||||||
# that process (while maintaining the separation between arguments), and wrap
|
|
||||||
# the whole thing up as a single "set" statement.
|
|
||||||
#
|
|
||||||
# This will of course break if any of these variables contains a newline or
|
|
||||||
# an unmatched quote.
|
|
||||||
#
|
|
||||||
|
|
||||||
eval "set -- $(
|
|
||||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
|
||||||
xargs -n1 |
|
|
||||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
|
||||||
tr '\n' ' '
|
|
||||||
)" '"$@"'
|
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
|
175
profiler/LICENSE
vendored
175
profiler/LICENSE
vendored
|
@ -1,175 +0,0 @@
|
||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
10142
profiler/lib/Remotery.c
vendored
10142
profiler/lib/Remotery.c
vendored
File diff suppressed because it is too large
Load diff
1095
profiler/lib/Remotery.h
vendored
1095
profiler/lib/Remotery.h
vendored
File diff suppressed because it is too large
Load diff
59
profiler/lib/RemoteryMetal.mm
vendored
59
profiler/lib/RemoteryMetal.mm
vendored
|
@ -1,59 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright 2014-2018 Celtoys Ltd
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
// http://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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <Foundation/NSThread.h>
|
|
||||||
#include <Foundation/NSDictionary.h>
|
|
||||||
#include <Foundation/NSString.h>
|
|
||||||
|
|
||||||
#import <Metal/Metal.h>
|
|
||||||
|
|
||||||
// Store command buffer in thread-local so that each thread can point to its own
|
|
||||||
static void SetCommandBuffer(id command_buffer)
|
|
||||||
{
|
|
||||||
NSMutableDictionary* thread_data = [[NSThread currentThread] threadDictionary];
|
|
||||||
thread_data[@"rmtMTLCommandBuffer"] = command_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static id GetCommandBuffer()
|
|
||||||
{
|
|
||||||
NSMutableDictionary* thread_data = [[NSThread currentThread] threadDictionary];
|
|
||||||
return thread_data[@"rmtMTLCommandBuffer"];
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void _rmt_BindMetal(id command_buffer)
|
|
||||||
{
|
|
||||||
SetCommandBuffer(command_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void _rmt_UnbindMetal()
|
|
||||||
{
|
|
||||||
SetCommandBuffer(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Needs to be in the same lib for this to work
|
|
||||||
extern "C" unsigned long long rmtMetal_usGetTime();
|
|
||||||
|
|
||||||
static void SetTimestamp(void* data)
|
|
||||||
{
|
|
||||||
*((unsigned long long*)data) = rmtMetal_usGetTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void rmtMetal_MeasureCommandBuffer(unsigned long long* out_start, unsigned long long* out_end, unsigned int* out_ready)
|
|
||||||
{
|
|
||||||
id command_buffer = GetCommandBuffer();
|
|
||||||
[command_buffer addScheduledHandler:^(id <MTLCommandBuffer>){ SetTimestamp(out_start); }];
|
|
||||||
[command_buffer addCompletedHandler:^(id <MTLCommandBuffer>){ SetTimestamp(out_end); *out_ready = 1; }];
|
|
||||||
}
|
|
232
profiler/readme.md
vendored
232
profiler/readme.md
vendored
|
@ -1,232 +0,0 @@
|
||||||
Remotery
|
|
||||||
--------
|
|
||||||
|
|
||||||
[![Build](https://github.com/Celtoys/Remotery/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/Celtoys/Remotery/actions/workflows/build.yml)
|
|
||||||
|
|
||||||
A realtime CPU/GPU profiler hosted in a single C file with a viewer that runs in a web browser.
|
|
||||||
|
|
||||||
![screenshot](screenshot.png?raw=true)
|
|
||||||
|
|
||||||
Features:
|
|
||||||
|
|
||||||
* Lightweight instrumentation of multiple threads running on the CPU.
|
|
||||||
* Web viewer that runs in Chrome, Firefox and Safari; on Desktops, Mobiles or Tablets.
|
|
||||||
* GPU UI rendering, bypassing the DOM completely, for real-time 60hz viewer updates at 10,000x the performance.
|
|
||||||
* Automatic thread sampler that tells you what processor cores your threads are running on without requiring Administrator privileges.
|
|
||||||
* Drop saved traces onto the Remotery window to load historical runs for inspection.
|
|
||||||
* Console output for logging text.
|
|
||||||
* Console input for sending commands to your game.
|
|
||||||
* A Property API for recording named/typed values over time, alongside samples.
|
|
||||||
* Profiles itself and shows how it's performing in the viewer.
|
|
||||||
|
|
||||||
Supported Profiling Platforms:
|
|
||||||
|
|
||||||
* Windows 7/8/10/11/UWP (Hololens), Linux, OSX, iOS, Android, Xbox One/Series, Free BSD.
|
|
||||||
|
|
||||||
Supported GPU Profiling APIS:
|
|
||||||
|
|
||||||
* D3D 11/12, OpenGL, CUDA, Metal.
|
|
||||||
|
|
||||||
Compiling
|
|
||||||
---------
|
|
||||||
|
|
||||||
* Windows (MSVC) - add lib/Remotery.c and lib/Remotery.h to your program. Set include
|
|
||||||
directories to add Remotery/lib path. The required library ws2_32.lib should be picked
|
|
||||||
up through the use of the #pragma comment(lib, "ws2_32.lib") directive in Remotery.c.
|
|
||||||
|
|
||||||
* Mac OS X (XCode) - simply add lib/Remotery.c, lib/Remotery.h and lib/Remotery.mm to your program.
|
|
||||||
|
|
||||||
* Linux (GCC) - add the source in lib folder. Compilation of the code requires -pthreads for
|
|
||||||
library linkage. For example to compile the same run: cc lib/Remotery.c sample/sample.c
|
|
||||||
-I lib -pthread -lm
|
|
||||||
|
|
||||||
* FreeBSD - the easiest way is to take a look at the official port
|
|
||||||
([devel/remotery](https://www.freshports.org/devel/remotery/)) and modify the port's
|
|
||||||
Makefile if needed. There is also a package available via `pkg install remotery`.
|
|
||||||
|
|
||||||
You can define some extra macros to modify what features are compiled into Remotery:
|
|
||||||
|
|
||||||
Macro Default Description
|
|
||||||
|
|
||||||
RMT_ENABLED 1 Disable this to not include any bits of Remotery in your build
|
|
||||||
RMT_USE_TINYCRT 0 Used by the Celtoys TinyCRT library (not released yet)
|
|
||||||
RMT_USE_CUDA 0 Assuming CUDA headers/libs are setup, allow CUDA profiling
|
|
||||||
RMT_USE_D3D11 0 Assuming Direct3D 11 headers/libs are setup, allow D3D11 GPU profiling
|
|
||||||
RMT_USE_D3D12 0 Allow D3D12 GPU profiling
|
|
||||||
RMT_USE_OPENGL 0 Allow OpenGL GPU profiling (dynamically links OpenGL libraries on available platforms)
|
|
||||||
RMT_USE_METAL 0 Allow Metal profiling of command buffers
|
|
||||||
|
|
||||||
|
|
||||||
Basic Use
|
|
||||||
---------
|
|
||||||
|
|
||||||
See the sample directory for further examples. A quick example:
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
// Create the main instance of Remotery.
|
|
||||||
// You need only do this once per program.
|
|
||||||
Remotery* rmt;
|
|
||||||
rmt_CreateGlobalInstance(&rmt);
|
|
||||||
|
|
||||||
// Explicit begin/end for C
|
|
||||||
{
|
|
||||||
rmt_BeginCPUSample(LogText, 0);
|
|
||||||
rmt_LogText("Time me, please!");
|
|
||||||
rmt_EndCPUSample();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scoped begin/end for C++
|
|
||||||
{
|
|
||||||
rmt_ScopedCPUSample(LogText, 0);
|
|
||||||
rmt_LogText("Time me, too!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy the main instance of Remotery.
|
|
||||||
rmt_DestroyGlobalInstance(rmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Running the Viewer
|
|
||||||
------------------
|
|
||||||
|
|
||||||
Double-click or launch `vis/index.html` from the browser.
|
|
||||||
|
|
||||||
|
|
||||||
Sampling CUDA GPU activity
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
Remotery allows for profiling multiple threads of CUDA execution using different asynchronous streams
|
|
||||||
that must all share the same context. After initialising both Remotery and CUDA you need to bind the
|
|
||||||
two together using the call:
|
|
||||||
|
|
||||||
rmtCUDABind bind;
|
|
||||||
bind.context = m_Context;
|
|
||||||
bind.CtxSetCurrent = &cuCtxSetCurrent;
|
|
||||||
bind.CtxGetCurrent = &cuCtxGetCurrent;
|
|
||||||
bind.EventCreate = &cuEventCreate;
|
|
||||||
bind.EventDestroy = &cuEventDestroy;
|
|
||||||
bind.EventRecord = &cuEventRecord;
|
|
||||||
bind.EventQuery = &cuEventQuery;
|
|
||||||
bind.EventElapsedTime = &cuEventElapsedTime;
|
|
||||||
rmt_BindCUDA(&bind);
|
|
||||||
|
|
||||||
Explicitly pointing to the CUDA interface allows Remotery to be included anywhere in your project without
|
|
||||||
need for you to link with the required CUDA libraries. After the bind completes you can safely sample any
|
|
||||||
CUDA activity:
|
|
||||||
|
|
||||||
CUstream stream;
|
|
||||||
|
|
||||||
// Explicit begin/end for C
|
|
||||||
{
|
|
||||||
rmt_BeginCUDASample(UnscopedSample, stream);
|
|
||||||
// ... CUDA code ...
|
|
||||||
rmt_EndCUDASample(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scoped begin/end for C++
|
|
||||||
{
|
|
||||||
rmt_ScopedCUDASample(ScopedSample, stream);
|
|
||||||
// ... CUDA code ...
|
|
||||||
}
|
|
||||||
|
|
||||||
Remotery supports only one context for all threads and will use cuCtxGetCurrent and cuCtxSetCurrent to
|
|
||||||
ensure the current thread has the context you specify in rmtCUDABind.context.
|
|
||||||
|
|
||||||
|
|
||||||
Sampling Direct3D 11 GPU activity
|
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
Remotery allows sampling of D3D11 GPU activity on multiple devices on multiple threads. After initialising Remotery, you need to bind it to D3D11 with a single call from the thread that owns the device context:
|
|
||||||
|
|
||||||
// Parameters are ID3D11Device* and ID3D11DeviceContext*
|
|
||||||
rmt_BindD3D11(d3d11_device, d3d11_context);
|
|
||||||
|
|
||||||
Sampling is then a simple case of:
|
|
||||||
|
|
||||||
// Explicit begin/end for C
|
|
||||||
{
|
|
||||||
rmt_BeginD3D11Sample(UnscopedSample);
|
|
||||||
// ... D3D code ...
|
|
||||||
rmt_EndD3D11Sample();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scoped begin/end for C++
|
|
||||||
{
|
|
||||||
rmt_ScopedD3D11Sample(ScopedSample);
|
|
||||||
// ... D3D code ...
|
|
||||||
}
|
|
||||||
|
|
||||||
Subsequent sampling calls from the same thread will use that device/context combination. When you shutdown your D3D11 device and context, ensure you notify Remotery before shutting down Remotery itself:
|
|
||||||
|
|
||||||
rmt_UnbindD3D11();
|
|
||||||
|
|
||||||
|
|
||||||
Sampling OpenGL GPU activity
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
Remotery allows sampling of GPU activity on your main OpenGL context. After initialising Remotery, you need
|
|
||||||
to bind it to OpenGL with the single call:
|
|
||||||
|
|
||||||
rmt_BindOpenGL();
|
|
||||||
|
|
||||||
Sampling is then a simple case of:
|
|
||||||
|
|
||||||
// Explicit begin/end for C
|
|
||||||
{
|
|
||||||
rmt_BeginOpenGLSample(UnscopedSample);
|
|
||||||
// ... OpenGL code ...
|
|
||||||
rmt_EndOpenGLSample();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scoped begin/end for C++
|
|
||||||
{
|
|
||||||
rmt_ScopedOpenGLSample(ScopedSample);
|
|
||||||
// ... OpenGL code ...
|
|
||||||
}
|
|
||||||
|
|
||||||
Support for multiple contexts can be added pretty easily if there is demand for the feature. When you shutdown
|
|
||||||
your OpenGL device and context, ensure you notify Remotery before shutting down Remotery itself:
|
|
||||||
|
|
||||||
rmt_UnbindOpenGL();
|
|
||||||
|
|
||||||
|
|
||||||
Sampling Metal GPU activity
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Remotery can sample Metal command buffers issued to the GPU from multiple threads. As the Metal API does not
|
|
||||||
support finer grained profiling, samples will return only the timing of the bound command buffer, irrespective
|
|
||||||
of how many you issue. As such, make sure you bind and sample the command buffer for each call site:
|
|
||||||
|
|
||||||
rmt_BindMetal(mtl_command_buffer);
|
|
||||||
rmt_ScopedMetalSample(command_buffer_name);
|
|
||||||
|
|
||||||
The C API supports begin/end also:
|
|
||||||
|
|
||||||
rmt_BindMetal(mtl_command_buffer);
|
|
||||||
rmt_BeginMetalSample(command_buffer_name);
|
|
||||||
...
|
|
||||||
rmt_EndMetalSample();
|
|
||||||
|
|
||||||
|
|
||||||
Applying Configuration Settings
|
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
Before creating your Remotery instance, you can configure its behaviour by retrieving its settings object:
|
|
||||||
|
|
||||||
rmtSettings* settings = rmt_Settings();
|
|
||||||
|
|
||||||
Some important settings are:
|
|
||||||
|
|
||||||
// Redirect any Remotery allocations to your own malloc/free, with an additional context pointer
|
|
||||||
// that gets passed to your callbacks.
|
|
||||||
settings->malloc;
|
|
||||||
settings->free;
|
|
||||||
settings->mm_context;
|
|
||||||
|
|
||||||
// Specify an input pipelineStage that receives text input from the Remotery console, with an additional
|
|
||||||
// context pointer that gets passed to your callback.
|
|
||||||
// The pipelineStage will be called from the Remotery thread so synchronization with a mutex or atomics
|
|
||||||
// might be needed to avoid race conditions with your threads.
|
|
||||||
settings->input_handler;
|
|
||||||
settings->input_handler_context;
|
|
184
profiler/sample/dump.c
vendored
184
profiler/sample/dump.c
vendored
|
@ -1,184 +0,0 @@
|
||||||
#include <stdlib.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "../lib/Remotery.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
rmt_PropertyDefine_Group(Game, "Game Properties");
|
|
||||||
rmt_PropertyDefine_Bool(WasUpdated, RMT_FALSE, FrameReset, "Was the game loop executed this frame?", &Game);
|
|
||||||
rmt_PropertyDefine_U32(RecursiveDepth, 0, FrameReset, "How deep did we go in recursiveFunction?", &Game);
|
|
||||||
rmt_PropertyDefine_F32(Accumulated, 0, FrameReset, "What was the latest value?", &Game);
|
|
||||||
rmt_PropertyDefine_U32(FrameCounter, 0, NoFlags, "What is the current frame number?", &Game);
|
|
||||||
|
|
||||||
|
|
||||||
void aggregateFunction() {
|
|
||||||
rmt_BeginCPUSample(aggregate, RMTSF_Aggregate);
|
|
||||||
rmt_EndCPUSample();
|
|
||||||
}
|
|
||||||
void recursiveFunction(int depth) {
|
|
||||||
rmt_PropertySet_U32(RecursiveDepth, depth);
|
|
||||||
rmt_BeginCPUSample(recursive, RMTSF_Recursive);
|
|
||||||
if (depth < 5) {
|
|
||||||
recursiveFunction(depth + 1);
|
|
||||||
}
|
|
||||||
rmt_EndCPUSample();
|
|
||||||
}
|
|
||||||
|
|
||||||
double delay() {
|
|
||||||
int i, end;
|
|
||||||
double j = 0;
|
|
||||||
|
|
||||||
rmt_BeginCPUSample(delay, 0);
|
|
||||||
for( i = 0, end = rand()/100; i < end; ++i ) {
|
|
||||||
double v = sin(i);
|
|
||||||
j += v;
|
|
||||||
|
|
||||||
rmt_PropertyAdd_F32(Accumulated, v);
|
|
||||||
}
|
|
||||||
recursiveFunction(0);
|
|
||||||
aggregateFunction();
|
|
||||||
aggregateFunction();
|
|
||||||
aggregateFunction();
|
|
||||||
rmt_EndCPUSample();
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
|
|
||||||
void printIndent(int indent)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < indent; ++i) {
|
|
||||||
printf(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void printSample(rmtSample* sample, int indent)
|
|
||||||
{
|
|
||||||
const char* name = rmt_SampleGetName(sample);
|
|
||||||
rmtU32 callcount = rmt_SampleGetCallCount(sample);
|
|
||||||
rmtU64 time = rmt_SampleGetTime(sample);
|
|
||||||
rmtU64 self_time = rmt_SampleGetSelfTime(sample);
|
|
||||||
rmtSampleType type = rmt_SampleGetType(sample);
|
|
||||||
rmtU8 r, g, b;
|
|
||||||
rmt_SampleGetColour(sample, &r, &g, &b);
|
|
||||||
|
|
||||||
printIndent(indent); printf("%s %u time: %llu self: %llu type: %d color: 0x%02x%02x%02x\n", name, callcount, time, self_time, type, r, g, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
void printTree(rmtSample* sample, int indent)
|
|
||||||
{
|
|
||||||
rmtSampleIterator iter;
|
|
||||||
|
|
||||||
printSample(sample, indent);
|
|
||||||
|
|
||||||
rmt_IterateChildren(&iter, sample);
|
|
||||||
while (rmt_IterateNext(&iter)) {
|
|
||||||
printTree(iter.sample, indent+1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dumpTree(void* ctx, rmtSampleTree* sample_tree)
|
|
||||||
{
|
|
||||||
rmtSample* root = rmt_SampleTreeGetRootSample(sample_tree);
|
|
||||||
const char* thread_name = rmt_SampleTreeGetThreadName(sample_tree);
|
|
||||||
if (strcmp("Remotery", thread_name) == 0)
|
|
||||||
{
|
|
||||||
return; // to minimize the verbosity in this example
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("// ******************** DUMP TREE: %s ************************\n", thread_name);
|
|
||||||
|
|
||||||
printTree(root, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void printProperty(rmtProperty* property, int indent)
|
|
||||||
{
|
|
||||||
rmtPropertyIterator iter;
|
|
||||||
|
|
||||||
const char* name = rmt_PropertyGetName(property);
|
|
||||||
rmtPropertyType type = rmt_PropertyGetType(property);
|
|
||||||
rmtPropertyValue value = rmt_PropertyGetValue(property);
|
|
||||||
|
|
||||||
printIndent(indent); printf("%s: ", name);
|
|
||||||
|
|
||||||
switch(type)
|
|
||||||
{
|
|
||||||
case RMT_PropertyType_rmtBool: printf("%s\n", value.Bool ? "true":"false"); break;
|
|
||||||
case RMT_PropertyType_rmtS32: printf("%d\n", value.S32); break;
|
|
||||||
case RMT_PropertyType_rmtU32: printf("%u\n", value.U32); break;
|
|
||||||
case RMT_PropertyType_rmtF32: printf("%f\n", value.F32); break;
|
|
||||||
case RMT_PropertyType_rmtS64: printf("%lld\n", value.S64); break;
|
|
||||||
case RMT_PropertyType_rmtU64: printf("%llu\n", value.U64); break;
|
|
||||||
case RMT_PropertyType_rmtF64: printf("%g\n", value.F64); break;
|
|
||||||
case RMT_PropertyType_rmtGroup: printf("\n"); break;
|
|
||||||
default: break;
|
|
||||||
};
|
|
||||||
|
|
||||||
rmt_PropertyIterateChildren(&iter, property);
|
|
||||||
while (rmt_PropertyIterateNext(&iter)) {
|
|
||||||
printProperty(iter.property, indent + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dumpProperties(void* ctx, rmtProperty* root)
|
|
||||||
{
|
|
||||||
rmtPropertyIterator iter;
|
|
||||||
printf("// ******************** DUMP PROPERTIES: ************************\n");
|
|
||||||
|
|
||||||
rmt_PropertyIterateChildren(&iter, root);
|
|
||||||
while (rmt_PropertyIterateNext(&iter)) {
|
|
||||||
printProperty(iter.property, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int sig = 0;
|
|
||||||
|
|
||||||
/// Allow to close cleanly with ctrl + c
|
|
||||||
void sigintHandler(int sig_num) {
|
|
||||||
sig = sig_num;
|
|
||||||
printf("Interrupted\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
Remotery* rmt;
|
|
||||||
rmtError error;
|
|
||||||
|
|
||||||
signal(SIGINT, sigintHandler);
|
|
||||||
|
|
||||||
rmtSettings* settings = rmt_Settings();
|
|
||||||
if (settings)
|
|
||||||
{
|
|
||||||
settings->sampletree_handler = dumpTree;
|
|
||||||
settings->sampletree_context = 0;
|
|
||||||
|
|
||||||
settings->snapshot_callback = dumpProperties;
|
|
||||||
settings->snapshot_context = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = rmt_CreateGlobalInstance(&rmt);
|
|
||||||
|
|
||||||
if( RMT_ERROR_NONE != error) {
|
|
||||||
printf("Error launching Remotery %d\n", error);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int max_count = 5;
|
|
||||||
|
|
||||||
while (sig == 0 && --max_count > 0) {
|
|
||||||
rmt_LogText("start profiling");
|
|
||||||
delay();
|
|
||||||
rmt_LogText("end profiling");
|
|
||||||
|
|
||||||
rmt_PropertySet_Bool(WasUpdated, RMT_TRUE);
|
|
||||||
rmt_PropertyAdd_U32(FrameCounter, 1);
|
|
||||||
|
|
||||||
rmt_PropertySnapshotAll();
|
|
||||||
rmt_PropertyFrameResetAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
rmt_DestroyGlobalInstance(rmt);
|
|
||||||
printf("Cleaned up and quit\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
64
profiler/sample/sample.c
vendored
64
profiler/sample/sample.c
vendored
|
@ -1,64 +0,0 @@
|
||||||
#include <stdlib.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "../lib/Remotery.h"
|
|
||||||
|
|
||||||
void aggregateFunction() {
|
|
||||||
rmt_BeginCPUSample(aggregate, RMTSF_Aggregate);
|
|
||||||
rmt_EndCPUSample();
|
|
||||||
}
|
|
||||||
void recursiveFunction(int depth) {
|
|
||||||
rmt_BeginCPUSample(recursive, RMTSF_Recursive);
|
|
||||||
if (depth < 5) {
|
|
||||||
recursiveFunction(depth + 1);
|
|
||||||
}
|
|
||||||
rmt_EndCPUSample();
|
|
||||||
}
|
|
||||||
|
|
||||||
double delay() {
|
|
||||||
int i, end;
|
|
||||||
double j = 0;
|
|
||||||
|
|
||||||
rmt_BeginCPUSample(delay, 0);
|
|
||||||
for( i = 0, end = rand()/100; i < end; ++i ) {
|
|
||||||
j += sin(i);
|
|
||||||
}
|
|
||||||
recursiveFunction(0);
|
|
||||||
aggregateFunction();
|
|
||||||
aggregateFunction();
|
|
||||||
aggregateFunction();
|
|
||||||
rmt_EndCPUSample();
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sig = 0;
|
|
||||||
|
|
||||||
/// Allow to close cleanly with ctrl + c
|
|
||||||
void sigintHandler(int sig_num) {
|
|
||||||
sig = sig_num;
|
|
||||||
printf("Interrupted\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main( ) {
|
|
||||||
Remotery *rmt;
|
|
||||||
rmtError error;
|
|
||||||
|
|
||||||
signal(SIGINT, sigintHandler);
|
|
||||||
|
|
||||||
error = rmt_CreateGlobalInstance(&rmt);
|
|
||||||
if( RMT_ERROR_NONE != error) {
|
|
||||||
printf("Error launching Remotery %d\n", error);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (sig == 0) {
|
|
||||||
rmt_LogText("start profiling");
|
|
||||||
delay();
|
|
||||||
rmt_LogText("end profiling");
|
|
||||||
}
|
|
||||||
|
|
||||||
rmt_DestroyGlobalInstance(rmt);
|
|
||||||
printf("Cleaned up and quit\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
BIN
profiler/screenshot.png
vendored
BIN
profiler/screenshot.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 164 KiB |
218
profiler/vis/Code/Console.js
vendored
218
profiler/vis/Code/Console.js
vendored
|
@ -1,218 +0,0 @@
|
||||||
|
|
||||||
Console = (function()
|
|
||||||
{
|
|
||||||
var BORDER = 10;
|
|
||||||
var HEIGHT = 200;
|
|
||||||
|
|
||||||
|
|
||||||
function Console(wm, server)
|
|
||||||
{
|
|
||||||
// Create the window and its controls
|
|
||||||
this.Window = wm.AddWindow("Console", 10, 10, 100, 100);
|
|
||||||
this.PageContainer = this.Window.AddControlNew(new WM.Container(10, 10, 400, 160));
|
|
||||||
DOM.Node.AddClass(this.PageContainer.Node, "ConsoleText");
|
|
||||||
this.AppContainer = this.Window.AddControlNew(new WM.Container(10, 10, 400, 160));
|
|
||||||
DOM.Node.AddClass(this.AppContainer.Node, "ConsoleText");
|
|
||||||
this.UserInput = this.Window.AddControlNew(new WM.EditBox(10, 5, 400, 30, "Input", ""));
|
|
||||||
this.UserInput.SetChangeHandler(Bind(ProcessInput, this));
|
|
||||||
this.Window.ShowNoAnim();
|
|
||||||
|
|
||||||
// This accumulates log text as fast as is required
|
|
||||||
this.PageTextBuffer = "";
|
|
||||||
this.PageTextUpdatePending = false;
|
|
||||||
this.AppTextBuffer = "";
|
|
||||||
this.AppTextUpdatePending = false;
|
|
||||||
|
|
||||||
// Setup command history control
|
|
||||||
this.CommandHistory = LocalStore.Get("App", "Global", "CommandHistory", [ ]);
|
|
||||||
this.CommandIndex = 0;
|
|
||||||
this.MaxNbCommands = 10000;
|
|
||||||
DOM.Event.AddHandler(this.UserInput.EditNode, "keydown", Bind(OnKeyPress, this));
|
|
||||||
DOM.Event.AddHandler(this.UserInput.EditNode, "focus", Bind(OnFocus, this));
|
|
||||||
|
|
||||||
// At a much lower frequency this will update the console window
|
|
||||||
window.setInterval(Bind(UpdateHTML, this), 500);
|
|
||||||
|
|
||||||
// Setup log requests from the server
|
|
||||||
this.Server = server;
|
|
||||||
server.SetConsole(this);
|
|
||||||
server.AddMessageHandler("LOGM", Bind(OnLog, this));
|
|
||||||
|
|
||||||
this.Window.SetOnResize(Bind(OnUserResize, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Console.prototype.Log = function(text)
|
|
||||||
{
|
|
||||||
this.PageTextBuffer = LogText(this.PageTextBuffer, text);
|
|
||||||
this.PageTextUpdatePending = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Console.prototype.WindowResized = function(width, height)
|
|
||||||
{
|
|
||||||
// Place window
|
|
||||||
this.Window.SetPosition(BORDER, height - BORDER - 200);
|
|
||||||
this.Window.SetSize(width - 2 * BORDER, HEIGHT);
|
|
||||||
|
|
||||||
ResizeInternals(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Console.prototype.TriggerUpdate = function()
|
|
||||||
{
|
|
||||||
this.AppTextUpdatePending = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnLog(self, socket, data_view_reader)
|
|
||||||
{
|
|
||||||
var text = data_view_reader.GetString();
|
|
||||||
self.AppTextBuffer = LogText(self.AppTextBuffer, text);
|
|
||||||
|
|
||||||
// Don't register text as updating if disconnected as this implies a trace is being loaded, which we want to speed up
|
|
||||||
if (self.Server.Connected())
|
|
||||||
{
|
|
||||||
self.AppTextUpdatePending = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function LogText(existing_text, new_text)
|
|
||||||
{
|
|
||||||
// Filter the text a little to make it safer
|
|
||||||
if (new_text == null)
|
|
||||||
new_text = "NULL";
|
|
||||||
|
|
||||||
// Find and convert any HTML entities, ensuring the browser doesn't parse any embedded HTML code
|
|
||||||
// This also allows the log to contain arbitrary C++ code (e.g. assert comparison operators)
|
|
||||||
new_text = Convert.string_to_html_entities(new_text);
|
|
||||||
|
|
||||||
// Prefix date and end with new line
|
|
||||||
var d = new Date();
|
|
||||||
new_text = "[" + d.toLocaleTimeString() + "] " + new_text + "<br>";
|
|
||||||
|
|
||||||
// Append to local text buffer and ensure clip the oldest text to ensure a max size
|
|
||||||
existing_text = existing_text + new_text;
|
|
||||||
var max_len = 100 * 1024;
|
|
||||||
var len = existing_text.length;
|
|
||||||
if (len > max_len)
|
|
||||||
existing_text = existing_text.substr(len - max_len, max_len);
|
|
||||||
|
|
||||||
return existing_text;
|
|
||||||
}
|
|
||||||
|
|
||||||
function OnUserResize(self, evt)
|
|
||||||
{
|
|
||||||
ResizeInternals(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ResizeInternals(self)
|
|
||||||
{
|
|
||||||
// Place controls
|
|
||||||
var parent_size = self.Window.Size;
|
|
||||||
var mid_w = parent_size[0] / 3;
|
|
||||||
self.UserInput.SetPosition(BORDER, parent_size[1] - 2 * BORDER - 30);
|
|
||||||
self.UserInput.SetSize(parent_size[0] - 100, 18);
|
|
||||||
var output_height = self.UserInput.Position[1] - 2 * BORDER;
|
|
||||||
self.PageContainer.SetPosition(BORDER, BORDER);
|
|
||||||
self.PageContainer.SetSize(mid_w - 2 * BORDER, output_height);
|
|
||||||
self.AppContainer.SetPosition(mid_w, BORDER);
|
|
||||||
self.AppContainer.SetSize(parent_size[0] - mid_w - BORDER, output_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function UpdateHTML(self)
|
|
||||||
{
|
|
||||||
// Reset the current text buffer as html
|
|
||||||
|
|
||||||
if (self.PageTextUpdatePending)
|
|
||||||
{
|
|
||||||
var page_node = self.PageContainer.Node;
|
|
||||||
page_node.innerHTML = self.PageTextBuffer;
|
|
||||||
page_node.scrollTop = page_node.scrollHeight;
|
|
||||||
self.PageTextUpdatePending = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.AppTextUpdatePending)
|
|
||||||
{
|
|
||||||
var app_node = self.AppContainer.Node;
|
|
||||||
app_node.innerHTML = self.AppTextBuffer;
|
|
||||||
app_node.scrollTop = app_node.scrollHeight;
|
|
||||||
self.AppTextUpdatePending = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function ProcessInput(self, node)
|
|
||||||
{
|
|
||||||
// Send the message exactly
|
|
||||||
var msg = node.value;
|
|
||||||
self.Server.Send("CONI" + msg);
|
|
||||||
|
|
||||||
// Emit to console and clear
|
|
||||||
self.Log("> " + msg);
|
|
||||||
self.UserInput.SetValue("");
|
|
||||||
|
|
||||||
// Keep track of recently issued commands, with an upper bound
|
|
||||||
self.CommandHistory.push(msg);
|
|
||||||
var extra_commands = self.CommandHistory.length - self.MaxNbCommands;
|
|
||||||
if (extra_commands > 0)
|
|
||||||
self.CommandHistory.splice(0, extra_commands);
|
|
||||||
|
|
||||||
// Set command history index to the most recent command
|
|
||||||
self.CommandIndex = self.CommandHistory.length;
|
|
||||||
|
|
||||||
// Backup to local store
|
|
||||||
LocalStore.Set("App", "Global", "CommandHistory", self.CommandHistory);
|
|
||||||
|
|
||||||
// Keep focus with the edit box
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnKeyPress(self, evt)
|
|
||||||
{
|
|
||||||
evt = DOM.Event.Get(evt);
|
|
||||||
|
|
||||||
if (evt.keyCode == Keyboard.Codes.UP)
|
|
||||||
{
|
|
||||||
if (self.CommandHistory.length > 0)
|
|
||||||
{
|
|
||||||
// Cycle backwards through the command history
|
|
||||||
self.CommandIndex--;
|
|
||||||
if (self.CommandIndex < 0)
|
|
||||||
self.CommandIndex = self.CommandHistory.length - 1;
|
|
||||||
var command = self.CommandHistory[self.CommandIndex];
|
|
||||||
self.UserInput.SetValue(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stops default behaviour of moving cursor to the beginning
|
|
||||||
DOM.Event.StopDefaultAction(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (evt.keyCode == Keyboard.Codes.DOWN)
|
|
||||||
{
|
|
||||||
if (self.CommandHistory.length > 0)
|
|
||||||
{
|
|
||||||
// Cycle fowards through the command history
|
|
||||||
self.CommandIndex = (self.CommandIndex + 1) % self.CommandHistory.length;
|
|
||||||
var command = self.CommandHistory[self.CommandIndex];
|
|
||||||
self.UserInput.SetValue(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stops default behaviour of moving cursor to the end
|
|
||||||
DOM.Event.StopDefaultAction(evt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnFocus(self)
|
|
||||||
{
|
|
||||||
// Reset command index on focus
|
|
||||||
self.CommandIndex = self.CommandHistory.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return Console;
|
|
||||||
})();
|
|
94
profiler/vis/Code/DataViewReader.js
vendored
94
profiler/vis/Code/DataViewReader.js
vendored
|
@ -1,94 +0,0 @@
|
||||||
|
|
||||||
//
|
|
||||||
// Simple wrapper around DataView that auto-advances the read offset and provides
|
|
||||||
// a few common data type conversions specific to this app
|
|
||||||
//
|
|
||||||
DataViewReader = (function ()
|
|
||||||
{
|
|
||||||
function DataViewReader(data_view, offset)
|
|
||||||
{
|
|
||||||
this.DataView = data_view;
|
|
||||||
this.Offset = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataViewReader.prototype.AtEnd = function()
|
|
||||||
{
|
|
||||||
return this.Offset >= this.DataView.byteLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataViewReader.prototype.GetBool = function ()
|
|
||||||
{
|
|
||||||
let v = this.DataView.getUint8(this.Offset);
|
|
||||||
this.Offset++;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataViewReader.prototype.GetUInt8 = function ()
|
|
||||||
{
|
|
||||||
let v = this.DataView.getUint8(this.Offset);
|
|
||||||
this.Offset++;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataViewReader.prototype.GetInt32 = function ()
|
|
||||||
{
|
|
||||||
let v = this.DataView.getInt32(this.Offset, true);
|
|
||||||
this.Offset += 4;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataViewReader.prototype.GetUInt32 = function ()
|
|
||||||
{
|
|
||||||
var v = this.DataView.getUint32(this.Offset, true);
|
|
||||||
this.Offset += 4;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataViewReader.prototype.GetFloat32 = function ()
|
|
||||||
{
|
|
||||||
const v = this.DataView.getFloat32(this.Offset, true);
|
|
||||||
this.Offset += 4;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataViewReader.prototype.GetInt64 = function ()
|
|
||||||
{
|
|
||||||
var v = this.DataView.getFloat64(this.Offset, true);
|
|
||||||
this.Offset += 8;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataViewReader.prototype.GetUInt64 = function ()
|
|
||||||
{
|
|
||||||
var v = this.DataView.getFloat64(this.Offset, true);
|
|
||||||
this.Offset += 8;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataViewReader.prototype.GetFloat64 = function ()
|
|
||||||
{
|
|
||||||
var v = this.DataView.getFloat64(this.Offset, true);
|
|
||||||
this.Offset += 8;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataViewReader.prototype.GetStringOfLength = function (string_length)
|
|
||||||
{
|
|
||||||
var string = "";
|
|
||||||
for (var i = 0; i < string_length; i++)
|
|
||||||
{
|
|
||||||
string += String.fromCharCode(this.DataView.getInt8(this.Offset));
|
|
||||||
this.Offset++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataViewReader.prototype.GetString = function ()
|
|
||||||
{
|
|
||||||
var string_length = this.GetUInt32();
|
|
||||||
return this.GetStringOfLength(string_length);
|
|
||||||
}
|
|
||||||
|
|
||||||
return DataViewReader;
|
|
||||||
})();
|
|
123
profiler/vis/Code/GLCanvas.js
vendored
123
profiler/vis/Code/GLCanvas.js
vendored
|
@ -1,123 +0,0 @@
|
||||||
|
|
||||||
class GLCanvas
|
|
||||||
{
|
|
||||||
constructor(width, height)
|
|
||||||
{
|
|
||||||
this.width = width;
|
|
||||||
this.height = height;
|
|
||||||
|
|
||||||
// Create a WebGL 2 canvas without premultiplied alpha
|
|
||||||
this.glCanvas = document.createElement("canvas");
|
|
||||||
this.glCanvas.width = width;
|
|
||||||
this.glCanvas.height = height;
|
|
||||||
this.gl = this.glCanvas.getContext("webgl2", { premultipliedAlpha: false, antialias: false });
|
|
||||||
|
|
||||||
// Overlay the canvas on top of everything and make sure mouse events click-through
|
|
||||||
this.glCanvas.style.position = "fixed";
|
|
||||||
this.glCanvas.style.pointerEvents = "none";
|
|
||||||
this.glCanvas.style.zIndex = 1000;
|
|
||||||
document.body.appendChild(this.glCanvas);
|
|
||||||
|
|
||||||
// Hook up resize event handler
|
|
||||||
DOM.Event.AddHandler(window, "resize", () => this.OnResizeWindow());
|
|
||||||
this.OnResizeWindow();
|
|
||||||
|
|
||||||
// Compile needed shaders
|
|
||||||
this.timelineProgram = glCreateProgramFromSource(this.gl, "TimelineVShader", TimelineVShader, "TimelineFShader", TimelineFShader);
|
|
||||||
this.timelineHighlightProgram = glCreateProgramFromSource(this.gl, "TimelineHighlightVShader", TimelineHighlightVShader, "TimelineHighlightFShader", TimelineHighlightFShader);
|
|
||||||
this.timelineGpuToCpuProgram = glCreateProgramFromSource(this.gl, "TimelineGpuToCpuVShader", TimelineGpuToCpuVShader, "TimelineGpuToCpuFShader", TimelineGpuToCpuFShader);
|
|
||||||
this.timelineBackgroundProgram = glCreateProgramFromSource(this.gl, "TimelineBackgroundVShader", TimelineBackgroundVShader, "TimelineBackgroundFShader", TimelineBackgroundFShader);
|
|
||||||
this.gridProgram = glCreateProgramFromSource(this.gl, "GridVShader", GridVShader, "GridFShader", GridFShader);
|
|
||||||
this.gridNumberProgram = glCreateProgramFromSource(this.gl, "GridNumberVShader", GridNumberVShader, "GridNumberFShader", GridNumberFShader);
|
|
||||||
this.windowProgram = glCreateProgramFromSource(this.gl, "WindowVShader", WindowVShader, "WindowFShader", WindowFShader);
|
|
||||||
|
|
||||||
// Create the shader font resources
|
|
||||||
this.font = new glFont(this.gl);
|
|
||||||
this.textBuffer = new glTextBuffer(this.gl, this.font);
|
|
||||||
this.nameMap = new NameMap(this.textBuffer);
|
|
||||||
|
|
||||||
// Kick off the rendering refresh
|
|
||||||
this.OnDrawHandler = null;
|
|
||||||
this.Draw(performance.now());
|
|
||||||
}
|
|
||||||
|
|
||||||
SetOnDraw(handler)
|
|
||||||
{
|
|
||||||
this.OnDrawHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClearTextResources()
|
|
||||||
{
|
|
||||||
this.nameMap = new NameMap(this.textBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
OnResizeWindow()
|
|
||||||
{
|
|
||||||
// Resize to match the window
|
|
||||||
this.width = window.innerWidth;
|
|
||||||
this.height = window.innerHeight;
|
|
||||||
this.glCanvas.width = window.innerWidth;
|
|
||||||
this.glCanvas.height = window.innerHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetFontUniforms(program)
|
|
||||||
{
|
|
||||||
// Font texture may not be loaded yet
|
|
||||||
if (this.font.atlasTexture != null)
|
|
||||||
{
|
|
||||||
const gl = this.gl;
|
|
||||||
glSetUniform(gl, program, "inFontAtlasTexture", this.font.atlasTexture, 0);
|
|
||||||
glSetUniform(gl, program, "inTextBufferDesc.fontWidth", this.font.fontWidth);
|
|
||||||
glSetUniform(gl, program, "inTextBufferDesc.fontHeight", this.font.fontHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SetTextUniforms(program)
|
|
||||||
{
|
|
||||||
const gl = this.gl;
|
|
||||||
this.SetFontUniforms(program);
|
|
||||||
this.textBuffer.SetAsUniform(gl, program, "inTextBuffer", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetContainerUniforms(program, container)
|
|
||||||
{
|
|
||||||
const gl = this.gl;
|
|
||||||
const container_rect = container.getBoundingClientRect();
|
|
||||||
glSetUniform(gl, program, "inContainer.x0", container_rect.left);
|
|
||||||
glSetUniform(gl, program, "inContainer.y0", container_rect.top);
|
|
||||||
glSetUniform(gl, program, "inContainer.x1", container_rect.left + container_rect.width);
|
|
||||||
glSetUniform(gl, program, "inContainer.y1", container_rect.top + container_rect.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
EnableBlendPremulAlpha()
|
|
||||||
{
|
|
||||||
const gl = this.gl;
|
|
||||||
gl.enable(gl.BLEND);
|
|
||||||
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
||||||
}
|
|
||||||
|
|
||||||
DisableBlend()
|
|
||||||
{
|
|
||||||
const gl = this.gl;
|
|
||||||
gl.disable(gl.BLEND);
|
|
||||||
}
|
|
||||||
|
|
||||||
Draw(timestamp)
|
|
||||||
{
|
|
||||||
// Setup the viewport and clear the screen
|
|
||||||
const gl = this.gl;
|
|
||||||
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
|
||||||
gl.clearColor(0, 0, 0, 0);
|
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
// Chain to the Draw handler
|
|
||||||
const seconds = timestamp / 1000.0;
|
|
||||||
if (this.OnDrawHandler != null)
|
|
||||||
{
|
|
||||||
this.OnDrawHandler(gl, seconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reschedule
|
|
||||||
window.requestAnimationFrame((timestamp) => this.Draw(timestamp));
|
|
||||||
}
|
|
||||||
};
|
|
291
profiler/vis/Code/GridWindow.js
vendored
291
profiler/vis/Code/GridWindow.js
vendored
|
@ -1,291 +0,0 @@
|
||||||
|
|
||||||
class GridConfigSamples
|
|
||||||
{
|
|
||||||
constructor()
|
|
||||||
{
|
|
||||||
this.nbFloatsPerSample = g_nbFloatsPerSample;
|
|
||||||
|
|
||||||
this.columns = [];
|
|
||||||
this.columns.push(new GridColumn("Sample Name", 196));
|
|
||||||
this.columns.push(new GridColumn("Time (ms)", 56, g_sampleOffsetFloats_Length, 4));
|
|
||||||
this.columns.push(new GridColumn("Self (ms)", 56, g_sampleOffsetFloats_Self, 4));
|
|
||||||
this.columns.push(new GridColumn("Calls", 34, g_sampleOffsetFloats_Calls, 0));
|
|
||||||
this.columns.push(new GridColumn("Depth", 34, g_sampleOffsetFloats_Recurse, 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GridConfigProperties
|
|
||||||
{
|
|
||||||
constructor()
|
|
||||||
{
|
|
||||||
this.nbFloatsPerSample = 10;
|
|
||||||
|
|
||||||
this.columns = [];
|
|
||||||
this.columns.push(new GridColumn("Property Name", 196));
|
|
||||||
this.columns.push(new GridColumn("Value", 90, 4, 4));
|
|
||||||
this.columns.push(new GridColumn("Prev Value", 90, 6, 4));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GridColumn
|
|
||||||
{
|
|
||||||
static ColumnTemplate = `<div class="GridNameHeader"></div>`;
|
|
||||||
|
|
||||||
constructor(name, width, number_offset, nb_float_chars)
|
|
||||||
{
|
|
||||||
// Description
|
|
||||||
this.name = name;
|
|
||||||
this.width = width;
|
|
||||||
this.numberOffset = number_offset;
|
|
||||||
this.nbFloatChars = nb_float_chars;
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
this.rowHeight = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
Attach(parent_node)
|
|
||||||
{
|
|
||||||
// Generate HTML for the header and parent it
|
|
||||||
const column = DOM.Node.CreateHTML(GridColumn.ColumnTemplate);
|
|
||||||
column.innerHTML = this.name;
|
|
||||||
column.style.width = (this.width - 4) + "px";
|
|
||||||
this.headerNode = parent_node.appendChild(column);
|
|
||||||
}
|
|
||||||
|
|
||||||
Draw(gl_canvas, x, buffer, scroll_pos, clip, nb_entries, nb_floats_per_sample)
|
|
||||||
{
|
|
||||||
// If a number offset in the data stream is provided, we're rendering numbers and not names
|
|
||||||
if (this.numberOffset !== undefined)
|
|
||||||
{
|
|
||||||
this._DrawNumbers(gl_canvas, x, buffer, scroll_pos, clip, nb_entries, nb_floats_per_sample);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this._DrawNames(gl_canvas, x, buffer, scroll_pos, clip, nb_entries, nb_floats_per_sample);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_DrawNames(gl_canvas, x, buffer, scroll_pos, clip, nb_entries, nb_floats_per_sample)
|
|
||||||
{
|
|
||||||
const gl = gl_canvas.gl;
|
|
||||||
const program = gl_canvas.gridProgram;
|
|
||||||
|
|
||||||
gl.useProgram(program);
|
|
||||||
gl_canvas.SetTextUniforms(program);
|
|
||||||
|
|
||||||
this._DrawAny(gl, program, x, buffer, scroll_pos, clip, nb_entries, nb_floats_per_sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
_DrawNumbers(gl_canvas, x, buffer, scroll_pos, clip, nb_entries, nb_floats_per_sample)
|
|
||||||
{
|
|
||||||
const gl = gl_canvas.gl;
|
|
||||||
const program = gl_canvas.gridNumberProgram;
|
|
||||||
|
|
||||||
gl.useProgram(program);
|
|
||||||
gl_canvas.SetFontUniforms(program);
|
|
||||||
glSetUniform(gl, program, "inNumberOffset", this.numberOffset);
|
|
||||||
glSetUniform(gl, program, "inNbFloatChars", this.nbFloatChars);
|
|
||||||
|
|
||||||
this._DrawAny(gl, program, x, buffer, scroll_pos, clip, nb_entries, nb_floats_per_sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
_DrawAny(gl, program, x, buffer, scroll_pos, clip, nb_entries, nb_floats_per_sample)
|
|
||||||
{
|
|
||||||
const clip_min_x = clip[0];
|
|
||||||
const clip_min_y = clip[1];
|
|
||||||
const clip_max_x = clip[2];
|
|
||||||
const clip_max_y = clip[3];
|
|
||||||
|
|
||||||
// Scrolled position of the grid
|
|
||||||
const pos_x = clip_min_x + scroll_pos[0] + x;
|
|
||||||
const pos_y = clip_min_y + scroll_pos[1];
|
|
||||||
|
|
||||||
// Clip column to the window
|
|
||||||
const min_x = Math.min(Math.max(clip_min_x, pos_x), clip_max_x);
|
|
||||||
const min_y = Math.min(Math.max(clip_min_y, pos_y), clip_max_y);
|
|
||||||
const max_x = Math.max(Math.min(clip_max_x, pos_x + this.width), clip_min_x);
|
|
||||||
const max_y = Math.max(Math.min(clip_max_y, pos_y + nb_entries * this.rowHeight), clip_min_y);
|
|
||||||
|
|
||||||
// Don't render if outside the bounds of the main window
|
|
||||||
if (min_x > gl.canvas.width || max_x < 0 || min_y > gl.canvas.height || max_y < 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pixel_offset_x = Math.max(min_x - pos_x, 0);
|
|
||||||
const pixel_offset_y = Math.max(min_y - pos_y, 0);
|
|
||||||
|
|
||||||
// Viewport constants
|
|
||||||
glSetUniform(gl, program, "inViewport.width", gl.canvas.width);
|
|
||||||
glSetUniform(gl, program, "inViewport.height", gl.canvas.height);
|
|
||||||
|
|
||||||
// Grid constants
|
|
||||||
glSetUniform(gl, program, "inGrid.minX", min_x);
|
|
||||||
glSetUniform(gl, program, "inGrid.minY", min_y);
|
|
||||||
glSetUniform(gl, program, "inGrid.maxX", max_x);
|
|
||||||
glSetUniform(gl, program, "inGrid.maxY", max_y);
|
|
||||||
glSetUniform(gl, program, "inGrid.pixelOffsetX", pixel_offset_x);
|
|
||||||
glSetUniform(gl, program, "inGrid.pixelOffsetY", pixel_offset_y);
|
|
||||||
|
|
||||||
// Source data set buffers
|
|
||||||
glSetUniform(gl, program, "inSamples", buffer.texture, 2);
|
|
||||||
glSetUniform(gl, program, "inSamplesLength", buffer.nbEntries);
|
|
||||||
glSetUniform(gl, program, "inFloatsPerSample", nb_floats_per_sample);
|
|
||||||
glSetUniform(gl, program, "inNbSamples", buffer.nbEntries / nb_floats_per_sample);
|
|
||||||
|
|
||||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GridWindow
|
|
||||||
{
|
|
||||||
static GridTemplate = `
|
|
||||||
<div style="height:100%; overflow: hidden; position: relative; display:flex; flex-flow: column;">
|
|
||||||
<div style="height: 16px; flex: 0 1 auto;"></div>
|
|
||||||
<div style="flex: 1 1 auto;"></div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
constructor(wm, name, offset, gl_canvas, config)
|
|
||||||
{
|
|
||||||
this.nbEntries = 0;
|
|
||||||
this.scrollPos = [ 0, 0 ];
|
|
||||||
|
|
||||||
// Window setup
|
|
||||||
this.xPos = 10 + offset * 410;
|
|
||||||
this.window = wm.AddWindow(name, 100, 100, 100, 100, null, this);
|
|
||||||
this.window.ShowNoAnim();
|
|
||||||
this.visible = true;
|
|
||||||
|
|
||||||
// Cache how much internal padding the window has, for clipping
|
|
||||||
const style = getComputedStyle(this.window.BodyNode);
|
|
||||||
this.bodyPadding = parseFloat(style.padding);
|
|
||||||
|
|
||||||
// Create the Grid host HTML
|
|
||||||
const grid_node = DOM.Node.CreateHTML(GridWindow.GridTemplate);
|
|
||||||
this.gridNode = this.window.BodyNode.appendChild(grid_node);
|
|
||||||
this.headerNode = this.gridNode.children[0];
|
|
||||||
this.contentNode = this.gridNode.children[1];
|
|
||||||
|
|
||||||
// Build column data
|
|
||||||
this.nbFloatsPerSample = config.nbFloatsPerSample;
|
|
||||||
this.columns = config.columns;
|
|
||||||
for (let column of this.columns)
|
|
||||||
{
|
|
||||||
column.Attach(this.headerNode);
|
|
||||||
}
|
|
||||||
this._PositionHeaders();
|
|
||||||
|
|
||||||
// Header nodes have 1 pixel borders so the first column is required to have a width 1 less than everything else
|
|
||||||
// To counter that, shift the first header node one to the left (it will clip) so that it can have its full width
|
|
||||||
this.columns[0].headerNode.style.marginLeft = "-1px";
|
|
||||||
|
|
||||||
// Setup for pan/wheel scrolling
|
|
||||||
this.mouseInteraction = new MouseInteraction(this.window.BodyNode);
|
|
||||||
this.mouseInteraction.onMoveHandler = (mouse_state, mx, my) => this._OnMouseMove(mouse_state, mx, my);
|
|
||||||
this.mouseInteraction.onScrollHandler = (mouse_state) => this._OnMouseScroll(mouse_state);
|
|
||||||
|
|
||||||
const gl = gl_canvas.gl;
|
|
||||||
this.glCanvas = gl_canvas;
|
|
||||||
this.sampleBuffer = new glDynamicBuffer(gl, glDynamicBufferType.Texture, gl.FLOAT, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Close()
|
|
||||||
{
|
|
||||||
this.window.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
static AnimatedMove(self, top_window, bottom_window, val)
|
|
||||||
{
|
|
||||||
self.xPos = val;
|
|
||||||
self.WindowResized(top_window, bottom_window);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetXPos(xpos, top_window, bottom_window)
|
|
||||||
{
|
|
||||||
Anim.Animate(
|
|
||||||
Bind(GridWindow.AnimatedMove, this, top_window, bottom_window),
|
|
||||||
this.xPos, 10 + xpos * 410, 0.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetVisible(visible)
|
|
||||||
{
|
|
||||||
if (visible != this.visible)
|
|
||||||
{
|
|
||||||
if (visible == true)
|
|
||||||
this.window.ShowNoAnim();
|
|
||||||
else
|
|
||||||
this.window.HideNoAnim();
|
|
||||||
|
|
||||||
this.visible = visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WindowResized(top_window, bottom_window)
|
|
||||||
{
|
|
||||||
const top = top_window.Position[1] + top_window.Size[1] + 10;
|
|
||||||
this.window.SetPosition(this.xPos, top_window.Position[1] + top_window.Size[1] + 10);
|
|
||||||
this.window.SetSize(400, bottom_window.Position[1] - 10 - top);
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateEntries(nb_entries, samples)
|
|
||||||
{
|
|
||||||
// This tracks the latest actual entry count
|
|
||||||
this.nbEntries = nb_entries;
|
|
||||||
|
|
||||||
// Resize buffers to match any new entry count
|
|
||||||
if (nb_entries * this.nbFloatsPerSample > this.sampleBuffer.nbEntries)
|
|
||||||
{
|
|
||||||
this.sampleBuffer.ResizeToFitNextPow2(nb_entries * this.nbFloatsPerSample);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy and upload the entry data
|
|
||||||
this.sampleBuffer.cpuArray.set(samples);
|
|
||||||
this.sampleBuffer.UploadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
Draw()
|
|
||||||
{
|
|
||||||
// Establish content node clipping rectangle
|
|
||||||
const rect = this.contentNode.getBoundingClientRect();
|
|
||||||
const clip = [
|
|
||||||
rect.left,
|
|
||||||
rect.top,
|
|
||||||
rect.left + rect.width,
|
|
||||||
rect.top + rect.height,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Draw columns, left-to-right
|
|
||||||
let x = 0;
|
|
||||||
for (let column of this.columns)
|
|
||||||
{
|
|
||||||
column.Draw(this.glCanvas, x, this.sampleBuffer, this.scrollPos, clip, this.nbEntries, this.nbFloatsPerSample);
|
|
||||||
x += column.width + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_PositionHeaders()
|
|
||||||
{
|
|
||||||
let x = this.scrollPos[0];
|
|
||||||
for (let i in this.columns)
|
|
||||||
{
|
|
||||||
const column = this.columns[i];
|
|
||||||
column.headerNode.style.left = x + "px";
|
|
||||||
x += column.width;
|
|
||||||
x += (i >= 1) ? 1 : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_OnMouseMove(mouse_state, mouse_offset_x, mouse_offset_y)
|
|
||||||
{
|
|
||||||
this.scrollPos[0] = Math.min(0, this.scrollPos[0] + mouse_offset_x);
|
|
||||||
this.scrollPos[1] = Math.min(0, this.scrollPos[1] + mouse_offset_y);
|
|
||||||
|
|
||||||
this._PositionHeaders();
|
|
||||||
}
|
|
||||||
|
|
||||||
_OnMouseScroll(mouse_state)
|
|
||||||
{
|
|
||||||
this.scrollPos[1] = Math.min(0, this.scrollPos[1] + mouse_state.WheelDelta * 15);
|
|
||||||
}
|
|
||||||
}
|
|
106
profiler/vis/Code/MouseInteraction.js
vendored
106
profiler/vis/Code/MouseInteraction.js
vendored
|
@ -1,106 +0,0 @@
|
||||||
class MouseInteraction
|
|
||||||
{
|
|
||||||
constructor(node)
|
|
||||||
{
|
|
||||||
this.node = node;
|
|
||||||
|
|
||||||
// Current interaction state
|
|
||||||
this.mouseDown = false;
|
|
||||||
this.lastMouseState = null;
|
|
||||||
this.mouseMoved = false;
|
|
||||||
|
|
||||||
// Empty user handlers
|
|
||||||
this.onClickHandler = null;
|
|
||||||
this.onMoveHandler = null;
|
|
||||||
this.onHoverHandler = null;
|
|
||||||
this.onScrollHandler = null;
|
|
||||||
|
|
||||||
// Setup DOM handlers
|
|
||||||
DOM.Event.AddHandler(this.node, "mousedown", (evt) => this._OnMouseDown(evt));
|
|
||||||
DOM.Event.AddHandler(this.node, "mouseup", (evt) => this._OnMouseUp(evt));
|
|
||||||
DOM.Event.AddHandler(this.node, "mousemove", (evt) => this._OnMouseMove(evt));
|
|
||||||
DOM.Event.AddHandler(this.node, "mouseleave", (evt) => this._OnMouseLeave(evt));
|
|
||||||
|
|
||||||
// Mouse wheel is a little trickier
|
|
||||||
const mouse_wheel_event = (/Firefox/i.test(navigator.userAgent)) ? "DOMMouseScroll" : "mousewheel";
|
|
||||||
DOM.Event.AddHandler(this.node, mouse_wheel_event, (evt) => this._OnMouseScroll(evt));
|
|
||||||
}
|
|
||||||
|
|
||||||
_OnMouseDown(evt)
|
|
||||||
{
|
|
||||||
this.mouseDown = true;
|
|
||||||
this.lastMouseState = new Mouse.State(evt);
|
|
||||||
this.mouseMoved = false;
|
|
||||||
DOM.Event.StopDefaultAction(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
_OnMouseUp(evt)
|
|
||||||
{
|
|
||||||
const mouse_state = new Mouse.State(evt);
|
|
||||||
|
|
||||||
this.mouseDown = false;
|
|
||||||
|
|
||||||
// Chain to on click event when released without movement
|
|
||||||
if (!this.mouseMoved)
|
|
||||||
{
|
|
||||||
if (this.onClickHandler)
|
|
||||||
{
|
|
||||||
this.onClickHandler(mouse_state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_OnMouseMove(evt)
|
|
||||||
{
|
|
||||||
const mouse_state = new Mouse.State(evt);
|
|
||||||
|
|
||||||
if (this.mouseDown)
|
|
||||||
{
|
|
||||||
// Has the mouse moved while being held down?
|
|
||||||
const move_offset_x = mouse_state.Position[0] - this.lastMouseState.Position[0];
|
|
||||||
const move_offset_y = mouse_state.Position[1] - this.lastMouseState.Position[1];
|
|
||||||
if (move_offset_x != 0 || move_offset_y != 0)
|
|
||||||
{
|
|
||||||
this.mouseMoved = true;
|
|
||||||
|
|
||||||
// Chain to move handler
|
|
||||||
if (this.onMoveHandler)
|
|
||||||
{
|
|
||||||
this.onMoveHandler(mouse_state, move_offset_x, move_offset_y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chain to hover handler
|
|
||||||
else if (this.onHoverHandler)
|
|
||||||
{
|
|
||||||
this.onHoverHandler(mouse_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lastMouseState = mouse_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
_OnMouseLeave(evt)
|
|
||||||
{
|
|
||||||
// Cancel panning
|
|
||||||
this.mouseDown = false;
|
|
||||||
|
|
||||||
// Cancel hovering
|
|
||||||
if (this.onHoverHandler)
|
|
||||||
{
|
|
||||||
this.onHoverHandler(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_OnMouseScroll(evt)
|
|
||||||
{
|
|
||||||
const mouse_state = new Mouse.State(evt);
|
|
||||||
if (this.onScrollHandler)
|
|
||||||
{
|
|
||||||
this.onScrollHandler(mouse_state);
|
|
||||||
|
|
||||||
// Prevent vertical scrolling on mouse-wheel
|
|
||||||
DOM.Event.StopDefaultAction(evt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
53
profiler/vis/Code/NameMap.js
vendored
53
profiler/vis/Code/NameMap.js
vendored
|
@ -1,53 +0,0 @@
|
||||||
class NameMap
|
|
||||||
{
|
|
||||||
constructor(text_buffer)
|
|
||||||
{
|
|
||||||
this.names = { };
|
|
||||||
this.textBuffer = text_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
Get(name_hash)
|
|
||||||
{
|
|
||||||
// Return immediately if it's in the hash
|
|
||||||
let name = this.names[name_hash];
|
|
||||||
if (name != undefined)
|
|
||||||
{
|
|
||||||
return [ true, name ];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a temporary name that uses the hash
|
|
||||||
name = {
|
|
||||||
string: name_hash.toString(),
|
|
||||||
hash: name_hash
|
|
||||||
};
|
|
||||||
this.names[name_hash] = name;
|
|
||||||
|
|
||||||
// Add to the text buffer the first time this name is encountered
|
|
||||||
name.textEntry = this.textBuffer.AddText(name.string);
|
|
||||||
|
|
||||||
return [ false, name ];
|
|
||||||
}
|
|
||||||
|
|
||||||
Set(name_hash, name_string)
|
|
||||||
{
|
|
||||||
// Create the name on-demand if its hash doesn't exist
|
|
||||||
let name = this.names[name_hash];
|
|
||||||
if (name == undefined)
|
|
||||||
{
|
|
||||||
name = {
|
|
||||||
string: name_string,
|
|
||||||
hash: name_hash
|
|
||||||
};
|
|
||||||
this.names[name_hash] = name;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
name.string = name_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply the updated text to the buffer
|
|
||||||
name.textEntry = this.textBuffer.AddText(name_string);
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
61
profiler/vis/Code/PixelTimeRange.js
vendored
61
profiler/vis/Code/PixelTimeRange.js
vendored
|
@ -1,61 +0,0 @@
|
||||||
|
|
||||||
class PixelTimeRange
|
|
||||||
{
|
|
||||||
constructor(start_us, span_us, span_px)
|
|
||||||
{
|
|
||||||
this.Span_px = span_px;
|
|
||||||
this.Set(start_us, span_us);
|
|
||||||
}
|
|
||||||
|
|
||||||
Set(start_us, span_us)
|
|
||||||
{
|
|
||||||
this.Start_us = start_us;
|
|
||||||
this.Span_us = span_us;
|
|
||||||
this.End_us = this.Start_us + span_us;
|
|
||||||
this.usPerPixel = this.Span_px / this.Span_us;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetStart(start_us)
|
|
||||||
{
|
|
||||||
this.Start_us = start_us;
|
|
||||||
this.End_us = start_us + this.Span_us;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetEnd(end_us)
|
|
||||||
{
|
|
||||||
this.End_us = end_us;
|
|
||||||
this.Start_us = end_us - this.Span_us;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetPixelSpan(span_px)
|
|
||||||
{
|
|
||||||
this.Span_px = span_px;
|
|
||||||
this.usPerPixel = this.Span_px / this.Span_us;
|
|
||||||
}
|
|
||||||
|
|
||||||
PixelOffset(time_us)
|
|
||||||
{
|
|
||||||
return Math.floor((time_us - this.Start_us) * this.usPerPixel);
|
|
||||||
}
|
|
||||||
|
|
||||||
PixelSize(time_us)
|
|
||||||
{
|
|
||||||
return Math.floor(time_us * this.usPerPixel);
|
|
||||||
}
|
|
||||||
|
|
||||||
TimeAtPosition(position)
|
|
||||||
{
|
|
||||||
return this.Start_us + position / this.usPerPixel;
|
|
||||||
}
|
|
||||||
|
|
||||||
Clone()
|
|
||||||
{
|
|
||||||
return new PixelTimeRange(this.Start_us, this.Span_us, this.Span_px);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetAsUniform(gl, program)
|
|
||||||
{
|
|
||||||
glSetUniform(gl, program, "inTimeRange.msStart", this.Start_us / 1000.0);
|
|
||||||
glSetUniform(gl, program, "inTimeRange.msPerPixel", this.usPerPixel * 1000.0);
|
|
||||||
}
|
|
||||||
}
|
|
756
profiler/vis/Code/Remotery.js
vendored
756
profiler/vis/Code/Remotery.js
vendored
|
@ -1,756 +0,0 @@
|
||||||
|
|
||||||
//
|
|
||||||
// TODO: Window resizing needs finer-grain control
|
|
||||||
// TODO: Take into account where user has moved the windows
|
|
||||||
// TODO: Controls need automatic resizing within their parent windows
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
Settings = (function()
|
|
||||||
{
|
|
||||||
function Settings()
|
|
||||||
{
|
|
||||||
this.IsPaused = false;
|
|
||||||
this.SyncTimelines = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Settings;
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
Remotery = (function()
|
|
||||||
{
|
|
||||||
// crack the url and get the parameter we want
|
|
||||||
var getUrlParameter = function getUrlParameter( search_param)
|
|
||||||
{
|
|
||||||
var page_url = decodeURIComponent( window.location.search.substring(1) ),
|
|
||||||
url_vars = page_url.split('&'),
|
|
||||||
param_name,
|
|
||||||
i;
|
|
||||||
|
|
||||||
for (i = 0; i < url_vars.length; i++)
|
|
||||||
{
|
|
||||||
param_name = url_vars[i].split('=');
|
|
||||||
|
|
||||||
if (param_name[0] === search_param)
|
|
||||||
{
|
|
||||||
return param_name[1] === undefined ? true : param_name[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function Remotery()
|
|
||||||
{
|
|
||||||
this.WindowManager = new WM.WindowManager();
|
|
||||||
this.Settings = new Settings();
|
|
||||||
|
|
||||||
// "addr" param is ip:port and will override the local store version if passed in the URL
|
|
||||||
var addr = getUrlParameter( "addr" );
|
|
||||||
if ( addr != null )
|
|
||||||
this.ConnectionAddress = "ws://" + addr + "/rmt";
|
|
||||||
else
|
|
||||||
this.ConnectionAddress = LocalStore.Get("App", "Global", "ConnectionAddress", "ws://127.0.0.1:17815/rmt");
|
|
||||||
|
|
||||||
this.Server = new WebSocketConnection();
|
|
||||||
this.Server.AddConnectHandler(Bind(OnConnect, this));
|
|
||||||
this.Server.AddDisconnectHandler(Bind(OnDisconnect, this));
|
|
||||||
|
|
||||||
this.glCanvas = new GLCanvas(100, 100);
|
|
||||||
this.glCanvas.SetOnDraw((gl, seconds) => this.OnGLCanvasDraw(gl, seconds));
|
|
||||||
|
|
||||||
// Create the console up front as everything reports to it
|
|
||||||
this.Console = new Console(this.WindowManager, this.Server);
|
|
||||||
|
|
||||||
// Create required windows
|
|
||||||
this.TitleWindow = new TitleWindow(this.WindowManager, this.Settings, this.Server, this.ConnectionAddress);
|
|
||||||
this.TitleWindow.SetConnectionAddressChanged(Bind(OnAddressChanged, this));
|
|
||||||
this.SampleTimelineWindow = new TimelineWindow(this.WindowManager, "Sample Timeline", this.Settings, Bind(OnTimelineCheck, this), this.glCanvas);
|
|
||||||
this.SampleTimelineWindow.SetOnHover(Bind(OnSampleHover, this));
|
|
||||||
this.SampleTimelineWindow.SetOnSelected(Bind(OnSampleSelected, this));
|
|
||||||
this.ProcessorTimelineWindow = new TimelineWindow(this.WindowManager, "Processor Timeline", this.Settings, null, this.glCanvas);
|
|
||||||
|
|
||||||
this.SampleTimelineWindow.SetOnMoved(Bind(OnTimelineMoved, this));
|
|
||||||
this.ProcessorTimelineWindow.SetOnMoved(Bind(OnTimelineMoved, this));
|
|
||||||
|
|
||||||
this.TraceDrop = new TraceDrop(this);
|
|
||||||
|
|
||||||
this.nbGridWindows = 0;
|
|
||||||
this.gridWindows = { };
|
|
||||||
this.FrameHistory = { };
|
|
||||||
this.ProcessorFrameHistory = { };
|
|
||||||
this.PropertyFrameHistory = [ ];
|
|
||||||
this.SelectedFrames = { };
|
|
||||||
|
|
||||||
this.Server.AddMessageHandler("SMPL", Bind(OnSamples, this));
|
|
||||||
this.Server.AddMessageHandler("SSMP", Bind(OnSampleName, this));
|
|
||||||
this.Server.AddMessageHandler("PRTH", Bind(OnProcessorThreads, this));
|
|
||||||
this.Server.AddMessageHandler("PSNP", Bind(OnPropertySnapshots, this));
|
|
||||||
|
|
||||||
// Kick-off the auto-connect loop
|
|
||||||
AutoConnect(this);
|
|
||||||
|
|
||||||
// Hook up resize event handler
|
|
||||||
DOM.Event.AddHandler(window, "resize", Bind(OnResizeWindow, this));
|
|
||||||
OnResizeWindow(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Remotery.prototype.Clear = function()
|
|
||||||
{
|
|
||||||
// Clear timelines
|
|
||||||
this.SampleTimelineWindow.Clear();
|
|
||||||
this.ProcessorTimelineWindow.Clear();
|
|
||||||
|
|
||||||
// Close and clear all sample windows
|
|
||||||
for (var i in this.gridWindows)
|
|
||||||
{
|
|
||||||
const grid_window = this.gridWindows[i];
|
|
||||||
grid_window.Close();
|
|
||||||
}
|
|
||||||
this.nbGridWindows = 0;
|
|
||||||
this.gridWindows = { };
|
|
||||||
|
|
||||||
this.propertyGridWindow = this.AddGridWindow("__rmt__global__properties__", "Global Properties", new GridConfigProperties());
|
|
||||||
|
|
||||||
// Clear runtime data
|
|
||||||
this.FrameHistory = { };
|
|
||||||
this.ProcessorFrameHistory = { };
|
|
||||||
this.PropertyFrameHistory = [ ];
|
|
||||||
this.SelectedFrames = { };
|
|
||||||
this.glCanvas.ClearTextResources();
|
|
||||||
|
|
||||||
// Resize everything to fit new layout
|
|
||||||
OnResizeWindow(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
function DrawWindowMask(gl, program, window_node)
|
|
||||||
{
|
|
||||||
gl.useProgram(program);
|
|
||||||
|
|
||||||
// Using depth as a mask
|
|
||||||
gl.enable(gl.DEPTH_TEST);
|
|
||||||
gl.depthFunc(gl.LEQUAL);
|
|
||||||
|
|
||||||
// Viewport constants
|
|
||||||
glSetUniform(gl, program, "inViewport.width", gl.canvas.width);
|
|
||||||
glSetUniform(gl, program, "inViewport.height", gl.canvas.height);
|
|
||||||
|
|
||||||
// Window dimensions
|
|
||||||
const rect = window_node.getBoundingClientRect();
|
|
||||||
glSetUniform(gl, program, "minX", rect.left);
|
|
||||||
glSetUniform(gl, program, "minY", rect.top);
|
|
||||||
glSetUniform(gl, program, "maxX", rect.left + rect.width);
|
|
||||||
glSetUniform(gl, program, "maxY", rect.top + rect.height);
|
|
||||||
|
|
||||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
Remotery.prototype.OnGLCanvasDraw = function(gl, seconds)
|
|
||||||
{
|
|
||||||
this.glCanvas.textBuffer.UploadData();
|
|
||||||
|
|
||||||
// Draw windows in their z-order, front-to-back
|
|
||||||
// Depth test is enabled, rejecting equal z, and windows render transparent
|
|
||||||
// Draw window content first, then draw the invisible window mask
|
|
||||||
// Any windows that come after another window will not draw where the previous window already masked out depth
|
|
||||||
for (let window of this.WindowManager.Windows)
|
|
||||||
{
|
|
||||||
// Some windows might not have WebGL drawing on them; they need to occlude as well
|
|
||||||
DrawWindowMask(gl, this.glCanvas.windowProgram, window.Node);
|
|
||||||
|
|
||||||
if (window.userData != null)
|
|
||||||
{
|
|
||||||
window.userData.Draw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function AutoConnect(self)
|
|
||||||
{
|
|
||||||
// Only attempt to connect if there isn't already a connection or an attempt to connect
|
|
||||||
if (!self.Server.Connected() && !self.Server.Connecting())
|
|
||||||
{
|
|
||||||
self.Server.Connect(self.ConnectionAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always schedule another check
|
|
||||||
window.setTimeout(Bind(AutoConnect, self), 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnConnect(self)
|
|
||||||
{
|
|
||||||
// Connection address has been validated
|
|
||||||
LocalStore.Set("App", "Global", "ConnectionAddress", self.ConnectionAddress);
|
|
||||||
|
|
||||||
self.Clear();
|
|
||||||
|
|
||||||
// Ensure the viewer is ready for realtime updates
|
|
||||||
self.TitleWindow.Unpause();
|
|
||||||
}
|
|
||||||
|
|
||||||
function OnDisconnect(self)
|
|
||||||
{
|
|
||||||
// Pause so the user can inspect the trace
|
|
||||||
self.TitleWindow.Pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnAddressChanged(self, node)
|
|
||||||
{
|
|
||||||
// Update and disconnect, relying on auto-connect to reconnect
|
|
||||||
self.ConnectionAddress = node.value;
|
|
||||||
self.Server.Disconnect();
|
|
||||||
|
|
||||||
// Give input focus away
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Remotery.prototype.AddGridWindow = function(name, display_name, config)
|
|
||||||
{
|
|
||||||
const grid_window = new GridWindow(this.WindowManager, display_name, this.nbGridWindows, this.glCanvas, config);
|
|
||||||
this.gridWindows[name] = grid_window;
|
|
||||||
this.gridWindows[name].WindowResized(this.SampleTimelineWindow.Window, this.Console.Window);
|
|
||||||
this.nbGridWindows++;
|
|
||||||
MoveGridWindows(this);
|
|
||||||
return grid_window;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function DecodeSampleHeader(self, data_view_reader, length)
|
|
||||||
{
|
|
||||||
// Message-specific header
|
|
||||||
let message = { };
|
|
||||||
message.messageStart = data_view_reader.Offset;
|
|
||||||
message.thread_name = data_view_reader.GetString();
|
|
||||||
message.nb_samples = data_view_reader.GetUInt32();
|
|
||||||
message.partial_tree = data_view_reader.GetUInt32();
|
|
||||||
|
|
||||||
// Align sample reading to 32-bit boundary
|
|
||||||
const align = ((4 - (data_view_reader.Offset & 3)) & 3);
|
|
||||||
data_view_reader.Offset += align;
|
|
||||||
message.samplesStart = data_view_reader.Offset;
|
|
||||||
message.samplesLength = length - (message.samplesStart - message.messageStart);
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function SetNanosecondsAsMilliseconds(samples_view, offset)
|
|
||||||
{
|
|
||||||
samples_view.setFloat32(offset, samples_view.getFloat64(offset, true) / 1000.0, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function SetUint32AsFloat32(samples_view, offset)
|
|
||||||
{
|
|
||||||
samples_view.setFloat32(offset, samples_view.getUint32(offset, true), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function ProcessSampleTree(self, sample_data, message)
|
|
||||||
{
|
|
||||||
const empty_text_entry = {
|
|
||||||
offset: 0,
|
|
||||||
length: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
const samples_length = message.nb_samples * g_nbBytesPerSample;
|
|
||||||
const samples_view = new DataView(sample_data, message.samplesStart, samples_length);
|
|
||||||
message.sampleDataView = samples_view;
|
|
||||||
|
|
||||||
for (let offset = 0; offset < samples_length; offset += g_nbBytesPerSample)
|
|
||||||
{
|
|
||||||
// Get name hash and lookup in name map
|
|
||||||
const name_hash = samples_view.getUint32(offset, true);
|
|
||||||
const [ name_exists, name ] = self.glCanvas.nameMap.Get(name_hash);
|
|
||||||
|
|
||||||
// If the name doesn't exist in the map yet, request it from the server
|
|
||||||
if (!name_exists)
|
|
||||||
{
|
|
||||||
if (self.Server.Connected())
|
|
||||||
{
|
|
||||||
self.Server.Send("GSMP" + name_hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add sample name text buffer location
|
|
||||||
const text_entry = name.textEntry != null ? name.textEntry : empty_text_entry;
|
|
||||||
samples_view.setFloat32(offset + g_sampleOffsetBytes_NameOffset, text_entry.offset, true);
|
|
||||||
samples_view.setFloat32(offset + g_sampleOffsetBytes_NameLength, text_entry.length, true);
|
|
||||||
|
|
||||||
// Time in milliseconds
|
|
||||||
SetNanosecondsAsMilliseconds(samples_view, offset + g_sampleOffsetBytes_Start);
|
|
||||||
SetNanosecondsAsMilliseconds(samples_view, offset + g_sampleOffsetBytes_Length);
|
|
||||||
SetNanosecondsAsMilliseconds(samples_view, offset + g_sampleOffsetBytes_Self);
|
|
||||||
SetNanosecondsAsMilliseconds(samples_view, offset + g_sampleOffsetBytes_GpuToCpu);
|
|
||||||
|
|
||||||
// Convert call count/recursion integers to float
|
|
||||||
SetUint32AsFloat32(samples_view, offset + g_sampleOffsetBytes_Calls);
|
|
||||||
SetUint32AsFloat32(samples_view, offset + g_sampleOffsetBytes_Recurse);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to floats for GPU
|
|
||||||
message.sampleFloats = new Float32Array(sample_data, message.samplesStart, message.nb_samples * g_nbFloatsPerSample);
|
|
||||||
}
|
|
||||||
|
|
||||||
function OnSamples(self, socket, data_view_reader, length)
|
|
||||||
{
|
|
||||||
// Discard any new samples while paused and connected
|
|
||||||
// Otherwise this stops a paused Remotery from loading new samples from disk
|
|
||||||
if (self.Settings.IsPaused && self.Server.Connected())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Binary decode incoming sample data
|
|
||||||
var message = DecodeSampleHeader(self, data_view_reader, length);
|
|
||||||
if (message.nb_samples == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var name = message.thread_name;
|
|
||||||
ProcessSampleTree(self, data_view_reader.DataView.buffer, message);
|
|
||||||
|
|
||||||
// Add to frame history for this thread
|
|
||||||
var thread_frame = new ThreadFrame(message);
|
|
||||||
if (!(name in self.FrameHistory))
|
|
||||||
{
|
|
||||||
self.FrameHistory[name] = [ ];
|
|
||||||
}
|
|
||||||
var frame_history = self.FrameHistory[name];
|
|
||||||
if (frame_history.length > 0 && frame_history[frame_history.length - 1].PartialTree)
|
|
||||||
{
|
|
||||||
// Always overwrite partial trees with new information
|
|
||||||
frame_history[frame_history.length - 1] = thread_frame;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
frame_history.push(thread_frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Discard old frames to keep memory-use constant
|
|
||||||
var max_nb_frames = 10000;
|
|
||||||
var extra_frames = frame_history.length - max_nb_frames;
|
|
||||||
if (extra_frames > 0)
|
|
||||||
frame_history.splice(0, extra_frames);
|
|
||||||
|
|
||||||
// Create sample windows on-demand
|
|
||||||
if (!(name in self.gridWindows))
|
|
||||||
{
|
|
||||||
self.AddGridWindow(name, name, new GridConfigSamples());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set on the window and timeline if connected as this implies a trace is being loaded, which we want to speed up
|
|
||||||
if (self.Server.Connected())
|
|
||||||
{
|
|
||||||
self.gridWindows[name].UpdateEntries(message.nb_samples, message.sampleFloats);
|
|
||||||
|
|
||||||
self.SampleTimelineWindow.OnSamples(name, frame_history);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnSampleName(self, socket, data_view_reader)
|
|
||||||
{
|
|
||||||
// Add any names sent by the server to the local map
|
|
||||||
let name_hash = data_view_reader.GetUInt32();
|
|
||||||
let name_string = data_view_reader.GetString();
|
|
||||||
self.glCanvas.nameMap.Set(name_hash, name_string);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnProcessorThreads(self, socket, data_view_reader)
|
|
||||||
{
|
|
||||||
// Discard any new samples while paused and connected
|
|
||||||
// Otherwise this stops a paused Remotery from loading new samples from disk
|
|
||||||
if (self.Settings.IsPaused && self.Server.Connected())
|
|
||||||
return;
|
|
||||||
|
|
||||||
let nb_processors = data_view_reader.GetUInt32();
|
|
||||||
let message_index = data_view_reader.GetUInt64();
|
|
||||||
|
|
||||||
const empty_text_entry = {
|
|
||||||
offset: 0,
|
|
||||||
length: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Decode each processor
|
|
||||||
for (let i = 0; i < nb_processors; i++)
|
|
||||||
{
|
|
||||||
let thread_id = data_view_reader.GetUInt32();
|
|
||||||
let thread_name_hash = data_view_reader.GetUInt32();
|
|
||||||
let sample_time = data_view_reader.GetUInt64();
|
|
||||||
|
|
||||||
// Add frame history for this processor
|
|
||||||
let processor_name = "Processor " + i.toString();
|
|
||||||
if (!(processor_name in self.ProcessorFrameHistory))
|
|
||||||
{
|
|
||||||
self.ProcessorFrameHistory[processor_name] = [ ];
|
|
||||||
}
|
|
||||||
let frame_history = self.ProcessorFrameHistory[processor_name];
|
|
||||||
|
|
||||||
if (thread_id == 0xFFFFFFFF)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to merge this frame's samples with the previous frame if the are the same thread
|
|
||||||
if (frame_history.length > 0)
|
|
||||||
{
|
|
||||||
let last_thread_frame = frame_history[frame_history.length - 1];
|
|
||||||
if (last_thread_frame.threadId == thread_id && last_thread_frame.messageIndex == message_index - 1)
|
|
||||||
{
|
|
||||||
// Update last frame message index so that the next frame can check for continuity
|
|
||||||
last_thread_frame.messageIndex = message_index;
|
|
||||||
|
|
||||||
// Sum time elapsed on the previous frame
|
|
||||||
const us_length = sample_time - last_thread_frame.usLastStart;
|
|
||||||
last_thread_frame.usLastStart = sample_time;
|
|
||||||
last_thread_frame.EndTime_us += us_length;
|
|
||||||
const last_length = last_thread_frame.sampleDataView.getFloat32(g_sampleOffsetBytes_Length, true);
|
|
||||||
last_thread_frame.sampleDataView.setFloat32(g_sampleOffsetBytes_Length, last_length + us_length / 1000.0, true);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Discard old frames to keep memory-use constant
|
|
||||||
var max_nb_frames = 10000;
|
|
||||||
var extra_frames = frame_history.length - max_nb_frames;
|
|
||||||
if (extra_frames > 0)
|
|
||||||
{
|
|
||||||
frame_history.splice(0, extra_frames);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup the thread name
|
|
||||||
let [ name_exists, thread_name ] = self.glCanvas.nameMap.Get(thread_name_hash);
|
|
||||||
|
|
||||||
// If the name doesn't exist in the map yet, request it from the server
|
|
||||||
if (!name_exists)
|
|
||||||
{
|
|
||||||
if (self.Server.Connected())
|
|
||||||
{
|
|
||||||
self.Server.Send("GSMP" + thread_name_hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We are co-opting the sample rendering functionality of the timeline window to display processor threads as
|
|
||||||
// thread samples. Fabricate a thread frame message, packing the processor info into one root sample.
|
|
||||||
// TODO(don): Abstract the timeline window for pure range display as this is quite inefficient.
|
|
||||||
let thread_message = { };
|
|
||||||
thread_message.nb_samples = 1;
|
|
||||||
thread_message.sampleData = new ArrayBuffer(g_nbBytesPerSample);
|
|
||||||
thread_message.sampleDataView = new DataView(thread_message.sampleData);
|
|
||||||
const sample_data_view = thread_message.sampleDataView;
|
|
||||||
|
|
||||||
// Set the name
|
|
||||||
const text_entry = thread_name.textEntry != null ? thread_name.textEntry : empty_text_entry;
|
|
||||||
sample_data_view.setFloat32(g_sampleOffsetBytes_NameOffset, text_entry.offset, true);
|
|
||||||
sample_data_view.setFloat32(g_sampleOffsetBytes_NameLength, text_entry.length, true);
|
|
||||||
|
|
||||||
// Make a pastel-y colour from the thread name hash
|
|
||||||
const hash = thread_name.hash;
|
|
||||||
sample_data_view.setUint8(g_sampleOffsetBytes_Colour + 0, 127 + (hash & 255) / 2);
|
|
||||||
sample_data_view.setUint8(g_sampleOffsetBytes_Colour + 1, 127 + ((hash >> 4) & 255) / 2);
|
|
||||||
sample_data_view.setUint8(g_sampleOffsetBytes_Colour + 2, 127 + ((hash >> 8) & 255) / 2);
|
|
||||||
|
|
||||||
// Set the time
|
|
||||||
sample_data_view.setFloat32(g_sampleOffsetBytes_Start, sample_time / 1000.0, true);
|
|
||||||
sample_data_view.setFloat32(g_sampleOffsetBytes_Length, 0.25, true);
|
|
||||||
|
|
||||||
thread_message.sampleFloats = new Float32Array(thread_message.sampleData, 0, thread_message.nb_samples * g_nbFloatsPerSample);
|
|
||||||
|
|
||||||
// Create a thread frame and annotate with data required to merge processor samples
|
|
||||||
let thread_frame = new ThreadFrame(thread_message);
|
|
||||||
thread_frame.threadId = thread_id;
|
|
||||||
thread_frame.messageIndex = message_index;
|
|
||||||
thread_frame.usLastStart = sample_time;
|
|
||||||
frame_history.push(thread_frame);
|
|
||||||
|
|
||||||
if (self.Server.Connected())
|
|
||||||
{
|
|
||||||
self.ProcessorTimelineWindow.OnSamples(processor_name, frame_history);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function UInt64ToFloat32(view, offset)
|
|
||||||
{
|
|
||||||
// Unpack as two 32-bit integers so we have a vague attempt at reconstructing the value
|
|
||||||
const a = view.getUint32(offset + 0, true);
|
|
||||||
const b = view.getUint32(offset + 4, true);
|
|
||||||
|
|
||||||
// Can't do bit arithmetic above 32-bits in JS so combine using power-of-two math
|
|
||||||
const v = a + (b * Math.pow(2, 32));
|
|
||||||
|
|
||||||
// TODO(don): Potentially massive data loss!
|
|
||||||
snapshots_view.setFloat32(offset, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function SInt64ToFloat32(view, offset)
|
|
||||||
{
|
|
||||||
// Unpack as two 32-bit integers so we have a vague attempt at reconstructing the value
|
|
||||||
const a = view.getUint32(offset + 0, true);
|
|
||||||
const b = view.getUint32(offset + 4, true);
|
|
||||||
|
|
||||||
// Is this negative?
|
|
||||||
if (b & 0x80000000)
|
|
||||||
{
|
|
||||||
// Can only convert from twos-complement with 32-bit arithmetic so shave off the upper 32-bits
|
|
||||||
// TODO(don): Crazy data loss here
|
|
||||||
const v = -(~(a - 1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Can't do bit arithmetic above 32-bits in JS so combine using power-of-two math
|
|
||||||
const v = a + (b * Math.pow(2, 32));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(don): Potentially massive data loss!
|
|
||||||
snapshots_view.setFloat32(offset, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function DecodeSnapshotHeader(self, data_view_reader, length)
|
|
||||||
{
|
|
||||||
// Message-specific header
|
|
||||||
let message = { };
|
|
||||||
message.messageStart = data_view_reader.Offset;
|
|
||||||
message.nbSnapshots = data_view_reader.GetUInt32();
|
|
||||||
message.propertyFrame = data_view_reader.GetUInt32();
|
|
||||||
message.snapshotsStart = data_view_reader.Offset;
|
|
||||||
message.snapshotsLength = length - (message.snapshotsStart - message.messageStart);
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function ProcessSnapshots(self, snapshot_data, message)
|
|
||||||
{
|
|
||||||
if (self.Settings.IsPaused)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const empty_text_entry = {
|
|
||||||
offset: 0,
|
|
||||||
length: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
const snapshots_length = message.nbSnapshots * g_nbBytesPerSnapshot;
|
|
||||||
const snapshots_view = new DataView(snapshot_data, message.snapshotsStart, snapshots_length);
|
|
||||||
|
|
||||||
for (let offset = 0; offset < snapshots_length; offset += g_nbBytesPerSnapshot)
|
|
||||||
{
|
|
||||||
// Get name hash and lookup in name map
|
|
||||||
const name_hash = snapshots_view.getUint32(offset, true);
|
|
||||||
const [ name_exists, name ] = self.glCanvas.nameMap.Get(name_hash);
|
|
||||||
|
|
||||||
// If the name doesn't exist in the map yet, request it from the server
|
|
||||||
if (!name_exists)
|
|
||||||
{
|
|
||||||
if (self.Server.Connected())
|
|
||||||
{
|
|
||||||
self.Server.Send("GSMP" + name_hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add snapshot name text buffer location
|
|
||||||
const text_entry = name.textEntry != null ? name.textEntry : empty_text_entry;
|
|
||||||
snapshots_view.setFloat32(offset + 0, text_entry.offset, true);
|
|
||||||
snapshots_view.setFloat32(offset + 4, text_entry.length, true);
|
|
||||||
|
|
||||||
// Heat colour style falloff to quickly identify modified properties
|
|
||||||
let r = 255, g = 255, b = 255;
|
|
||||||
const prev_value_frame = snapshots_view.getUint32(offset + 32, true);
|
|
||||||
const frame_delta = message.propertyFrame - prev_value_frame;
|
|
||||||
if (frame_delta < 64)
|
|
||||||
{
|
|
||||||
g = Math.min(Math.min(frame_delta, 32) * 8, 255);
|
|
||||||
b = Math.min(frame_delta * 4, 255);
|
|
||||||
}
|
|
||||||
snapshots_view.setUint8(offset + 8, r);
|
|
||||||
snapshots_view.setUint8(offset + 9, g);
|
|
||||||
snapshots_view.setUint8(offset + 10, b);
|
|
||||||
|
|
||||||
const snapshot_type = snapshots_view.getUint32(offset + 12, true);
|
|
||||||
switch (snapshot_type)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
case 4:
|
|
||||||
case 7:
|
|
||||||
snapshots_view.setFloat32(offset + 16, snapshots_view.getFloat64(offset + 16, true), true);
|
|
||||||
snapshots_view.setFloat32(offset + 24, snapshots_view.getFloat64(offset + 24, true), true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Unpack 64-bit integers stored full precision in the logs and view them to the best of our current abilities
|
|
||||||
case 5:
|
|
||||||
SInt64ToFloat32(snapshots_view, offset + 16);
|
|
||||||
SInt64ToFloat32(snapshots_view, offset + 24);
|
|
||||||
case 6:
|
|
||||||
UInt64ToFloat32(snapshots_view, offset + 16);
|
|
||||||
UInt64ToFloat32(snapshots_view, offset + 24);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to floats for GPU
|
|
||||||
return new Float32Array(snapshot_data, message.snapshotsStart, message.nbSnapshots * g_nbFloatsPerSnapshot);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnPropertySnapshots(self, socket, data_view_reader, length)
|
|
||||||
{
|
|
||||||
// Discard any new snapshots while paused and connected
|
|
||||||
// Otherwise this stops a paused Remotery from loading new samples from disk
|
|
||||||
if (self.Settings.IsPaused && self.Server.Connected())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Binary decode incoming snapshot data
|
|
||||||
const message = DecodeSnapshotHeader(self, data_view_reader, length);
|
|
||||||
message.snapshotFloats = ProcessSnapshots(self, data_view_reader.DataView.buffer, message);
|
|
||||||
|
|
||||||
// Add to frame history
|
|
||||||
const thread_frame = new PropertySnapshotFrame(message);
|
|
||||||
const frame_history = self.PropertyFrameHistory;
|
|
||||||
frame_history.push(thread_frame);
|
|
||||||
|
|
||||||
// Discard old frames to keep memory-use constant
|
|
||||||
var max_nb_frames = 10000;
|
|
||||||
var extra_frames = frame_history.length - max_nb_frames;
|
|
||||||
if (extra_frames > 0)
|
|
||||||
frame_history.splice(0, extra_frames);
|
|
||||||
|
|
||||||
// Set on the window if connected as this implies a trace is being loaded, which we want to speed up
|
|
||||||
if (self.Server.Connected())
|
|
||||||
{
|
|
||||||
self.propertyGridWindow.UpdateEntries(message.nbSnapshots, message.snapshotFloats);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function OnTimelineCheck(self, name, evt)
|
|
||||||
{
|
|
||||||
// Show/hide the equivalent sample window and move all the others to occupy any left-over space
|
|
||||||
var target = DOM.Event.GetNode(evt);
|
|
||||||
self.gridWindows[name].SetVisible(target.checked);
|
|
||||||
MoveGridWindows(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function MoveGridWindows(self)
|
|
||||||
{
|
|
||||||
// Stack all windows next to each other
|
|
||||||
let xpos = 0;
|
|
||||||
for (let i in self.gridWindows)
|
|
||||||
{
|
|
||||||
const grid_window = self.gridWindows[i];
|
|
||||||
if (grid_window.visible)
|
|
||||||
{
|
|
||||||
grid_window.SetXPos(xpos++, self.SampleTimelineWindow.Window, self.Console.Window);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnSampleHover(self, thread_name, hover)
|
|
||||||
{
|
|
||||||
if (!self.Settings.IsPaused)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for the grid window for the thread being hovered over
|
|
||||||
for (let window_thread_name in self.gridWindows)
|
|
||||||
{
|
|
||||||
if (window_thread_name == thread_name)
|
|
||||||
{
|
|
||||||
const grid_window = self.gridWindows[thread_name];
|
|
||||||
|
|
||||||
// Populate with the sample under hover
|
|
||||||
if (hover != null)
|
|
||||||
{
|
|
||||||
const frame = hover[0];
|
|
||||||
grid_window.UpdateEntries(frame.NbSamples, frame.sampleFloats);
|
|
||||||
}
|
|
||||||
|
|
||||||
// When there's no hover, go back to the selected frame
|
|
||||||
else if (self.SelectedFrames[thread_name])
|
|
||||||
{
|
|
||||||
const frame = self.SelectedFrames[thread_name];
|
|
||||||
grid_window.UpdateEntries(frame.NbSamples, frame.sampleFloats);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise display the last sample in the frame
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const frames = self.FrameHistory[thread_name];
|
|
||||||
const frame = frames[frames.length - 1];
|
|
||||||
grid_window.UpdateEntries(frame.NbSamples, frame.sampleFloats);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnSampleSelected(self, thread_name, select)
|
|
||||||
{
|
|
||||||
// Lookup sample window
|
|
||||||
if (thread_name in self.gridWindows)
|
|
||||||
{
|
|
||||||
const grid_window = self.gridWindows[thread_name];
|
|
||||||
|
|
||||||
// Set the grid window to the selected frame if valid
|
|
||||||
if (select)
|
|
||||||
{
|
|
||||||
const frame = select[0];
|
|
||||||
self.SelectedFrames[thread_name] = frame;
|
|
||||||
grid_window.UpdateEntries(frame.NbSamples, frame.sampleFloats);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise deselect
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const frames = self.FrameHistory[thread_name];
|
|
||||||
const frame = frames[frames.length - 1];
|
|
||||||
self.SelectedFrames[thread_name] = null;
|
|
||||||
grid_window.UpdateEntries(frame.NbSamples, frame.sampleFloats);
|
|
||||||
self.SampleTimelineWindow.Deselect(thread_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnResizeWindow(self)
|
|
||||||
{
|
|
||||||
var w = window.innerWidth;
|
|
||||||
var h = window.innerHeight;
|
|
||||||
|
|
||||||
// Resize windows
|
|
||||||
self.Console.WindowResized(w, h);
|
|
||||||
self.TitleWindow.WindowResized(w, h);
|
|
||||||
self.SampleTimelineWindow.WindowResized(10, w / 2 - 5, self.TitleWindow.Window);
|
|
||||||
self.ProcessorTimelineWindow.WindowResized(w / 2 + 5, w / 2 - 5, self.TitleWindow.Window);
|
|
||||||
for (var i in self.gridWindows)
|
|
||||||
{
|
|
||||||
self.gridWindows[i].WindowResized(self.SampleTimelineWindow.Window, self.Console.Window);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnTimelineMoved(self, timeline)
|
|
||||||
{
|
|
||||||
if (self.Settings.SyncTimelines)
|
|
||||||
{
|
|
||||||
let other_timeline = timeline == self.ProcessorTimelineWindow ? self.SampleTimelineWindow : self.ProcessorTimelineWindow;
|
|
||||||
other_timeline.SetTimeRange(timeline.TimeRange.Start_us, timeline.TimeRange.Span_us);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Remotery;
|
|
||||||
})();
|
|
28
profiler/vis/Code/SampleGlobals.js
vendored
28
profiler/vis/Code/SampleGlobals.js
vendored
|
@ -1,28 +0,0 @@
|
||||||
|
|
||||||
// Sample properties when viewed as an array of floats
|
|
||||||
const g_nbFloatsPerSample = 14;
|
|
||||||
const g_sampleOffsetFloats_NameOffset = 0;
|
|
||||||
const g_sampleOffsetFloats_NameLength = 1;
|
|
||||||
const g_sampleOffsetFloats_Start = 3;
|
|
||||||
const g_sampleOffsetFloats_Length = 5;
|
|
||||||
const g_sampleOffsetFloats_Self = 7;
|
|
||||||
const g_sampleOffsetFloats_GpuToCpu = 9;
|
|
||||||
const g_sampleOffsetFloats_Calls = 11;
|
|
||||||
const g_sampleOffsetFloats_Recurse = 12;
|
|
||||||
|
|
||||||
// Sample properties when viewed as bytes
|
|
||||||
const g_nbBytesPerSample = g_nbFloatsPerSample * 4;
|
|
||||||
const g_sampleOffsetBytes_NameOffset = g_sampleOffsetFloats_NameOffset * 4;
|
|
||||||
const g_sampleOffsetBytes_NameLength = g_sampleOffsetFloats_NameLength * 4;
|
|
||||||
const g_sampleOffsetBytes_Colour = 8;
|
|
||||||
const g_sampleOffsetBytes_Depth = 11;
|
|
||||||
const g_sampleOffsetBytes_Start = g_sampleOffsetFloats_Start * 4;
|
|
||||||
const g_sampleOffsetBytes_Length = g_sampleOffsetFloats_Length * 4;
|
|
||||||
const g_sampleOffsetBytes_Self = g_sampleOffsetFloats_Self * 4;
|
|
||||||
const g_sampleOffsetBytes_GpuToCpu = g_sampleOffsetFloats_GpuToCpu * 4;
|
|
||||||
const g_sampleOffsetBytes_Calls = g_sampleOffsetFloats_Calls * 4;
|
|
||||||
const g_sampleOffsetBytes_Recurse = g_sampleOffsetFloats_Recurse * 4;
|
|
||||||
|
|
||||||
// Snapshot properties
|
|
||||||
const g_nbFloatsPerSnapshot = 10;
|
|
||||||
const g_nbBytesPerSnapshot = g_nbFloatsPerSnapshot * 4;
|
|
162
profiler/vis/Code/Shaders/Grid.glsl
vendored
162
profiler/vis/Code/Shaders/Grid.glsl
vendored
|
@ -1,162 +0,0 @@
|
||||||
const GridShaderShared = ShaderShared + `
|
|
||||||
|
|
||||||
#define RowHeight 15.0
|
|
||||||
|
|
||||||
struct Grid
|
|
||||||
{
|
|
||||||
float minX;
|
|
||||||
float minY;
|
|
||||||
float maxX;
|
|
||||||
float maxY;
|
|
||||||
float pixelOffsetX;
|
|
||||||
float pixelOffsetY;
|
|
||||||
};
|
|
||||||
|
|
||||||
uniform Viewport inViewport;
|
|
||||||
uniform Grid inGrid;
|
|
||||||
|
|
||||||
float Row(vec2 pixel_position)
|
|
||||||
{
|
|
||||||
return floor(pixel_position.y / RowHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 RowColour(float row)
|
|
||||||
{
|
|
||||||
float row_grey = (int(row) & 1) == 0 ? 0.25 : 0.23;
|
|
||||||
return vec3(row_grey);
|
|
||||||
}
|
|
||||||
|
|
||||||
`;
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
// Vertex Shader
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
const GridVShader = GridShaderShared + `
|
|
||||||
|
|
||||||
out vec2 varPixelPosition;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
vec2 position = QuadPosition(gl_VertexID, inGrid.minX, inGrid.minY, inGrid.maxX, inGrid.maxY);
|
|
||||||
vec4 ndc_pos = UVToNDC(inViewport, position);
|
|
||||||
|
|
||||||
gl_Position = ndc_pos;
|
|
||||||
varPixelPosition = position - vec2(inGrid.minX, inGrid.minY) + vec2(inGrid.pixelOffsetX, inGrid.pixelOffsetY);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const GridNumberVShader = GridShaderShared + `
|
|
||||||
|
|
||||||
out vec2 varPixelPosition;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
vec2 position = QuadPosition(gl_VertexID, inGrid.minX, inGrid.minY, inGrid.maxX, inGrid.maxY);
|
|
||||||
vec4 ndc_pos = UVToNDC(inViewport, position);
|
|
||||||
|
|
||||||
gl_Position = ndc_pos;
|
|
||||||
varPixelPosition = position - vec2(inGrid.minX, inGrid.minY) + vec2(inGrid.pixelOffsetX, inGrid.pixelOffsetY);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
// Fragment Shader
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
const GridFShader = GridShaderShared + `
|
|
||||||
|
|
||||||
// Array of samples
|
|
||||||
uniform sampler2D inSamples;
|
|
||||||
uniform float inSamplesLength;
|
|
||||||
uniform float inFloatsPerSample;
|
|
||||||
uniform float inNbSamples;
|
|
||||||
|
|
||||||
in vec2 varPixelPosition;
|
|
||||||
|
|
||||||
out vec4 outColour;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
// Font description
|
|
||||||
float font_width_px = inTextBufferDesc.fontWidth;
|
|
||||||
float font_height_px = inTextBufferDesc.fontHeight;
|
|
||||||
float text_buffer_length = inTextBufferDesc.textBufferLength;
|
|
||||||
|
|
||||||
// Which row are we on?
|
|
||||||
float row = Row(varPixelPosition);
|
|
||||||
vec3 row_colour = RowColour(row);
|
|
||||||
|
|
||||||
float text_weight = 0.0;
|
|
||||||
vec3 text_colour = vec3(0.0);
|
|
||||||
if (row < inNbSamples)
|
|
||||||
{
|
|
||||||
// Unpack colour and depth
|
|
||||||
int colour_depth = floatBitsToInt(TextureBufferLookup(inSamples, row * inFloatsPerSample + 2.0, inSamplesLength).r);
|
|
||||||
text_colour.r = float(colour_depth & 255) / 255.0;
|
|
||||||
text_colour.g = float((colour_depth >> 8) & 255) / 255.0;
|
|
||||||
text_colour.b = float((colour_depth >> 16) & 255) / 255.0;
|
|
||||||
float depth = float(colour_depth >> 24);
|
|
||||||
|
|
||||||
float text_buffer_offset = TextureBufferLookup(inSamples, row * inFloatsPerSample + 0.0, inSamplesLength).r;
|
|
||||||
float text_length_chars = TextureBufferLookup(inSamples, row * inFloatsPerSample + 1.0, inSamplesLength).r;
|
|
||||||
float text_length_px = text_length_chars * font_width_px;
|
|
||||||
|
|
||||||
// Pixel position within the row
|
|
||||||
vec2 pos_in_box_px;
|
|
||||||
pos_in_box_px.x = varPixelPosition.x;
|
|
||||||
pos_in_box_px.y = varPixelPosition.y - row * RowHeight;
|
|
||||||
|
|
||||||
// Get text at this position
|
|
||||||
vec2 text_start_px = vec2(4.0 + depth * 10.0, 3.0);
|
|
||||||
text_weight = LookupText(pos_in_box_px - text_start_px, text_buffer_offset, text_length_chars);
|
|
||||||
}
|
|
||||||
|
|
||||||
outColour = vec4(mix(row_colour, text_colour, text_weight), 1.0);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const GridNumberFShader = GridShaderShared + `
|
|
||||||
|
|
||||||
// Array of samples
|
|
||||||
uniform sampler2D inSamples;
|
|
||||||
uniform float inSamplesLength;
|
|
||||||
uniform float inFloatsPerSample;
|
|
||||||
uniform float inNbSamples;
|
|
||||||
|
|
||||||
// Offset within the sample
|
|
||||||
uniform float inNumberOffset;
|
|
||||||
|
|
||||||
uniform float inNbFloatChars;
|
|
||||||
|
|
||||||
in vec2 varPixelPosition;
|
|
||||||
|
|
||||||
out vec4 outColour;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
// Font description
|
|
||||||
float font_width_px = inTextBufferDesc.fontWidth;
|
|
||||||
float font_height_px = inTextBufferDesc.fontHeight;
|
|
||||||
float text_buffer_length = inTextBufferDesc.textBufferLength;
|
|
||||||
|
|
||||||
// Which row are we on?
|
|
||||||
float row = Row(varPixelPosition);
|
|
||||||
vec3 row_colour = RowColour(row);
|
|
||||||
float text_weight = 0.0;
|
|
||||||
if (row < inNbSamples)
|
|
||||||
{
|
|
||||||
// Pixel position within the row
|
|
||||||
vec2 pos_in_box_px;
|
|
||||||
pos_in_box_px.x = varPixelPosition.x;
|
|
||||||
pos_in_box_px.y = varPixelPosition.y - row * RowHeight;
|
|
||||||
|
|
||||||
// Get the number at this pixel
|
|
||||||
const vec2 text_start_px = vec2(4.0, 3.0);
|
|
||||||
float number = TextureBufferLookup(inSamples, row * inFloatsPerSample + inNumberOffset, inSamplesLength).r;
|
|
||||||
text_weight = LookupNumber(pos_in_box_px - text_start_px, number, inNbFloatChars);
|
|
||||||
}
|
|
||||||
|
|
||||||
outColour = vec4(mix(row_colour, vec3(1.0), text_weight), 1.0);
|
|
||||||
}
|
|
||||||
`;
|
|
154
profiler/vis/Code/Shaders/Shared.glsl
vendored
154
profiler/vis/Code/Shaders/Shared.glsl
vendored
|
@ -1,154 +0,0 @@
|
||||||
const ShaderShared = `#version 300 es
|
|
||||||
|
|
||||||
precision mediump float;
|
|
||||||
|
|
||||||
struct Viewport
|
|
||||||
{
|
|
||||||
float width;
|
|
||||||
float height;
|
|
||||||
};
|
|
||||||
|
|
||||||
vec2 QuadPosition(int vertex_id, float min_x, float min_y, float max_x, float max_y)
|
|
||||||
{
|
|
||||||
// Quad indices are:
|
|
||||||
//
|
|
||||||
// 2 3
|
|
||||||
// +----+
|
|
||||||
// | |
|
|
||||||
// +----+
|
|
||||||
// 0 1
|
|
||||||
//
|
|
||||||
vec2 position;
|
|
||||||
position.x = (vertex_id & 1) == 0 ? min_x : max_x;
|
|
||||||
position.y = (vertex_id & 2) == 0 ? min_y : max_y;
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 UVToNDC(Viewport viewport, vec2 uv)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// NDC is:
|
|
||||||
// -1 to 1, left to right
|
|
||||||
// -1 to 1, bottom to top
|
|
||||||
//
|
|
||||||
vec4 ndc_pos;
|
|
||||||
ndc_pos.x = (uv.x / viewport.width) * 2.0 - 1.0;
|
|
||||||
ndc_pos.y = 1.0 - (uv.y / viewport.height) * 2.0;
|
|
||||||
ndc_pos.z = 0.0;
|
|
||||||
ndc_pos.w = 1.0;
|
|
||||||
return ndc_pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 TextureBufferLookup(sampler2D sampler, float index, float length)
|
|
||||||
{
|
|
||||||
vec2 uv = vec2((index + 0.5) / length, 0.5);
|
|
||||||
return texture(sampler, uv);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TextBufferDesc
|
|
||||||
{
|
|
||||||
float fontWidth;
|
|
||||||
float fontHeight;
|
|
||||||
float textBufferLength;
|
|
||||||
};
|
|
||||||
|
|
||||||
uniform sampler2D inFontAtlasTexture;
|
|
||||||
uniform sampler2D inTextBuffer;
|
|
||||||
uniform TextBufferDesc inTextBufferDesc;
|
|
||||||
|
|
||||||
float LookupCharacter(float char_ascii, float pos_x, float pos_y, float font_width_px, float font_height_px)
|
|
||||||
{
|
|
||||||
// 2D index of the ASCII character in the font atlas
|
|
||||||
float char_index_y = floor(char_ascii / 16.0);
|
|
||||||
float char_index_x = char_ascii - char_index_y * 16.0;
|
|
||||||
|
|
||||||
// Start UV of the character in the font atlas
|
|
||||||
float char_base_uv_x = char_index_x / 16.0;
|
|
||||||
float char_base_uv_y = char_index_y / 16.0;
|
|
||||||
|
|
||||||
// UV within the character itself, scaled to the font atlas
|
|
||||||
float char_uv_x = pos_x / (font_width_px * 16.0);
|
|
||||||
float char_uv_y = pos_y / (font_height_px * 16.0);
|
|
||||||
|
|
||||||
vec2 uv;
|
|
||||||
uv.x = char_base_uv_x + char_uv_x;
|
|
||||||
uv.y = char_base_uv_y + char_uv_y;
|
|
||||||
|
|
||||||
// Strip colour and return alpha only
|
|
||||||
return texture(inFontAtlasTexture, uv).a;
|
|
||||||
}
|
|
||||||
|
|
||||||
float LookupText(vec2 render_pos_px, float text_buffer_offset, float text_length_chars)
|
|
||||||
{
|
|
||||||
// Font description
|
|
||||||
float font_width_px = inTextBufferDesc.fontWidth;
|
|
||||||
float font_height_px = inTextBufferDesc.fontHeight;
|
|
||||||
float text_buffer_length = inTextBufferDesc.textBufferLength;
|
|
||||||
float text_length_px = text_length_chars * font_width_px;
|
|
||||||
|
|
||||||
// Text pixel position clamped to the bounds of the full word, allowing leakage to neighbouring NULL characters to pad zeroes
|
|
||||||
vec2 text_pixel_pos;
|
|
||||||
text_pixel_pos.x = max(min(render_pos_px.x, text_length_px), -1.0);
|
|
||||||
text_pixel_pos.y = max(min(render_pos_px.y, font_height_px - 1.0), 0.0);
|
|
||||||
|
|
||||||
// Index of the current character in the text buffer
|
|
||||||
float text_index = text_buffer_offset + floor(text_pixel_pos.x / font_width_px);
|
|
||||||
|
|
||||||
// Sample the 1D text buffer to get the ASCII character index
|
|
||||||
float char_ascii = TextureBufferLookup(inTextBuffer, text_index, text_buffer_length).a * 255.0;
|
|
||||||
|
|
||||||
return LookupCharacter(char_ascii,
|
|
||||||
text_pixel_pos.x - (text_index - text_buffer_offset) * font_width_px,
|
|
||||||
text_pixel_pos.y,
|
|
||||||
font_width_px, font_height_px);
|
|
||||||
}
|
|
||||||
|
|
||||||
float NbIntegerCharsForNumber(float number)
|
|
||||||
{
|
|
||||||
float number_int = floor(number);
|
|
||||||
return number_int == 0.0 ? 1.0 : floor(log(number_int) / 2.302585092994046) + 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base-10 lookup table for shifting digits of a float to the range 0-9 where they can be rendered
|
|
||||||
const float g_Multipliers[14] = float[14](
|
|
||||||
// Decimal part multipliers
|
|
||||||
1000.0, 100.0, 10.0,
|
|
||||||
// Zero entry for maintaining the ASCII "." base when rendering the period
|
|
||||||
0.0,
|
|
||||||
// Integer part multipliers
|
|
||||||
1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, 0.00000001, 0.000000001 );
|
|
||||||
|
|
||||||
float LookupNumber(vec2 render_pos_px, float number, float nb_float_chars)
|
|
||||||
{
|
|
||||||
// Font description
|
|
||||||
float font_width_px = inTextBufferDesc.fontWidth;
|
|
||||||
float font_height_px = inTextBufferDesc.fontHeight;
|
|
||||||
float text_buffer_length = inTextBufferDesc.textBufferLength;
|
|
||||||
|
|
||||||
float number_integer_chars = NbIntegerCharsForNumber(number);
|
|
||||||
|
|
||||||
// Clip
|
|
||||||
render_pos_px.y = max(min(render_pos_px.y, font_height_px - 1.0), 0.0);
|
|
||||||
|
|
||||||
float number_index = floor(render_pos_px.x / font_width_px);
|
|
||||||
|
|
||||||
if (number_index >= 0.0 && number_index < number_integer_chars + nb_float_chars)
|
|
||||||
{
|
|
||||||
// When we are indexing the period separating integer and decimal, set the base to ASCII "."
|
|
||||||
// The lookup table stores zero for this entry, multipying with the addend to produce no shift from this base
|
|
||||||
float base = (number_index == number_integer_chars) ? 46.0 : 48.0;
|
|
||||||
|
|
||||||
// Calculate digit using the current number index base-10 shift
|
|
||||||
float multiplier = g_Multipliers[int(number_integer_chars - number_index) + 3];
|
|
||||||
float number_shifted_int = floor(number * multiplier);
|
|
||||||
float number_digit = floor(mod(number_shifted_int, 10.0));
|
|
||||||
|
|
||||||
return LookupCharacter(base + number_digit,
|
|
||||||
render_pos_px.x - number_index * font_width_px,
|
|
||||||
render_pos_px.y,
|
|
||||||
font_width_px, font_height_px);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
`;
|
|
337
profiler/vis/Code/Shaders/Timeline.glsl
vendored
337
profiler/vis/Code/Shaders/Timeline.glsl
vendored
|
@ -1,337 +0,0 @@
|
||||||
const TimelineShaderShared = ShaderShared + `
|
|
||||||
|
|
||||||
#define SAMPLE_HEIGHT 16.0
|
|
||||||
#define SAMPLE_BORDER 2.0
|
|
||||||
#define SAMPLE_Y_SPACING (SAMPLE_HEIGHT + SAMPLE_BORDER * 2.0)
|
|
||||||
|
|
||||||
#define PIXEL_ROUNDED_OFFSETS
|
|
||||||
|
|
||||||
struct Container
|
|
||||||
{
|
|
||||||
float x0;
|
|
||||||
float y0;
|
|
||||||
float x1;
|
|
||||||
float y1;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TimeRange
|
|
||||||
{
|
|
||||||
float msStart;
|
|
||||||
float msPerPixel;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Row
|
|
||||||
{
|
|
||||||
float yOffset;
|
|
||||||
};
|
|
||||||
|
|
||||||
uniform Viewport inViewport;
|
|
||||||
uniform TimeRange inTimeRange;
|
|
||||||
uniform Container inContainer;
|
|
||||||
uniform Row inRow;
|
|
||||||
|
|
||||||
float PixelOffset(float time_ms)
|
|
||||||
{
|
|
||||||
float offset = (time_ms - inTimeRange.msStart) * inTimeRange.msPerPixel;
|
|
||||||
#ifdef PIXEL_ROUNDED_OFFSETS
|
|
||||||
return floor(offset);
|
|
||||||
#else
|
|
||||||
return offset;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
float PixelSize(float time_ms)
|
|
||||||
{
|
|
||||||
float size = time_ms * inTimeRange.msPerPixel;
|
|
||||||
#ifdef PIXEL_ROUNDED_OFFSETS
|
|
||||||
return floor(size);
|
|
||||||
#else
|
|
||||||
return size;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 SampleQuad(int vertex_id, vec4 in_sample_textoffset, float padding, out vec4 out_quad_pos_size_px)
|
|
||||||
{
|
|
||||||
// Unpack input data
|
|
||||||
float ms_start = in_sample_textoffset.x;
|
|
||||||
float ms_length = in_sample_textoffset.y;
|
|
||||||
float depth = in_sample_textoffset.z;
|
|
||||||
|
|
||||||
// Determine pixel range of the sample
|
|
||||||
float x0 = PixelOffset(ms_start);
|
|
||||||
float x1 = x0 + PixelSize(ms_length);
|
|
||||||
|
|
||||||
// Calculate box to render
|
|
||||||
// Ensure no sample is less than one pixel in length and so is always visible
|
|
||||||
float offset_x = inContainer.x0 + x0 - padding;
|
|
||||||
float offset_y = inRow.yOffset + (depth - 1.0) * SAMPLE_Y_SPACING + SAMPLE_BORDER - padding;
|
|
||||||
float size_x = max(x1 - x0, 1.0) + padding * 2.0;
|
|
||||||
float size_y = SAMPLE_HEIGHT + padding * 2.0;
|
|
||||||
|
|
||||||
// Box range clipped to container bounds
|
|
||||||
float min_x = min(max(offset_x, inContainer.x0), inContainer.x1);
|
|
||||||
float min_y = min(max(offset_y, inContainer.y0), inContainer.y1);
|
|
||||||
float max_x = min(max(offset_x + size_x, inContainer.x0), inContainer.x1);
|
|
||||||
float max_y = min(max(offset_y + size_y, inContainer.y0), inContainer.y1);
|
|
||||||
|
|
||||||
// Box quad position in NDC
|
|
||||||
vec2 position = QuadPosition(vertex_id, min_x, min_y, max_x, max_y);
|
|
||||||
vec4 ndc_pos = UVToNDC(inViewport, position);
|
|
||||||
|
|
||||||
out_quad_pos_size_px.xy = vec2(position.x - offset_x, position.y - offset_y);
|
|
||||||
out_quad_pos_size_px.zw = vec2(max_x - min_x, max_y - min_y);
|
|
||||||
|
|
||||||
return ndc_pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
`;
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
// Sample Rendering
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
const TimelineVShader = TimelineShaderShared + `
|
|
||||||
|
|
||||||
in vec4 inSample_TextOffset;
|
|
||||||
in vec4 inColour_TextLength;
|
|
||||||
|
|
||||||
out vec4 varColour_TimeMs;
|
|
||||||
out vec4 varPosInBoxPx_TextEntry;
|
|
||||||
out float varTimeChars;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
// Unpack input data
|
|
||||||
float ms_length = inSample_TextOffset.y;
|
|
||||||
float text_buffer_offset = inSample_TextOffset.w;
|
|
||||||
vec3 box_colour = inColour_TextLength.rgb;
|
|
||||||
float text_length_chars = inColour_TextLength.w;
|
|
||||||
|
|
||||||
// Calculate number of characters required to display the millisecond time
|
|
||||||
float time_chars = NbIntegerCharsForNumber(ms_length);
|
|
||||||
|
|
||||||
// Calculate sample quad vertex positions
|
|
||||||
vec4 quad_pos_size_px;
|
|
||||||
gl_Position = SampleQuad(gl_VertexID, inSample_TextOffset, 0.0, quad_pos_size_px);
|
|
||||||
|
|
||||||
// Pack for fragment shader
|
|
||||||
varColour_TimeMs = vec4(box_colour / 255.0, ms_length);
|
|
||||||
varPosInBoxPx_TextEntry = vec4(quad_pos_size_px.x, quad_pos_size_px.y, text_buffer_offset, text_length_chars);
|
|
||||||
varTimeChars = time_chars;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const TimelineFShader = TimelineShaderShared + `
|
|
||||||
|
|
||||||
in vec4 varColour_TimeMs;
|
|
||||||
in vec4 varPosInBoxPx_TextEntry;
|
|
||||||
in float varTimeChars;
|
|
||||||
|
|
||||||
out vec4 outColour;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
// Font description
|
|
||||||
float font_width_px = inTextBufferDesc.fontWidth;
|
|
||||||
float font_height_px = inTextBufferDesc.fontHeight;
|
|
||||||
|
|
||||||
// Text range in the text buffer
|
|
||||||
vec2 pos_in_box_px = varPosInBoxPx_TextEntry.xy;
|
|
||||||
float text_buffer_offset = varPosInBoxPx_TextEntry.z;
|
|
||||||
float text_length_chars = varPosInBoxPx_TextEntry.w;
|
|
||||||
float text_length_px = text_length_chars * font_width_px;
|
|
||||||
|
|
||||||
// Text placement offset within the box
|
|
||||||
const vec2 text_start_px = vec2(10.0, 3.0);
|
|
||||||
|
|
||||||
vec3 box_colour = varColour_TimeMs.rgb;
|
|
||||||
|
|
||||||
// Add a subtle border to the box so that you can visually separate samples when they are next to each other
|
|
||||||
vec2 top_left = min(pos_in_box_px.xy, 2.0);
|
|
||||||
float both = min(top_left.x, top_left.y);
|
|
||||||
box_colour *= (0.8 + both * 0.1);
|
|
||||||
|
|
||||||
float text_weight = 0.0;
|
|
||||||
|
|
||||||
// Are we over the time number or the text?
|
|
||||||
float text_end_px = text_start_px.x + text_length_px;
|
|
||||||
float number_start_px = text_end_px + font_width_px * 2.0;
|
|
||||||
if (pos_in_box_px.x > number_start_px)
|
|
||||||
{
|
|
||||||
vec2 time_pixel_pos;
|
|
||||||
time_pixel_pos.x = pos_in_box_px.x - number_start_px;
|
|
||||||
time_pixel_pos.y = max(min(pos_in_box_px.y - text_start_px.y, font_height_px - 1.0), 0.0);
|
|
||||||
|
|
||||||
// Time number
|
|
||||||
float time_ms = varColour_TimeMs.w;
|
|
||||||
float time_index = floor(time_pixel_pos.x / font_width_px);
|
|
||||||
if (time_index < varTimeChars + 4.0)
|
|
||||||
{
|
|
||||||
text_weight = LookupNumber(time_pixel_pos, time_ms, 4.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// " ms" label at the end of the time
|
|
||||||
else if (time_index < varTimeChars + 7.0)
|
|
||||||
{
|
|
||||||
const float ms[3] = float[3] ( 32.0, 109.0, 115.0 );
|
|
||||||
float char = ms[int(time_index - (varTimeChars + 4.0))];
|
|
||||||
text_weight = LookupCharacter(char, time_pixel_pos.x - time_index * font_width_px, time_pixel_pos.y, font_width_px, font_height_px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
text_weight = LookupText(pos_in_box_px - text_start_px, text_buffer_offset, text_length_chars);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blend text onto the box
|
|
||||||
vec3 text_colour = vec3(0.0, 0.0, 0.0);
|
|
||||||
outColour = vec4(mix(box_colour, text_colour, text_weight), 1.0);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
// Sample Highlights
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
const TimelineHighlightVShader = TimelineShaderShared + `
|
|
||||||
|
|
||||||
uniform float inStartMs;
|
|
||||||
uniform float inLengthMs;
|
|
||||||
uniform float inDepth;
|
|
||||||
|
|
||||||
out vec4 varPosSize;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
// Calculate sample quad vertex positions
|
|
||||||
gl_Position = SampleQuad(gl_VertexID, vec4(inStartMs, inLengthMs, inDepth, 0.0), 1.0, varPosSize);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const TimelineHighlightFShader = TimelineShaderShared + `
|
|
||||||
|
|
||||||
// TODO(don): Vector uniforms, please!
|
|
||||||
uniform float inColourR;
|
|
||||||
uniform float inColourG;
|
|
||||||
uniform float inColourB;
|
|
||||||
|
|
||||||
in vec4 varPosSize;
|
|
||||||
|
|
||||||
out vec4 outColour;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
// Rounded pixel co-ordinates interpolating across the sample
|
|
||||||
vec2 pos = floor(varPosSize.xy);
|
|
||||||
|
|
||||||
// Sample size in pixel co-ordinates
|
|
||||||
vec2 size = floor(varPosSize.zw);
|
|
||||||
|
|
||||||
// Highlight thickness
|
|
||||||
float t = 2.0;
|
|
||||||
|
|
||||||
// Distance along axes to highlight edges
|
|
||||||
vec2 dmin = abs(pos - 0.0);
|
|
||||||
vec2 dmax = abs(pos - (size - 1.0));
|
|
||||||
|
|
||||||
// Take the closest distance
|
|
||||||
float dx = min(dmin.x, dmax.x);
|
|
||||||
float dy = min(dmin.y, dmax.y);
|
|
||||||
float d = min(dx, dy);
|
|
||||||
|
|
||||||
// Invert the distance and clamp to thickness
|
|
||||||
d = (t + 1.0) - min(d, t + 1.0);
|
|
||||||
|
|
||||||
// Scale with thickness for uniform intensity
|
|
||||||
d = d / (t + 1.0);
|
|
||||||
outColour = vec4(inColourR * d, inColourG * d, inColourB * d, d);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
// GPU->CPU Sample Sources
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
const TimelineGpuToCpuVShader = TimelineShaderShared + `
|
|
||||||
|
|
||||||
uniform float inStartMs;
|
|
||||||
uniform float inLengthMs;
|
|
||||||
uniform float inDepth;
|
|
||||||
|
|
||||||
out vec4 varPosSize;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
// Calculate sample quad vertex positions
|
|
||||||
gl_Position = SampleQuad(gl_VertexID, vec4(inStartMs, inLengthMs, inDepth, 0.0), 1.0, varPosSize);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
|
|
||||||
const TimelineGpuToCpuFShader = TimelineShaderShared + `
|
|
||||||
|
|
||||||
in vec4 varPosSize;
|
|
||||||
|
|
||||||
out vec4 outColour;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
// Rounded pixel co-ordinates interpolating across the sample
|
|
||||||
vec2 pos = floor(varPosSize.xy);
|
|
||||||
|
|
||||||
// Sample size in pixel co-ordinates
|
|
||||||
vec2 size = floor(varPosSize.zw);
|
|
||||||
|
|
||||||
// Distance to centre line, bumped out every period to create a dash
|
|
||||||
float dc = abs(pos.y - size.y / 2.0);
|
|
||||||
dc += (int(pos.x / 3.0) & 1) == 0 ? 100.0 : 0.0;
|
|
||||||
|
|
||||||
// Min with the start line
|
|
||||||
float ds = abs(pos.x - 0.0);
|
|
||||||
float d = min(dc, ds);
|
|
||||||
|
|
||||||
// Invert the distance for highlight
|
|
||||||
d = 1.0 - min(d, 1.0);
|
|
||||||
|
|
||||||
outColour = vec4(d, d, d, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
`;
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
// Background
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
const TimelineBackgroundVShader = TimelineShaderShared + `
|
|
||||||
|
|
||||||
uniform float inYOffset;
|
|
||||||
|
|
||||||
out vec2 varPosition;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
|
|
||||||
// Container quad position in NDC
|
|
||||||
vec2 position = QuadPosition(gl_VertexID, inContainer.x0, inContainer.y0, inContainer.x1, inContainer.y1);
|
|
||||||
gl_Position = UVToNDC(inViewport, position);
|
|
||||||
|
|
||||||
// Offset Y with scroll position
|
|
||||||
varPosition = vec2(position.x, position.y - inYOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
`;
|
|
||||||
|
|
||||||
const TimelineBackgroundFShader = TimelineShaderShared + `
|
|
||||||
|
|
||||||
in vec2 varPosition;
|
|
||||||
|
|
||||||
out vec4 outColour;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
vec2 pos = floor(varPosition);
|
|
||||||
float f = round(fract(pos.y / SAMPLE_Y_SPACING) * SAMPLE_Y_SPACING);
|
|
||||||
float g = f >= 1.0 && f <= (SAMPLE_Y_SPACING - 2.0) ? 0.30 : 0.23;
|
|
||||||
outColour = vec4(g, g, g, 1.0);
|
|
||||||
}
|
|
||||||
`;
|
|
33
profiler/vis/Code/Shaders/Window.glsl
vendored
33
profiler/vis/Code/Shaders/Window.glsl
vendored
|
@ -1,33 +0,0 @@
|
||||||
// -------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
// Vertex Shader
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
const WindowVShader = ShaderShared + `
|
|
||||||
|
|
||||||
uniform Viewport inViewport;
|
|
||||||
|
|
||||||
uniform float minX;
|
|
||||||
uniform float minY;
|
|
||||||
uniform float maxX;
|
|
||||||
uniform float maxY;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
vec2 position = QuadPosition(gl_VertexID, minX, minY, maxX, maxY);
|
|
||||||
gl_Position = UVToNDC(inViewport, position);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
// Fragment Shader
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
const WindowFShader = ShaderShared + `
|
|
||||||
|
|
||||||
out vec4 outColour;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
outColour = vec4(1.0, 1.0, 1.0, 0.0);
|
|
||||||
}
|
|
||||||
`;
|
|
34
profiler/vis/Code/ThreadFrame.js
vendored
34
profiler/vis/Code/ThreadFrame.js
vendored
|
@ -1,34 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
class ThreadFrame
|
|
||||||
{
|
|
||||||
constructor(message)
|
|
||||||
{
|
|
||||||
// Persist the required message data
|
|
||||||
this.NbSamples = message.nb_samples;
|
|
||||||
this.sampleDataView = message.sampleDataView;
|
|
||||||
this.sampleFloats = message.sampleFloats;
|
|
||||||
this.PartialTree = message.partial_tree > 0 ? true : false;
|
|
||||||
|
|
||||||
// Point to the first/last samples
|
|
||||||
const first_sample_start_us = this.sampleFloats[g_sampleOffsetFloats_Start] * 1000.0;
|
|
||||||
const last_sample_offset = (this.NbSamples - 1) * g_nbFloatsPerSample;
|
|
||||||
const last_sample_start_us = this.sampleFloats[last_sample_offset + g_sampleOffsetFloats_Start] * 1000.0;
|
|
||||||
const last_sample_length_us = this.sampleFloats[last_sample_offset + g_sampleOffsetFloats_Length] * 1000.0;
|
|
||||||
|
|
||||||
// Calculate the frame start/end times
|
|
||||||
this.StartTime_us = first_sample_start_us;
|
|
||||||
this.EndTime_us = last_sample_start_us + last_sample_length_us;
|
|
||||||
this.Length_us = this.EndTime_us - this.StartTime_us;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PropertySnapshotFrame
|
|
||||||
{
|
|
||||||
constructor(message)
|
|
||||||
{
|
|
||||||
this.nbSnapshots = message.nbSnapshots;
|
|
||||||
this.snapshots = message.snapshots;
|
|
||||||
this.snapshotFloats = message.snapshotFloats;
|
|
||||||
}
|
|
||||||
}
|
|
186
profiler/vis/Code/TimelineMarkers.js
vendored
186
profiler/vis/Code/TimelineMarkers.js
vendored
|
@ -1,186 +0,0 @@
|
||||||
|
|
||||||
function GetTimeText(seconds)
|
|
||||||
{
|
|
||||||
if (seconds < 0)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
var text = "";
|
|
||||||
|
|
||||||
// Add any contributing hours
|
|
||||||
var h = Math.floor(seconds / 3600);
|
|
||||||
seconds -= h * 3600;
|
|
||||||
if (h)
|
|
||||||
{
|
|
||||||
text += h + "h ";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add any contributing minutes
|
|
||||||
var m = Math.floor(seconds / 60);
|
|
||||||
seconds -= m * 60;
|
|
||||||
if (m)
|
|
||||||
{
|
|
||||||
text += m + "m ";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add any contributing seconds or always add seconds when hours or minutes have no contribution
|
|
||||||
// This ensures the 0s marker displays
|
|
||||||
var s = Math.floor(seconds);
|
|
||||||
seconds -= s;
|
|
||||||
if (s || text == "")
|
|
||||||
{
|
|
||||||
text += s + "s ";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add remaining milliseconds
|
|
||||||
var ms = Math.floor(seconds * 1000);
|
|
||||||
if (ms)
|
|
||||||
{
|
|
||||||
text += ms + "ms";
|
|
||||||
}
|
|
||||||
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class TimelineMarkers
|
|
||||||
{
|
|
||||||
constructor(timeline)
|
|
||||||
{
|
|
||||||
this.timeline = timeline;
|
|
||||||
|
|
||||||
// Need a 2D drawing context
|
|
||||||
this.markerContainer = timeline.Window.AddControlNew(new WM.Container(10, 10, 10, 10));
|
|
||||||
this.markerCanvas = document.createElement("canvas");
|
|
||||||
this.markerContainer.Node.appendChild(this.markerCanvas);
|
|
||||||
this.markerContext = this.markerCanvas.getContext("2d");
|
|
||||||
}
|
|
||||||
|
|
||||||
Draw(time_range)
|
|
||||||
{
|
|
||||||
let ctx = this.markerContext;
|
|
||||||
|
|
||||||
ctx.clearRect(0, 0, this.markerCanvas.width, this.markerCanvas.height);
|
|
||||||
|
|
||||||
// Setup render state for the time line markers
|
|
||||||
ctx.strokeStyle = "#BBB";
|
|
||||||
ctx.fillStyle = "#BBB";
|
|
||||||
ctx.lineWidth = 1;
|
|
||||||
ctx.font = "9px LocalFiraCode";
|
|
||||||
|
|
||||||
// A list of all supported units of time (measured in seconds) that require markers
|
|
||||||
let units = [ 0.001, 0.01, 0.1, 1, 10, 60, 60 * 5, 60 * 60, 60 * 60 * 24 ];
|
|
||||||
|
|
||||||
// Given the current pixel size of a second, calculate the spacing for each unit marker
|
|
||||||
let second_pixel_size = time_range.PixelSize(1000 * 1000);
|
|
||||||
let sizeof_units = [ ];
|
|
||||||
for (let unit of units)
|
|
||||||
{
|
|
||||||
sizeof_units.push(unit * second_pixel_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate whether each unit marker is visible at the current zoom level
|
|
||||||
var show_unit = [ ];
|
|
||||||
for (let sizeof_unit of sizeof_units)
|
|
||||||
{
|
|
||||||
show_unit.push(Math.max(Math.min((sizeof_unit - 4) * 0.25, 1), 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the first visible unit
|
|
||||||
for (let i = 0; i < units.length; i++)
|
|
||||||
{
|
|
||||||
if (show_unit[i] > 0)
|
|
||||||
{
|
|
||||||
// Cut out unit information for the first set of units not visible
|
|
||||||
units = units.slice(i);
|
|
||||||
sizeof_units = sizeof_units.slice(i);
|
|
||||||
show_unit = show_unit.slice(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let timeline_end = this.markerCanvas.width;
|
|
||||||
for (let i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
// Round the start time up to the next visible unit
|
|
||||||
let time_start = time_range.Start_us / (1000 * 1000);
|
|
||||||
let unit_time_start = Math.ceil(time_start / units[i]) * units[i];
|
|
||||||
|
|
||||||
// Calculate the canvas offset required to step to the first visible unit
|
|
||||||
let pre_step_x = time_range.PixelOffset(unit_time_start * (1000 * 1000));
|
|
||||||
|
|
||||||
// Draw lines for every unit at this level, keeping tracking of the seconds
|
|
||||||
var seconds = unit_time_start;
|
|
||||||
for (let x = pre_step_x; x <= timeline_end; x += sizeof_units[i])
|
|
||||||
{
|
|
||||||
// For the first two units, don't draw the units above it to prevent
|
|
||||||
// overdraw and the visual errors that causes
|
|
||||||
// The last unit always draws
|
|
||||||
if (i > 1 || (seconds % units[i + 1]))
|
|
||||||
{
|
|
||||||
// Only the first two units scale with unit visibility
|
|
||||||
// The last unit maintains its size
|
|
||||||
let height = Math.min(i * 4 + 4 * show_unit[i], 16);
|
|
||||||
|
|
||||||
// Draw the line on an integer boundary, shifted by 0.5 to get an un-anti-aliased 1px line
|
|
||||||
let ix = Math.floor(x);
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(ix + 0.5, 1);
|
|
||||||
ctx.lineTo(ix + 0.5, 1 + height);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
seconds += units[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 1)
|
|
||||||
{
|
|
||||||
// Draw text labels for the second unit, fading them out as they slowly
|
|
||||||
// become the first unit
|
|
||||||
ctx.globalAlpha = show_unit[0];
|
|
||||||
var seconds = unit_time_start;
|
|
||||||
for (let x = pre_step_x; x <= timeline_end; x += sizeof_units[i])
|
|
||||||
{
|
|
||||||
if (seconds % units[2])
|
|
||||||
{
|
|
||||||
this.DrawTimeText(seconds, x, 16);
|
|
||||||
}
|
|
||||||
seconds += units[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore alpha
|
|
||||||
ctx.globalAlpha = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (i == 2)
|
|
||||||
{
|
|
||||||
// Draw text labels for the third unit with no fade
|
|
||||||
var seconds = unit_time_start;
|
|
||||||
for (let x = pre_step_x; x <= timeline_end; x += sizeof_units[i])
|
|
||||||
{
|
|
||||||
this.DrawTimeText(seconds, x, 16);
|
|
||||||
seconds += units[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawTimeText(seconds, x, y)
|
|
||||||
{
|
|
||||||
// Use text measuring to centre the text horizontally on the input x
|
|
||||||
var text = GetTimeText(seconds);
|
|
||||||
var width = this.markerContext.measureText(text).width;
|
|
||||||
this.markerContext.fillText(text, Math.floor(x) - width / 2, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
Resize(x, y, w, h)
|
|
||||||
{
|
|
||||||
this.markerContainer.SetPosition(x, y);
|
|
||||||
this.markerContainer.SetSize(w, h);
|
|
||||||
|
|
||||||
// Match canvas size to container
|
|
||||||
this.markerCanvas.width = this.markerContainer.Node.clientWidth;
|
|
||||||
this.markerCanvas.height = this.markerContainer.Node.clientHeight;
|
|
||||||
}
|
|
||||||
}
|
|
400
profiler/vis/Code/TimelineRow.js
vendored
400
profiler/vis/Code/TimelineRow.js
vendored
|
@ -1,400 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
TimelineRow = (function()
|
|
||||||
{
|
|
||||||
const RowLabelTemplate = `
|
|
||||||
<div class='TimelineRow'>
|
|
||||||
<div class='TimelineRowCheck TimelineBox'>
|
|
||||||
<input class='TimelineRowCheckbox' type='checkbox' />
|
|
||||||
</div>
|
|
||||||
<div class='TimelineRowExpand TimelineBox NoSelect'>
|
|
||||||
<div class='TimelineRowExpandButton'>+</div>
|
|
||||||
</div>
|
|
||||||
<div class='TimelineRowExpand TimelineBox NoSelect'>
|
|
||||||
<div class='TimelineRowExpandButton'>-</div>
|
|
||||||
</div>
|
|
||||||
<div class='TimelineRowLabel TimelineBox'></div>
|
|
||||||
<div style="clear:left"></div>
|
|
||||||
</div>`
|
|
||||||
|
|
||||||
|
|
||||||
var SAMPLE_HEIGHT = 16;
|
|
||||||
var SAMPLE_BORDER = 2;
|
|
||||||
var SAMPLE_Y_SPACING = SAMPLE_HEIGHT + SAMPLE_BORDER * 2;
|
|
||||||
|
|
||||||
|
|
||||||
function TimelineRow(gl, name, timeline, frame_history, check_handler)
|
|
||||||
{
|
|
||||||
this.Name = name;
|
|
||||||
this.timeline = timeline;
|
|
||||||
|
|
||||||
// Create the row HTML and add to the parent
|
|
||||||
this.LabelContainerNode = DOM.Node.CreateHTML(RowLabelTemplate);
|
|
||||||
const label_node = DOM.Node.FindWithClass(this.LabelContainerNode, "TimelineRowLabel");
|
|
||||||
label_node.innerHTML = name;
|
|
||||||
timeline.TimelineLabels.Node.appendChild(this.LabelContainerNode);
|
|
||||||
|
|
||||||
// All sample view windows visible by default
|
|
||||||
const checkbox_node = DOM.Node.FindWithClass(this.LabelContainerNode, "TimelineRowCheckbox");
|
|
||||||
checkbox_node.checked = true;
|
|
||||||
checkbox_node.addEventListener("change", (e) => check_handler(name, e));
|
|
||||||
|
|
||||||
// Manually hook-up events to simulate div:active
|
|
||||||
// I can't get the equivalent CSS to work in Firefox, so...
|
|
||||||
const expand_node_0 = DOM.Node.FindWithClass(this.LabelContainerNode, "TimelineRowExpand", 0);
|
|
||||||
const expand_node_1 = DOM.Node.FindWithClass(this.LabelContainerNode, "TimelineRowExpand", 1);
|
|
||||||
const inc_node = DOM.Node.FindWithClass(expand_node_0, "TimelineRowExpandButton");
|
|
||||||
const dec_node = DOM.Node.FindWithClass(expand_node_1, "TimelineRowExpandButton");
|
|
||||||
inc_node.addEventListener("mousedown", ExpandButtonDown);
|
|
||||||
inc_node.addEventListener("mouseup", ExpandButtonUp);
|
|
||||||
inc_node.addEventListener("mouseleave", ExpandButtonUp);
|
|
||||||
dec_node.addEventListener("mousedown", ExpandButtonDown);
|
|
||||||
dec_node.addEventListener("mouseup", ExpandButtonUp);
|
|
||||||
dec_node.addEventListener("mouseleave", ExpandButtonUp);
|
|
||||||
|
|
||||||
// Pressing +/i increases/decreases depth
|
|
||||||
inc_node.addEventListener("click", () => this.IncDepth());
|
|
||||||
dec_node.addEventListener("click", () => this.DecDepth());
|
|
||||||
|
|
||||||
// Frame index to start at when looking for first visible sample
|
|
||||||
this.StartFrameIndex = 0;
|
|
||||||
|
|
||||||
this.FrameHistory = frame_history;
|
|
||||||
this.VisibleFrames = [ ];
|
|
||||||
this.VisibleTimeRange = null;
|
|
||||||
this.Depth = 1;
|
|
||||||
|
|
||||||
// Currently selected sample
|
|
||||||
this.SelectSampleInfo = null;
|
|
||||||
|
|
||||||
// Create WebGL sample buffers
|
|
||||||
this.sampleBuffer = new glDynamicBuffer(gl, glDynamicBufferType.Buffer, gl.FLOAT, 4);
|
|
||||||
this.colourBuffer = new glDynamicBuffer(gl, glDynamicBufferType.Buffer, gl.UNSIGNED_BYTE, 4);
|
|
||||||
|
|
||||||
// An initial SetSize call to restore containers to their original size after traces were loaded prior to this
|
|
||||||
this.SetSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineRow.prototype.SetSize = function()
|
|
||||||
{
|
|
||||||
this.LabelContainerNode.style.height = SAMPLE_Y_SPACING * this.Depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineRow.prototype.SetVisibleFrames = function(time_range)
|
|
||||||
{
|
|
||||||
// Clear previous visible list
|
|
||||||
this.VisibleFrames = [ ];
|
|
||||||
if (this.FrameHistory.length == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Store a copy of the visible time range rather than referencing it
|
|
||||||
// This prevents external modifications to the time range from affecting rendering/selection
|
|
||||||
time_range = time_range.Clone();
|
|
||||||
this.VisibleTimeRange = time_range;
|
|
||||||
|
|
||||||
// The frame history can be reset outside this class
|
|
||||||
// This also catches the overflow to the end of the frame list below when a thread stops sending samples
|
|
||||||
var max_frame = Math.max(this.FrameHistory.length - 1, 0);
|
|
||||||
var start_frame_index = Math.min(this.StartFrameIndex, max_frame);
|
|
||||||
|
|
||||||
// First do a back-track in case the time range moves negatively
|
|
||||||
while (start_frame_index > 0)
|
|
||||||
{
|
|
||||||
var frame = this.FrameHistory[start_frame_index];
|
|
||||||
if (time_range.Start_us > frame.StartTime_us)
|
|
||||||
break;
|
|
||||||
start_frame_index--;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then search from this point for the first visible frame
|
|
||||||
while (start_frame_index < this.FrameHistory.length)
|
|
||||||
{
|
|
||||||
var frame = this.FrameHistory[start_frame_index];
|
|
||||||
if (frame.EndTime_us > time_range.Start_us)
|
|
||||||
break;
|
|
||||||
start_frame_index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gather all frames up to the end point
|
|
||||||
this.StartFrameIndex = start_frame_index;
|
|
||||||
for (var i = start_frame_index; i < this.FrameHistory.length; i++)
|
|
||||||
{
|
|
||||||
var frame = this.FrameHistory[i];
|
|
||||||
if (frame.StartTime_us > time_range.End_us)
|
|
||||||
break;
|
|
||||||
this.VisibleFrames.push(frame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineRow.prototype.DrawSampleHighlight = function(gl_canvas, container, frame, offset, depth, selected)
|
|
||||||
{
|
|
||||||
if (depth <= this.Depth)
|
|
||||||
{
|
|
||||||
const gl = gl_canvas.gl;
|
|
||||||
const program = gl_canvas.timelineHighlightProgram;
|
|
||||||
|
|
||||||
gl_canvas.SetContainerUniforms(program, container);
|
|
||||||
|
|
||||||
// Set row parameters
|
|
||||||
const row_rect = this.LabelContainerNode.getBoundingClientRect();
|
|
||||||
glSetUniform(gl, program, "inRow.yOffset", row_rect.top);
|
|
||||||
|
|
||||||
// Set sample parameters
|
|
||||||
const float_offset = offset / 4;
|
|
||||||
glSetUniform(gl, program, "inStartMs", frame.sampleFloats[float_offset + g_sampleOffsetFloats_Start]);
|
|
||||||
glSetUniform(gl, program, "inLengthMs", frame.sampleFloats[float_offset + g_sampleOffsetFloats_Length]);
|
|
||||||
glSetUniform(gl, program, "inDepth", depth);
|
|
||||||
|
|
||||||
// Set colour
|
|
||||||
glSetUniform(gl, program, "inColourR", 1.0);
|
|
||||||
glSetUniform(gl, program, "inColourG", selected ? 0.0 : 1.0);
|
|
||||||
glSetUniform(gl, program, "inColourB", selected ? 0.0 : 1.0);
|
|
||||||
|
|
||||||
gl_canvas.EnableBlendPremulAlpha();
|
|
||||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
||||||
gl_canvas.DisableBlend();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineRow.prototype.DrawSampleGpuToCpu = function(gl_canvas, container, frame, offset, depth)
|
|
||||||
{
|
|
||||||
// Is this a GPU sample?
|
|
||||||
const float_offset = offset / 4;
|
|
||||||
const start_ms = frame.sampleFloats[float_offset + g_sampleOffsetFloats_GpuToCpu];
|
|
||||||
if (start_ms > 0)
|
|
||||||
{
|
|
||||||
const gl = gl_canvas.gl;
|
|
||||||
const program = gl_canvas.timelineGpuToCpuProgram;
|
|
||||||
|
|
||||||
gl_canvas.SetContainerUniforms(program, container);
|
|
||||||
|
|
||||||
// Set row parameters
|
|
||||||
const row_rect = this.LabelContainerNode.getBoundingClientRect();
|
|
||||||
glSetUniform(gl, program, "inRow.yOffset", row_rect.top);
|
|
||||||
|
|
||||||
// Set sample parameters
|
|
||||||
const length_ms = frame.sampleFloats[float_offset + g_sampleOffsetFloats_Start] - start_ms;
|
|
||||||
glSetUniform(gl, program, "inStartMs", start_ms);
|
|
||||||
glSetUniform(gl, program, "inLengthMs", length_ms);
|
|
||||||
glSetUniform(gl, program, "inDepth", depth);
|
|
||||||
|
|
||||||
gl_canvas.EnableBlendPremulAlpha();
|
|
||||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
||||||
gl_canvas.DisableBlend();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineRow.prototype.DisplayHeight = function()
|
|
||||||
{
|
|
||||||
return this.LabelContainerNode.clientHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineRow.prototype.YOffset = function()
|
|
||||||
{
|
|
||||||
return this.LabelContainerNode.offsetTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function GatherSamples(self, frame, samples_per_depth)
|
|
||||||
{
|
|
||||||
const sample_data_view = frame.sampleDataView;
|
|
||||||
|
|
||||||
for (let offset = 0; offset < sample_data_view.byteLength; offset += g_nbBytesPerSample)
|
|
||||||
{
|
|
||||||
depth = sample_data_view.getUint8(offset + g_sampleOffsetBytes_Depth) + 1;
|
|
||||||
if (depth > self.Depth)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure there's enough entries for each depth
|
|
||||||
while (depth >= samples_per_depth.length)
|
|
||||||
{
|
|
||||||
samples_per_depth.push([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let samples_this_depth = samples_per_depth[depth];
|
|
||||||
samples_this_depth.push([frame, offset]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineRow.prototype.Draw = function(gl_canvas, container)
|
|
||||||
{
|
|
||||||
let samples_per_depth = [];
|
|
||||||
|
|
||||||
// Gather all sample data in the visible frame set
|
|
||||||
for (var i in this.VisibleFrames)
|
|
||||||
{
|
|
||||||
var frame = this.VisibleFrames[i];
|
|
||||||
GatherSamples(this, frame, samples_per_depth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count number of samples required
|
|
||||||
let nb_samples = 0;
|
|
||||||
for (const samples_this_depth of samples_per_depth)
|
|
||||||
{
|
|
||||||
nb_samples += samples_this_depth.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resize buffers to match any new count of samples
|
|
||||||
const gl = gl_canvas.gl;
|
|
||||||
const program = gl_canvas.timelineProgram;
|
|
||||||
if (nb_samples > this.sampleBuffer.nbEntries)
|
|
||||||
{
|
|
||||||
this.sampleBuffer.ResizeToFitNextPow2(nb_samples);
|
|
||||||
this.colourBuffer.ResizeToFitNextPow2(nb_samples);
|
|
||||||
|
|
||||||
// Have to create a new VAO for these buffers
|
|
||||||
this.vertexArrayObject = gl.createVertexArray();
|
|
||||||
gl.bindVertexArray(this.vertexArrayObject);
|
|
||||||
this.sampleBuffer.BindAsInstanceAttribute(program, "inSample_TextOffset");
|
|
||||||
this.colourBuffer.BindAsInstanceAttribute(program, "inColour_TextLength");
|
|
||||||
}
|
|
||||||
|
|
||||||
// CPU write destination for samples
|
|
||||||
let cpu_samples = this.sampleBuffer.cpuArray;
|
|
||||||
let cpu_colours = this.colourBuffer.cpuArray;
|
|
||||||
let sample_pos = 0;
|
|
||||||
|
|
||||||
// TODO(don): Pack offsets into the sample buffer, instead?
|
|
||||||
// Puts all samples together into one growing buffer (will need ring buffer management).
|
|
||||||
// Offset points into that.
|
|
||||||
// Remains to be seen how much of this can be done given the limitations of WebGL2...
|
|
||||||
|
|
||||||
// Copy samples to the CPU buffer
|
|
||||||
// TODO(don): Use a ring buffer instead and take advantage of timeline scrolling adding new samples at the beginning/end
|
|
||||||
for (let depth = 0; depth < samples_per_depth.length; depth++)
|
|
||||||
{
|
|
||||||
let samples_this_depth = samples_per_depth[depth];
|
|
||||||
for (const [frame, offset] of samples_this_depth)
|
|
||||||
{
|
|
||||||
const float_offset = offset / 4;
|
|
||||||
|
|
||||||
cpu_samples[sample_pos + 0] = frame.sampleFloats[float_offset + g_sampleOffsetFloats_Start];
|
|
||||||
cpu_samples[sample_pos + 1] = frame.sampleFloats[float_offset + g_sampleOffsetFloats_Length];
|
|
||||||
cpu_samples[sample_pos + 2] = depth;
|
|
||||||
cpu_samples[sample_pos + 3] = frame.sampleFloats[float_offset + g_sampleOffsetFloats_NameOffset];
|
|
||||||
|
|
||||||
cpu_colours[sample_pos + 0] = frame.sampleDataView.getUint8(offset + g_sampleOffsetBytes_Colour + 0);
|
|
||||||
cpu_colours[sample_pos + 1] = frame.sampleDataView.getUint8(offset + g_sampleOffsetBytes_Colour + 1);
|
|
||||||
cpu_colours[sample_pos + 2] = frame.sampleDataView.getUint8(offset + g_sampleOffsetBytes_Colour + 2);
|
|
||||||
cpu_colours[sample_pos + 3] = frame.sampleFloats[float_offset + g_sampleOffsetFloats_NameLength];
|
|
||||||
|
|
||||||
sample_pos += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload to GPU
|
|
||||||
this.sampleBuffer.UploadData();
|
|
||||||
this.colourBuffer.UploadData();
|
|
||||||
|
|
||||||
gl_canvas.SetContainerUniforms(program, container);
|
|
||||||
|
|
||||||
// Set row parameters
|
|
||||||
const row_rect = this.LabelContainerNode.getBoundingClientRect();
|
|
||||||
glSetUniform(gl, program, "inRow.yOffset", row_rect.top);
|
|
||||||
|
|
||||||
gl.bindVertexArray(this.vertexArrayObject);
|
|
||||||
gl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, nb_samples);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineRow.prototype.SetSelectSample = function(sample_info)
|
|
||||||
{
|
|
||||||
this.SelectSampleInfo = sample_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function ExpandButtonDown(evt)
|
|
||||||
{
|
|
||||||
var node = DOM.Event.GetNode(evt);
|
|
||||||
DOM.Node.AddClass(node, "TimelineRowExpandButtonActive");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function ExpandButtonUp(evt)
|
|
||||||
{
|
|
||||||
var node = DOM.Event.GetNode(evt);
|
|
||||||
DOM.Node.RemoveClass(node, "TimelineRowExpandButtonActive");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineRow.prototype.IncDepth = function()
|
|
||||||
{
|
|
||||||
this.Depth++;
|
|
||||||
this.SetSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineRow.prototype.DecDepth = function()
|
|
||||||
{
|
|
||||||
if (this.Depth > 1)
|
|
||||||
{
|
|
||||||
this.Depth--;
|
|
||||||
this.SetSize();
|
|
||||||
|
|
||||||
// Trigger scroll handling to ensure reducing the depth reduces the display height
|
|
||||||
this.timeline.MoveVertically(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineRow.prototype.GetSampleAtPosition = function(time_us, mouse_y)
|
|
||||||
{
|
|
||||||
// Calculate depth of the mouse cursor
|
|
||||||
const depth = Math.min(Math.floor(mouse_y / SAMPLE_Y_SPACING) + 1, this.Depth);
|
|
||||||
|
|
||||||
// Search for the first frame to intersect this time
|
|
||||||
for (let i in this.VisibleFrames)
|
|
||||||
{
|
|
||||||
// Use the sample's closed interval to detect hits.
|
|
||||||
// Rendering of samples ensures a sample is never smaller than one pixel so that all samples always draw, irrespective
|
|
||||||
// of zoom level. If a half-open interval is used then some visible samples will be unselectable due to them being
|
|
||||||
// smaller than a pixel. This feels pretty odd and the closed interval fixes this feeling well.
|
|
||||||
// TODO(don): There are still inconsistencies, need to shift to pixel range checking to match exactly.
|
|
||||||
const frame = this.VisibleFrames[i];
|
|
||||||
if (time_us >= frame.StartTime_us && time_us <= frame.EndTime_us)
|
|
||||||
{
|
|
||||||
const found_sample = FindSample(this, frame, time_us, depth, 1);
|
|
||||||
if (found_sample != null)
|
|
||||||
{
|
|
||||||
return [ frame, found_sample[0], found_sample[1], this ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function FindSample(self, frame, time_us, target_depth, depth)
|
|
||||||
{
|
|
||||||
// Search entire frame of samples looking for a depth and time range that contains the input time
|
|
||||||
const sample_data_view = frame.sampleDataView;
|
|
||||||
for (let offset = 0; offset < sample_data_view.byteLength; offset += g_nbBytesPerSample)
|
|
||||||
{
|
|
||||||
depth = sample_data_view.getUint8(offset + g_sampleOffsetBytes_Depth) + 1;
|
|
||||||
if (depth == target_depth)
|
|
||||||
{
|
|
||||||
const us_start = sample_data_view.getFloat32(offset + g_sampleOffsetBytes_Start, true) * 1000.0;
|
|
||||||
const us_length = sample_data_view.getFloat32(offset + g_sampleOffsetBytes_Length, true) * 1000.0;
|
|
||||||
if (time_us >= us_start && time_us < us_start + us_length)
|
|
||||||
{
|
|
||||||
return [ offset, depth ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return TimelineRow;
|
|
||||||
})();
|
|
496
profiler/vis/Code/TimelineWindow.js
vendored
496
profiler/vis/Code/TimelineWindow.js
vendored
|
@ -1,496 +0,0 @@
|
||||||
|
|
||||||
// TODO(don): Separate all knowledge of threads from this timeline
|
|
||||||
|
|
||||||
TimelineWindow = (function()
|
|
||||||
{
|
|
||||||
var BORDER = 10;
|
|
||||||
|
|
||||||
function TimelineWindow(wm, name, settings, check_handler, gl_canvas)
|
|
||||||
{
|
|
||||||
this.Settings = settings;
|
|
||||||
this.glCanvas = gl_canvas;
|
|
||||||
|
|
||||||
// Create timeline window
|
|
||||||
this.Window = wm.AddWindow("Timeline", 10, 20, 100, 100, null, this);
|
|
||||||
this.Window.SetTitle(name);
|
|
||||||
this.Window.ShowNoAnim();
|
|
||||||
|
|
||||||
this.timelineMarkers = new TimelineMarkers(this);
|
|
||||||
|
|
||||||
// DO THESE need to be containers... can they just be divs?
|
|
||||||
// divs need a retrieval function
|
|
||||||
this.TimelineLabelScrollClipper = this.Window.AddControlNew(new WM.Container(10, 10, 10, 10));
|
|
||||||
DOM.Node.AddClass(this.TimelineLabelScrollClipper.Node, "TimelineLabelScrollClipper");
|
|
||||||
this.TimelineLabels = this.TimelineLabelScrollClipper.AddControlNew(new WM.Container(0, 0, 10, 10));
|
|
||||||
DOM.Node.AddClass(this.TimelineLabels.Node, "TimelineLabels");
|
|
||||||
|
|
||||||
// Ordered list of thread rows on the timeline
|
|
||||||
this.ThreadRows = [ ];
|
|
||||||
|
|
||||||
// Create timeline container
|
|
||||||
this.TimelineContainer = this.Window.AddControlNew(new WM.Container(10, 10, 800, 160));
|
|
||||||
DOM.Node.AddClass(this.TimelineContainer.Node, "TimelineContainer");
|
|
||||||
|
|
||||||
// Setup mouse interaction
|
|
||||||
this.mouseInteraction = new MouseInteraction(this.TimelineContainer.Node);
|
|
||||||
this.mouseInteraction.onClickHandler = (mouse_state) => OnMouseClick(this, mouse_state);
|
|
||||||
this.mouseInteraction.onMoveHandler = (mouse_state, mx, my) => OnMouseMove(this, mouse_state, mx, my);
|
|
||||||
this.mouseInteraction.onHoverHandler = (mouse_state) => OnMouseHover(this, mouse_state);
|
|
||||||
this.mouseInteraction.onScrollHandler = (mouse_state) => OnMouseScroll(this, mouse_state);
|
|
||||||
|
|
||||||
// Allow user to click on the thread name to deselect any threads as finding empty space may be difficult
|
|
||||||
DOM.Event.AddHandler(this.TimelineLabels.Node, "mousedown", (evt) => OnLabelMouseDown(this, evt));
|
|
||||||
|
|
||||||
this.Window.SetOnResize(Bind(OnUserResize, this));
|
|
||||||
|
|
||||||
this.Clear();
|
|
||||||
|
|
||||||
this.OnHoverHandler = null;
|
|
||||||
this.OnSelectedHandler = null;
|
|
||||||
this.OnMovedHandler = null;
|
|
||||||
this.CheckHandler = check_handler;
|
|
||||||
|
|
||||||
this.yScrollOffset = 0;
|
|
||||||
|
|
||||||
this.HoverSampleInfo = null;
|
|
||||||
this.lastHoverThreadName = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineWindow.prototype.Clear = function()
|
|
||||||
{
|
|
||||||
// Clear out labels
|
|
||||||
this.TimelineLabels.ClearControls();
|
|
||||||
|
|
||||||
this.ThreadRows = [ ];
|
|
||||||
this.TimeRange = new PixelTimeRange(0, 200 * 1000, this.TimelineContainer.Node.clientWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineWindow.prototype.SetOnHover = function(handler)
|
|
||||||
{
|
|
||||||
this.OnHoverHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineWindow.prototype.SetOnSelected = function(handler)
|
|
||||||
{
|
|
||||||
this.OnSelectedHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineWindow.prototype.SetOnMoved = function(handler)
|
|
||||||
{
|
|
||||||
this.OnMovedHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineWindow.prototype.WindowResized = function(x, width, top_window)
|
|
||||||
{
|
|
||||||
// Resize window
|
|
||||||
var top = top_window.Position[1] + top_window.Size[1] + 10;
|
|
||||||
this.Window.SetPosition(x, top);
|
|
||||||
this.Window.SetSize(width - 2 * 10, 260);
|
|
||||||
|
|
||||||
ResizeInternals(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineWindow.prototype.OnSamples = function(thread_name, frame_history)
|
|
||||||
{
|
|
||||||
// Shift the timeline to the last entry on this thread
|
|
||||||
var last_frame = frame_history[frame_history.length - 1];
|
|
||||||
this.TimeRange.SetEnd(last_frame.EndTime_us);
|
|
||||||
|
|
||||||
// Search for the index of this thread
|
|
||||||
var thread_index = -1;
|
|
||||||
for (var i in this.ThreadRows)
|
|
||||||
{
|
|
||||||
if (this.ThreadRows[i].Name == thread_name)
|
|
||||||
{
|
|
||||||
thread_index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this thread has not been seen before, add a new row to the list
|
|
||||||
if (thread_index == -1)
|
|
||||||
{
|
|
||||||
var row = new TimelineRow(this.glCanvas.gl, thread_name, this, frame_history, this.CheckHandler);
|
|
||||||
this.ThreadRows.push(row);
|
|
||||||
|
|
||||||
// Sort thread rows in the collection by name
|
|
||||||
this.ThreadRows.sort((a, b) => a.Name.localeCompare(b.Name));
|
|
||||||
|
|
||||||
// Resort the view by removing timeline row nodes from their DOM parents and re-adding
|
|
||||||
const thread_rows = new Array();
|
|
||||||
for (let thread_row of this.ThreadRows)
|
|
||||||
{
|
|
||||||
this.TimelineLabels.Node.removeChild(thread_row.LabelContainerNode);
|
|
||||||
thread_rows.push(thread_row);
|
|
||||||
}
|
|
||||||
for (let thread_row of thread_rows)
|
|
||||||
{
|
|
||||||
this.TimelineLabels.Node.appendChild(thread_row.LabelContainerNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineWindow.prototype.DrawBackground = function()
|
|
||||||
{
|
|
||||||
const gl = this.glCanvas.gl;
|
|
||||||
const program = this.glCanvas.timelineBackgroundProgram;
|
|
||||||
gl.useProgram(program);
|
|
||||||
|
|
||||||
// Set viewport parameters
|
|
||||||
glSetUniform(gl, program, "inViewport.width", gl.canvas.width);
|
|
||||||
glSetUniform(gl, program, "inViewport.height", gl.canvas.height);
|
|
||||||
|
|
||||||
this.glCanvas.SetContainerUniforms(program, this.TimelineContainer.Node);
|
|
||||||
|
|
||||||
// Set row parameters
|
|
||||||
const row_rect = this.TimelineLabels.Node.getBoundingClientRect();
|
|
||||||
glSetUniform(gl, program, "inYOffset", row_rect.top);
|
|
||||||
|
|
||||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
||||||
|
|
||||||
this.timelineMarkers.Draw(this.TimeRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineWindow.prototype.Deselect = function(thread_name)
|
|
||||||
{
|
|
||||||
for (let thread_row of this.ThreadRows)
|
|
||||||
{
|
|
||||||
if (thread_name == thread_row.Name)
|
|
||||||
{
|
|
||||||
thread_row.SelectSampleInfo = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineWindow.prototype.DrawSampleHighlights = function()
|
|
||||||
{
|
|
||||||
const gl = this.glCanvas.gl;
|
|
||||||
const program = this.glCanvas.timelineHighlightProgram;
|
|
||||||
gl.useProgram(program);
|
|
||||||
|
|
||||||
// Set viewport parameters
|
|
||||||
glSetUniform(gl, program, "inViewport.width", gl.canvas.width);
|
|
||||||
glSetUniform(gl, program, "inViewport.height", gl.canvas.height);
|
|
||||||
|
|
||||||
// Set time range parameters
|
|
||||||
const time_range = this.TimeRange;
|
|
||||||
time_range.SetAsUniform(gl, program);
|
|
||||||
|
|
||||||
for (let thread_row of this.ThreadRows)
|
|
||||||
{
|
|
||||||
// Draw highlight for hover row
|
|
||||||
if (this.HoverSampleInfo != null && this.HoverSampleInfo[3] == thread_row)
|
|
||||||
{
|
|
||||||
const frame = this.HoverSampleInfo[0];
|
|
||||||
const offset = this.HoverSampleInfo[1];
|
|
||||||
const depth = this.HoverSampleInfo[2];
|
|
||||||
thread_row.DrawSampleHighlight(this.glCanvas, this.TimelineContainer.Node, frame, offset, depth, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw highlight for any samples selected on this row
|
|
||||||
if (thread_row.SelectSampleInfo != null)
|
|
||||||
{
|
|
||||||
const frame = thread_row.SelectSampleInfo[0];
|
|
||||||
const offset = thread_row.SelectSampleInfo[1];
|
|
||||||
const depth = thread_row.SelectSampleInfo[2];
|
|
||||||
thread_row.DrawSampleHighlight(this.glCanvas, this.TimelineContainer.Node, frame, offset, depth, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineWindow.prototype.DrawSampleGpuToCpu = function()
|
|
||||||
{
|
|
||||||
const gl = this.glCanvas.gl;
|
|
||||||
const program = this.glCanvas.timelineGpuToCpuProgram;
|
|
||||||
gl.useProgram(program);
|
|
||||||
|
|
||||||
// Set viewport parameters
|
|
||||||
glSetUniform(gl, program, "inViewport.width", gl.canvas.width);
|
|
||||||
glSetUniform(gl, program, "inViewport.height", gl.canvas.height);
|
|
||||||
|
|
||||||
// Set time range parameters
|
|
||||||
const time_range = this.TimeRange;
|
|
||||||
time_range.SetAsUniform(gl, program);
|
|
||||||
|
|
||||||
// Draw pointer for hover rows
|
|
||||||
for (let thread_row of this.ThreadRows)
|
|
||||||
{
|
|
||||||
if (this.HoverSampleInfo != null && this.HoverSampleInfo[3] == thread_row)
|
|
||||||
{
|
|
||||||
const frame = this.HoverSampleInfo[0];
|
|
||||||
const offset = this.HoverSampleInfo[1];
|
|
||||||
const depth = this.HoverSampleInfo[2];
|
|
||||||
thread_row.DrawSampleGpuToCpu(this.glCanvas, this.TimelineContainer.Node, frame, offset, depth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineWindow.prototype.Draw = function()
|
|
||||||
{
|
|
||||||
this.DrawBackground();
|
|
||||||
|
|
||||||
const gl = this.glCanvas.gl;
|
|
||||||
const program = this.glCanvas.timelineProgram;
|
|
||||||
gl.useProgram(program);
|
|
||||||
|
|
||||||
// Set viewport parameters
|
|
||||||
glSetUniform(gl, program, "inViewport.width", gl.canvas.width);
|
|
||||||
glSetUniform(gl, program, "inViewport.height", gl.canvas.height);
|
|
||||||
|
|
||||||
// Set time range parameters
|
|
||||||
const time_range = this.TimeRange;
|
|
||||||
time_range.SetAsUniform(gl, program);
|
|
||||||
|
|
||||||
this.glCanvas.SetTextUniforms(program);
|
|
||||||
|
|
||||||
for (let i in this.ThreadRows)
|
|
||||||
{
|
|
||||||
var thread_row = this.ThreadRows[i];
|
|
||||||
thread_row.SetVisibleFrames(time_range);
|
|
||||||
thread_row.Draw(this.glCanvas, this.TimelineContainer.Node);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.DrawSampleHighlights();
|
|
||||||
this.DrawSampleGpuToCpu();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnUserResize(self, evt)
|
|
||||||
{
|
|
||||||
ResizeInternals(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ResizeInternals(self)
|
|
||||||
{
|
|
||||||
// .TimelineRowLabel
|
|
||||||
// .TimelineRowExpand
|
|
||||||
// .TimelineRowExpand
|
|
||||||
// .TimelineRowCheck
|
|
||||||
// Window padding
|
|
||||||
let offset_x = 145+19+19+19+10;
|
|
||||||
|
|
||||||
let MarkersHeight = 18;
|
|
||||||
|
|
||||||
var parent_size = self.Window.Size;
|
|
||||||
|
|
||||||
self.timelineMarkers.Resize(BORDER + offset_x, 10, parent_size[0] - 2* BORDER - offset_x, MarkersHeight);
|
|
||||||
|
|
||||||
// Resize controls
|
|
||||||
self.TimelineContainer.SetPosition(BORDER + offset_x, 10 + MarkersHeight);
|
|
||||||
self.TimelineContainer.SetSize(parent_size[0] - 2 * BORDER - offset_x, parent_size[1] - MarkersHeight - 40);
|
|
||||||
|
|
||||||
self.TimelineLabelScrollClipper.SetPosition(10, 10 + MarkersHeight);
|
|
||||||
self.TimelineLabelScrollClipper.SetSize(offset_x, parent_size[1] - MarkersHeight - 40);
|
|
||||||
self.TimelineLabels.SetSize(offset_x, parent_size[1] - MarkersHeight - 40);
|
|
||||||
|
|
||||||
// Adjust time range to new width
|
|
||||||
const width = self.TimelineContainer.Node.clientWidth;
|
|
||||||
self.TimeRange.SetPixelSpan(width);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMouseScroll(self, mouse_state)
|
|
||||||
{
|
|
||||||
let scale = 1.11;
|
|
||||||
if (mouse_state.WheelDelta > 0)
|
|
||||||
scale = 1 / scale;
|
|
||||||
|
|
||||||
// What time is the mouse hovering over?
|
|
||||||
let mouse_pos = self.TimelineMousePosition(mouse_state);
|
|
||||||
let time_us = self.TimeRange.TimeAtPosition(mouse_pos[0]);
|
|
||||||
|
|
||||||
// Calculate start time relative to the mouse hover position
|
|
||||||
var time_start_us = self.TimeRange.Start_us - time_us;
|
|
||||||
|
|
||||||
// Scale and offset back to the hover time
|
|
||||||
self.TimeRange.Set(time_start_us * scale + time_us, self.TimeRange.Span_us * scale);
|
|
||||||
|
|
||||||
if (self.OnMovedHandler)
|
|
||||||
{
|
|
||||||
self.OnMovedHandler(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineWindow.prototype.SetTimeRange = function(start_us, span_us)
|
|
||||||
{
|
|
||||||
this.TimeRange.Set(start_us, span_us);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineWindow.prototype.DisplayHeight = function()
|
|
||||||
{
|
|
||||||
// Sum height of each thread row
|
|
||||||
let height = 0;
|
|
||||||
for (thread_row of this.ThreadRows)
|
|
||||||
{
|
|
||||||
height += thread_row.DisplayHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineWindow.prototype.MoveVertically = function(y_scroll)
|
|
||||||
{
|
|
||||||
// Calculate the minimum negative value the position of the labels can be to account for scrolling to the bottom
|
|
||||||
// of the label/depth list
|
|
||||||
let display_height = this.DisplayHeight();
|
|
||||||
let container_height = this.TimelineLabelScrollClipper.Node.clientHeight;
|
|
||||||
let minimum_y = Math.min(container_height - display_height, 0.0);
|
|
||||||
|
|
||||||
// Resize the label container to match the display height
|
|
||||||
this.TimelineLabels.Node.style.height = Math.max(display_height, container_height);
|
|
||||||
|
|
||||||
// Increment the y-scroll using just-calculated limits
|
|
||||||
let old_y_scroll_offset = this.yScrollOffset;
|
|
||||||
this.yScrollOffset = Math.min(Math.max(this.yScrollOffset + y_scroll, minimum_y), 0);
|
|
||||||
|
|
||||||
// Calculate how much the labels should actually scroll after limiting and apply
|
|
||||||
let y_scroll_px = this.yScrollOffset - old_y_scroll_offset;
|
|
||||||
this.TimelineLabels.Node.style.top = this.TimelineLabels.Node.offsetTop + y_scroll_px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineWindow.prototype.TimelineMousePosition = function(mouse_state)
|
|
||||||
{
|
|
||||||
// Position of the mouse relative to the timeline container
|
|
||||||
let node_offset = DOM.Node.GetPosition(this.TimelineContainer.Node);
|
|
||||||
let mouse_x = mouse_state.Position[0] - node_offset[0];
|
|
||||||
let mouse_y = mouse_state.Position[1] - node_offset[1];
|
|
||||||
|
|
||||||
// Offset by the amount of scroll
|
|
||||||
mouse_y -= this.yScrollOffset;
|
|
||||||
|
|
||||||
return [ mouse_x, mouse_y ];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TimelineWindow.prototype.GetHoverThreadRow = function(mouse_pos)
|
|
||||||
{
|
|
||||||
// Search for the thread row the mouse intersects
|
|
||||||
let height = 0;
|
|
||||||
for (let thread_row of this.ThreadRows)
|
|
||||||
{
|
|
||||||
let row_height = thread_row.DisplayHeight();
|
|
||||||
if (mouse_pos[1] >= height && mouse_pos[1] < height + row_height)
|
|
||||||
{
|
|
||||||
// Mouse y relative to row start
|
|
||||||
mouse_pos[1] -= height;
|
|
||||||
return thread_row;
|
|
||||||
}
|
|
||||||
height += row_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMouseClick(self, mouse_state)
|
|
||||||
{
|
|
||||||
// Are we hovering over a thread row?
|
|
||||||
const mouse_pos = self.TimelineMousePosition(mouse_state);
|
|
||||||
const hover_thread_row = self.GetHoverThreadRow(mouse_pos);
|
|
||||||
if (hover_thread_row != null)
|
|
||||||
{
|
|
||||||
// Are we hovering over a sample?
|
|
||||||
const time_us = self.TimeRange.TimeAtPosition(mouse_pos[0]);
|
|
||||||
const sample_info = hover_thread_row.GetSampleAtPosition(time_us, mouse_pos[1]);
|
|
||||||
if (sample_info != null)
|
|
||||||
{
|
|
||||||
// Toggle deselect if this sample is already selected
|
|
||||||
if (hover_thread_row.SelectSampleInfo != null &&
|
|
||||||
sample_info[0] == hover_thread_row.SelectSampleInfo[0] && sample_info[1] == hover_thread_row.SelectSampleInfo[1] &&
|
|
||||||
sample_info[2] == hover_thread_row.SelectSampleInfo[2] && sample_info[3] == hover_thread_row.SelectSampleInfo[3])
|
|
||||||
{
|
|
||||||
hover_thread_row.SetSelectSample(null);
|
|
||||||
self.OnSelectedHandler?.(hover_thread_row.Name, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise select
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hover_thread_row.SetSelectSample(sample_info);
|
|
||||||
self.OnSelectedHandler?.(hover_thread_row.Name, sample_info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deselect if not hovering over a sample
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.OnSelectedHandler?.(hover_thread_row.Name, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnLabelMouseDown(self, evt)
|
|
||||||
{
|
|
||||||
// Deselect sample on this thread
|
|
||||||
const mouse_state = new Mouse.State(evt);
|
|
||||||
let mouse_pos = self.TimelineMousePosition(mouse_state);
|
|
||||||
const thread_row = self.GetHoverThreadRow(mouse_pos);
|
|
||||||
self.OnSelectedHandler?.(thread_row.Name, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMouseMove(self, mouse_state, move_offset_x, move_offset_y)
|
|
||||||
{
|
|
||||||
// Shift the visible time range with mouse movement
|
|
||||||
const time_offset_us = move_offset_x / self.TimeRange.usPerPixel;
|
|
||||||
self.TimeRange.SetStart(self.TimeRange.Start_us - time_offset_us);
|
|
||||||
|
|
||||||
// Control vertical movement
|
|
||||||
self.MoveVertically(move_offset_y);
|
|
||||||
|
|
||||||
// Notify
|
|
||||||
self.OnMovedHandler?.(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMouseHover(self, mouse_state)
|
|
||||||
{
|
|
||||||
// Check for hover ending
|
|
||||||
if (mouse_state == null)
|
|
||||||
{
|
|
||||||
self.OnHoverHandler?.(self.lastHoverThreadName, null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Are we hovering over a thread row?
|
|
||||||
const mouse_pos = self.TimelineMousePosition(mouse_state);
|
|
||||||
const hover_thread_row = self.GetHoverThreadRow(mouse_pos);
|
|
||||||
if (hover_thread_row != null)
|
|
||||||
{
|
|
||||||
// Are we hovering over a sample?
|
|
||||||
const time_us = self.TimeRange.TimeAtPosition(mouse_pos[0]);
|
|
||||||
self.HoverSampleInfo = hover_thread_row.GetSampleAtPosition(time_us, mouse_pos[1]);
|
|
||||||
|
|
||||||
// Exit hover for the last hover row
|
|
||||||
self.OnHoverHandler?.(self.lastHoverThreadName, null);
|
|
||||||
self.lastHoverThreadName = hover_thread_row.Name;
|
|
||||||
|
|
||||||
// Tell listeners which sample we're hovering over
|
|
||||||
self.OnHoverHandler?.(hover_thread_row.Name, self.HoverSampleInfo);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.HoverSampleInfo = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return TimelineWindow;
|
|
||||||
})();
|
|
||||||
|
|
105
profiler/vis/Code/TitleWindow.js
vendored
105
profiler/vis/Code/TitleWindow.js
vendored
|
@ -1,105 +0,0 @@
|
||||||
|
|
||||||
TitleWindow = (function()
|
|
||||||
{
|
|
||||||
function TitleWindow(wm, settings, server, connection_address)
|
|
||||||
{
|
|
||||||
this.Settings = settings;
|
|
||||||
|
|
||||||
this.Window = wm.AddWindow(" Remotery", 10, 10, 100, 100);
|
|
||||||
this.Window.ShowNoAnim();
|
|
||||||
|
|
||||||
this.PingContainer = this.Window.AddControlNew(new WM.Container(4, -13, 10, 10));
|
|
||||||
DOM.Node.AddClass(this.PingContainer.Node, "PingContainer");
|
|
||||||
|
|
||||||
this.EditBox = this.Window.AddControlNew(new WM.EditBox(10, 5, 300, 18, "Connection Address", connection_address));
|
|
||||||
|
|
||||||
// Setup pause button
|
|
||||||
this.PauseButton = this.Window.AddControlNew(new WM.Button("Pause", 5, 5, { toggle: true }));
|
|
||||||
this.PauseButton.SetOnClick(Bind(OnPausePressed, this));
|
|
||||||
|
|
||||||
this.SyncButton = this.Window.AddControlNew(new WM.Button("Sync Timelines", 5, 5, { toggle: true}));
|
|
||||||
this.SyncButton.SetOnClick(Bind(OnSyncPressed, this));
|
|
||||||
this.SyncButton.SetState(this.Settings.SyncTimelines);
|
|
||||||
|
|
||||||
server.AddMessageHandler("PING", Bind(OnPing, this));
|
|
||||||
|
|
||||||
this.Window.SetOnResize(Bind(OnUserResize, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TitleWindow.prototype.SetConnectionAddressChanged = function(handler)
|
|
||||||
{
|
|
||||||
this.EditBox.SetChangeHandler(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TitleWindow.prototype.WindowResized = function(width, height)
|
|
||||||
{
|
|
||||||
this.Window.SetSize(width - 2 * 10, 50);
|
|
||||||
ResizeInternals(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
TitleWindow.prototype.Pause = function()
|
|
||||||
{
|
|
||||||
if (!this.Settings.IsPaused)
|
|
||||||
{
|
|
||||||
this.PauseButton.SetText("Paused");
|
|
||||||
this.PauseButton.SetState(true);
|
|
||||||
this.Settings.IsPaused = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TitleWindow.prototype.Unpause = function()
|
|
||||||
{
|
|
||||||
if (this.Settings.IsPaused)
|
|
||||||
{
|
|
||||||
this.PauseButton.SetText("Pause");
|
|
||||||
this.PauseButton.SetState(false);
|
|
||||||
this.Settings.IsPaused = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function OnUserResize(self, evt)
|
|
||||||
{
|
|
||||||
ResizeInternals(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ResizeInternals(self)
|
|
||||||
{
|
|
||||||
self.PauseButton.SetPosition(self.Window.Size[0] - 60, 5);
|
|
||||||
self.SyncButton.SetPosition(self.Window.Size[0] - 155, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnPausePressed(self)
|
|
||||||
{
|
|
||||||
if (self.PauseButton.IsPressed())
|
|
||||||
{
|
|
||||||
self.Pause();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.Unpause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnSyncPressed(self)
|
|
||||||
{
|
|
||||||
self.Settings.SyncTimelines = self.SyncButton.IsPressed();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnPing(self, server)
|
|
||||||
{
|
|
||||||
// Set the ping container as active and take it off half a second later
|
|
||||||
DOM.Node.AddClass(self.PingContainer.Node, "PingContainerActive");
|
|
||||||
window.setTimeout(Bind(function(self)
|
|
||||||
{
|
|
||||||
DOM.Node.RemoveClass(self.PingContainer.Node, "PingContainerActive");
|
|
||||||
}, self), 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return TitleWindow;
|
|
||||||
})();
|
|
147
profiler/vis/Code/TraceDrop.js
vendored
147
profiler/vis/Code/TraceDrop.js
vendored
|
@ -1,147 +0,0 @@
|
||||||
|
|
||||||
class TraceDrop
|
|
||||||
{
|
|
||||||
constructor(remotery)
|
|
||||||
{
|
|
||||||
this.Remotery = remotery;
|
|
||||||
|
|
||||||
// Create a full-page overlay div for dropping files onto
|
|
||||||
this.DropNode = DOM.Node.CreateHTML("<div id='DropZone' class='DropZone'>Load Remotery Trace</div>");
|
|
||||||
document.body.appendChild(this.DropNode);
|
|
||||||
|
|
||||||
// Attach drop handlers
|
|
||||||
window.addEventListener("dragenter", () => this.ShowDropZone());
|
|
||||||
this.DropNode.addEventListener("dragenter", (e) => this.AllowDrag(e));
|
|
||||||
this.DropNode.addEventListener("dragover", (e) => this.AllowDrag(e));
|
|
||||||
this.DropNode.addEventListener("dragleave", () => this.HideDropZone());
|
|
||||||
this.DropNode.addEventListener("drop", (e) => this.OnDrop(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
ShowDropZone()
|
|
||||||
{
|
|
||||||
this.DropNode.style.display = "flex";
|
|
||||||
}
|
|
||||||
|
|
||||||
HideDropZone()
|
|
||||||
{
|
|
||||||
this.DropNode.style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
AllowDrag(evt)
|
|
||||||
{
|
|
||||||
// Prevent the default drag handler kicking in
|
|
||||||
evt.preventDefault();
|
|
||||||
|
|
||||||
evt.dataTransfer.dropEffect = "copy";
|
|
||||||
}
|
|
||||||
|
|
||||||
OnDrop(evt)
|
|
||||||
{
|
|
||||||
// Prevent the default drop handler kicking in
|
|
||||||
evt.preventDefault();
|
|
||||||
|
|
||||||
this.HideDropZone(evt);
|
|
||||||
|
|
||||||
// Get the file that was dropped
|
|
||||||
let files = DOM.Event.GetDropFiles(evt);
|
|
||||||
if (files.length == 0)
|
|
||||||
{
|
|
||||||
alert("No files dropped");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (files.length > 1)
|
|
||||||
{
|
|
||||||
alert("Too many files dropped");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check file type
|
|
||||||
let file = files[0];
|
|
||||||
if (!file.name.endsWith(".rbin"))
|
|
||||||
{
|
|
||||||
alert("Not the correct .rbin file type");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Background-load the file
|
|
||||||
var remotery = this.Remotery;
|
|
||||||
let file_reader = new FileReader();
|
|
||||||
file_reader.onload = function()
|
|
||||||
{
|
|
||||||
// Create the data reader and verify the header
|
|
||||||
let data_view = new DataView(this.result);
|
|
||||||
let data_view_reader = new DataViewReader(data_view, 0);
|
|
||||||
let header = data_view_reader.GetStringOfLength(8);
|
|
||||||
if (header != "RMTBLOGF")
|
|
||||||
{
|
|
||||||
alert("Not a valid Remotery Log File");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
remotery.Clear();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Forward all recorded events to message handlers
|
|
||||||
while (!data_view_reader.AtEnd())
|
|
||||||
{
|
|
||||||
const start_offset = data_view_reader.Offset;
|
|
||||||
const [id, length ] = remotery.Server.CallMessageHandlers(data_view_reader, this.Result);
|
|
||||||
data_view_reader.Offset = start_offset + length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
// The last message may be partially written due to process exit
|
|
||||||
// Catch this safely as it's a valid state for the file to be in
|
|
||||||
if (e instanceof RangeError)
|
|
||||||
{
|
|
||||||
console.log("Aborted reading last message");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// After loading completes, populate the UI which wasn't updated during loading
|
|
||||||
|
|
||||||
remotery.Console.TriggerUpdate();
|
|
||||||
|
|
||||||
// Set frame history for each timeline thread
|
|
||||||
for (let name in remotery.FrameHistory)
|
|
||||||
{
|
|
||||||
let frame_history = remotery.FrameHistory[name];
|
|
||||||
remotery.SampleTimelineWindow.OnSamples(name, frame_history);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set frame history for each processor
|
|
||||||
for (let name in remotery.ProcessorFrameHistory)
|
|
||||||
{
|
|
||||||
let frame_history = remotery.ProcessorFrameHistory[name];
|
|
||||||
remotery.ProcessorTimelineWindow.OnSamples(name, frame_history);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the last frame values for each grid window
|
|
||||||
for (let name in remotery.gridWindows)
|
|
||||||
{
|
|
||||||
const grid_window = remotery.gridWindows[name];
|
|
||||||
|
|
||||||
const frame_history = remotery.FrameHistory[name];
|
|
||||||
if (frame_history)
|
|
||||||
{
|
|
||||||
// This is a sample window
|
|
||||||
const frame = frame_history[frame_history.length - 1];
|
|
||||||
grid_window.UpdateEntries(frame.NbSamples, frame.sampleFloats);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// This is a property window
|
|
||||||
const frame_history = remotery.PropertyFrameHistory;
|
|
||||||
const frame = frame_history[frame_history.length - 1];
|
|
||||||
grid_window.UpdateEntries(frame.nbSnapshots, frame.snapshotFloats);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pause for viewing
|
|
||||||
remotery.TitleWindow.Pause();
|
|
||||||
};
|
|
||||||
file_reader.readAsArrayBuffer(file);
|
|
||||||
}
|
|
||||||
}
|
|
252
profiler/vis/Code/WebGL.js
vendored
252
profiler/vis/Code/WebGL.js
vendored
|
@ -1,252 +0,0 @@
|
||||||
|
|
||||||
function assert(condition, message)
|
|
||||||
{
|
|
||||||
if (!condition)
|
|
||||||
{
|
|
||||||
throw new Error(message || "Assertion failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function glCompileShader(gl, type, name, source)
|
|
||||||
{
|
|
||||||
console.log("Compiling " + name);
|
|
||||||
|
|
||||||
// Compile the shader
|
|
||||||
let shader = gl.createShader(type);
|
|
||||||
gl.shaderSource(shader, source);
|
|
||||||
gl.compileShader(shader);
|
|
||||||
|
|
||||||
// Report any errors
|
|
||||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
|
|
||||||
{
|
|
||||||
console.log("Error compiling " + name);
|
|
||||||
console.log(gl.getShaderInfoLog(shader));
|
|
||||||
console.trace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
function glCreateProgram(gl, vshader, fshader)
|
|
||||||
{
|
|
||||||
// Attach shaders and link
|
|
||||||
let program = gl.createProgram();
|
|
||||||
gl.attachShader(program, vshader);
|
|
||||||
gl.attachShader(program, fshader);
|
|
||||||
gl.linkProgram(program);
|
|
||||||
|
|
||||||
// Report any errors
|
|
||||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
|
|
||||||
{
|
|
||||||
console.log("Failed to link program");
|
|
||||||
console.trace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return program;
|
|
||||||
}
|
|
||||||
|
|
||||||
function glCreateProgramFromSource(gl, vshader_name, vshader_source, fshader_name, fshader_source)
|
|
||||||
{
|
|
||||||
const vshader = glCompileShader(gl, gl.VERTEX_SHADER, vshader_name, vshader_source);
|
|
||||||
const fshader = glCompileShader(gl, gl.FRAGMENT_SHADER, fshader_name, fshader_source);
|
|
||||||
return glCreateProgram(gl, vshader, fshader);
|
|
||||||
}
|
|
||||||
|
|
||||||
function glSetUniform(gl, program, name, value, index)
|
|
||||||
{
|
|
||||||
// Get location
|
|
||||||
const location = gl.getUniformLocation(program, name);
|
|
||||||
assert(location != null, "Can't find uniform " + name);
|
|
||||||
|
|
||||||
// Dispatch to uniform function by type
|
|
||||||
assert(value != null, "Value is null");
|
|
||||||
const type = Object.prototype.toString.call(value).slice(8, -1);
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case "Number":
|
|
||||||
gl.uniform1f(location, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "WebGLTexture":
|
|
||||||
gl.activeTexture(gl.TEXTURE0 + index);
|
|
||||||
gl.bindTexture(gl.TEXTURE_2D, value);
|
|
||||||
gl.uniform1i(location, index);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(false, "Unhandled type " + type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function glCreateTexture(gl, width, height, data)
|
|
||||||
{
|
|
||||||
const texture = gl.createTexture();
|
|
||||||
|
|
||||||
// Set filtering/wrapping to nearest/clamp
|
|
||||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
||||||
|
|
||||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
|
|
||||||
|
|
||||||
return texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
const glDynamicBufferType = Object.freeze({
|
|
||||||
Buffer: 1,
|
|
||||||
Texture: 2
|
|
||||||
});
|
|
||||||
|
|
||||||
class glDynamicBuffer
|
|
||||||
{
|
|
||||||
constructor(gl, buffer_type, element_type, nb_elements, nb_entries = 1)
|
|
||||||
{
|
|
||||||
this.gl = gl;
|
|
||||||
this.elementType = element_type;
|
|
||||||
this.nbElements = nb_elements;
|
|
||||||
this.bufferType = buffer_type;
|
|
||||||
this.dirty = false;
|
|
||||||
|
|
||||||
this.Resize(nb_entries);
|
|
||||||
}
|
|
||||||
|
|
||||||
BindAsInstanceAttribute(program, attrib_name)
|
|
||||||
{
|
|
||||||
assert(this.bufferType == glDynamicBufferType.Buffer, "Can only call BindAsInstanceAttribute with Buffer types");
|
|
||||||
|
|
||||||
let gl = this.gl;
|
|
||||||
|
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
|
|
||||||
|
|
||||||
// The attribute referenced in the program
|
|
||||||
const attrib_location = gl.getAttribLocation(program, attrib_name);
|
|
||||||
|
|
||||||
gl.enableVertexAttribArray(attrib_location);
|
|
||||||
gl.vertexAttribPointer(attrib_location, this.nbElements, this.elementType, false, 0, 0);
|
|
||||||
|
|
||||||
// One per instance
|
|
||||||
gl.vertexAttribDivisor(attrib_location, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
UploadData()
|
|
||||||
{
|
|
||||||
let gl = this.gl;
|
|
||||||
|
|
||||||
switch (this.bufferType)
|
|
||||||
{
|
|
||||||
case glDynamicBufferType.Buffer:
|
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
|
|
||||||
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.cpuArray);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case glDynamicBufferType.Texture:
|
|
||||||
assert(this.elementType == gl.UNSIGNED_BYTE || this.elementType == gl.FLOAT);
|
|
||||||
gl.bindTexture(gl.TEXTURE_2D, this.texture);
|
|
||||||
|
|
||||||
// Very limited map from internal type to texture type
|
|
||||||
let internal_format, format, type;
|
|
||||||
if (this.elementType == gl.UNSIGNED_BYTE)
|
|
||||||
{
|
|
||||||
internal_format = this.nbElements == 1 ? gl.ALPHA : gl.RGBA8;
|
|
||||||
format = this.nbElements == 1 ? gl.ALPHA : gl.RGBA;
|
|
||||||
type = gl.UNSIGNED_BYTE;
|
|
||||||
}
|
|
||||||
else if (this.elementType == gl.FLOAT)
|
|
||||||
{
|
|
||||||
internal_format = this.nbElements == 1 ? gl.R32F : RGBA32F;
|
|
||||||
format = this.nbElements == 1 ? gl.RED : gl.RGBA;
|
|
||||||
type = gl.FLOAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
gl.texImage2D(gl.TEXTURE_2D, 0, internal_format, this.nbEntries, 1, 0, format, type, this.cpuArray);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UploadDirtyData()
|
|
||||||
{
|
|
||||||
if (this.dirty)
|
|
||||||
{
|
|
||||||
this.UploadData();
|
|
||||||
this.dirty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ResizeToFitNextPow2(target_count)
|
|
||||||
{
|
|
||||||
let nb_entries = this.nbEntries;
|
|
||||||
while (target_count > nb_entries)
|
|
||||||
{
|
|
||||||
nb_entries <<= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nb_entries > this.nbEntries)
|
|
||||||
{
|
|
||||||
this.Resize(nb_entries);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Resize(nb_entries)
|
|
||||||
{
|
|
||||||
this.nbEntries = nb_entries;
|
|
||||||
|
|
||||||
let gl = this.gl;
|
|
||||||
|
|
||||||
// Create the CPU array
|
|
||||||
const old_array = this.cpuArray;
|
|
||||||
switch (this.elementType)
|
|
||||||
{
|
|
||||||
case gl.FLOAT:
|
|
||||||
this.nbElementBytes = 4;
|
|
||||||
this.cpuArray = new Float32Array(this.nbElements * this.nbEntries);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case gl.UNSIGNED_BYTE:
|
|
||||||
this.nbElementBytes = 1;
|
|
||||||
this.cpuArray = new Uint8Array(this.nbElements * this.nbEntries);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(false, "Unsupported dynamic buffer element type");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate byte size of the buffer
|
|
||||||
this.nbBytes = this.nbElementBytes * this.nbElements * this.nbEntries;
|
|
||||||
|
|
||||||
if (old_array != undefined)
|
|
||||||
{
|
|
||||||
// Copy the values of the previous array over
|
|
||||||
this.cpuArray.set(old_array);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the GPU buffer
|
|
||||||
switch (this.bufferType)
|
|
||||||
{
|
|
||||||
case glDynamicBufferType.Buffer:
|
|
||||||
this.buffer = gl.createBuffer();
|
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
|
|
||||||
gl.bufferData(gl.ARRAY_BUFFER, this.nbBytes, gl.DYNAMIC_DRAW);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case glDynamicBufferType.Texture:
|
|
||||||
this.texture = gl.createTexture();
|
|
||||||
|
|
||||||
// Point sampling with clamp for indexing
|
|
||||||
gl.bindTexture(gl.TEXTURE_2D, this.texture);
|
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(false, "Unsupported dynamic buffer type");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.UploadData();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
125
profiler/vis/Code/WebGLFont.js
vendored
125
profiler/vis/Code/WebGLFont.js
vendored
|
@ -1,125 +0,0 @@
|
||||||
|
|
||||||
class glFont
|
|
||||||
{
|
|
||||||
constructor(gl)
|
|
||||||
{
|
|
||||||
// Offscreen canvas for rendering individual characters
|
|
||||||
this.charCanvas = document.createElement("canvas");
|
|
||||||
this.charContext = this.charCanvas.getContext("2d");
|
|
||||||
|
|
||||||
// Describe the font
|
|
||||||
const font_size = 9;
|
|
||||||
this.fontWidth = 5;
|
|
||||||
this.fontHeight = 13;
|
|
||||||
const font_face = "LocalFiraCode";
|
|
||||||
const font_desc = font_size + "px " + font_face;
|
|
||||||
|
|
||||||
// Ensure the CSS font is loaded before we do any work with it
|
|
||||||
const self = this;
|
|
||||||
document.fonts.load(font_desc).then(function (){
|
|
||||||
|
|
||||||
// Create a canvas atlas for all characters in the font
|
|
||||||
const atlas_canvas = document.createElement("canvas");
|
|
||||||
const atlas_context = atlas_canvas.getContext("2d");
|
|
||||||
atlas_canvas.width = 16 * self.fontWidth;
|
|
||||||
atlas_canvas.height = 16 * self.fontHeight;
|
|
||||||
|
|
||||||
// Add each character to the atlas
|
|
||||||
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-+=[]{};\'~#,./<>?!\"£$%%^&*()";
|
|
||||||
for (let char of chars)
|
|
||||||
{
|
|
||||||
// Render this character to the canvas on its own
|
|
||||||
self.RenderTextToCanvas(char, font_desc, self.fontWidth, self.fontHeight);
|
|
||||||
|
|
||||||
// Calculate a location for it in the atlas using its ASCII code
|
|
||||||
const ascii_code = char.charCodeAt(0);
|
|
||||||
assert(ascii_code < 256);
|
|
||||||
const y_index = Math.floor(ascii_code / 16);
|
|
||||||
const x_index = ascii_code - y_index * 16
|
|
||||||
assert(x_index < 16);
|
|
||||||
assert(y_index < 16);
|
|
||||||
|
|
||||||
// Copy into the atlas
|
|
||||||
atlas_context.drawImage(self.charCanvas, x_index * self.fontWidth, y_index * self.fontHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the atlas texture and store it in the destination object
|
|
||||||
self.atlasTexture = glCreateTexture(gl, atlas_canvas.width, atlas_canvas.height, atlas_canvas);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderTextToCanvas(text, font, width, height)
|
|
||||||
{
|
|
||||||
// Resize canvas to match
|
|
||||||
this.charCanvas.width = width;
|
|
||||||
this.charCanvas.height = height;
|
|
||||||
|
|
||||||
// Clear the background
|
|
||||||
this.charContext.fillStyle = "black";
|
|
||||||
this.charContext.clearRect(0, 0, width, height);
|
|
||||||
|
|
||||||
// TODO(don): I don't know why this results in the crispest text!
|
|
||||||
// Every pattern I've checked so far has thrown up no ideas... but it works, so it will do for now
|
|
||||||
let offset = 0.25;
|
|
||||||
if ("AFILMTWijmw4+{};\'#,.?!\"£*()".includes(text))
|
|
||||||
{
|
|
||||||
offset = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render the text
|
|
||||||
this.charContext.font = font;
|
|
||||||
this.charContext.textAlign = "left";
|
|
||||||
this.charContext.textBaseline = "top";
|
|
||||||
this.charContext.fillText(text, offset, 2.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class glTextBuffer
|
|
||||||
{
|
|
||||||
constructor(gl, font)
|
|
||||||
{
|
|
||||||
this.font = font;
|
|
||||||
this.textMap = {};
|
|
||||||
this.textBuffer = new glDynamicBuffer(gl, glDynamicBufferType.Texture, gl.UNSIGNED_BYTE, 1, 8);
|
|
||||||
this.textBufferPos = 0;
|
|
||||||
this.textEncoder = new TextEncoder();
|
|
||||||
}
|
|
||||||
|
|
||||||
AddText(text)
|
|
||||||
{
|
|
||||||
// Return if it already exists
|
|
||||||
const existing_entry = this.textMap[text];
|
|
||||||
if (existing_entry != undefined)
|
|
||||||
{
|
|
||||||
return existing_entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to the map
|
|
||||||
// Note we're leaving an extra NULL character before every piece of text so that the shader can sample into it on text
|
|
||||||
// boundaries and sample a zero colour for clamp.
|
|
||||||
let entry = {
|
|
||||||
offset: this.textBufferPos + 1,
|
|
||||||
length: text.length,
|
|
||||||
};
|
|
||||||
this.textMap[text] = entry;
|
|
||||||
|
|
||||||
// Ensure there's always enough space in the text buffer before adding
|
|
||||||
this.textBuffer.ResizeToFitNextPow2(entry.offset + entry.length + 1);
|
|
||||||
this.textBuffer.cpuArray.set(this.textEncoder.encode(text), entry.offset, entry.length);
|
|
||||||
this.textBuffer.dirty = true;
|
|
||||||
this.textBufferPos = entry.offset + entry.length;
|
|
||||||
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
UploadData()
|
|
||||||
{
|
|
||||||
this.textBuffer.UploadDirtyData();
|
|
||||||
}
|
|
||||||
|
|
||||||
SetAsUniform(gl, program, name, index)
|
|
||||||
{
|
|
||||||
glSetUniform(gl, program, name, this.textBuffer.texture, index);
|
|
||||||
glSetUniform(gl, program, "inTextBufferDesc.textBufferLength", this.textBuffer.nbEntries);
|
|
||||||
}
|
|
||||||
}
|
|
149
profiler/vis/Code/WebSocketConnection.js
vendored
149
profiler/vis/Code/WebSocketConnection.js
vendored
|
@ -1,149 +0,0 @@
|
||||||
|
|
||||||
WebSocketConnection = (function()
|
|
||||||
{
|
|
||||||
function WebSocketConnection()
|
|
||||||
{
|
|
||||||
this.MessageHandlers = { };
|
|
||||||
this.Socket = null;
|
|
||||||
this.Console = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WebSocketConnection.prototype.SetConsole = function(console)
|
|
||||||
{
|
|
||||||
this.Console = console;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WebSocketConnection.prototype.Connecting = function()
|
|
||||||
{
|
|
||||||
return this.Socket != null && this.Socket.readyState == WebSocket.CONNECTING;
|
|
||||||
}
|
|
||||||
|
|
||||||
WebSocketConnection.prototype.Connected = function()
|
|
||||||
{
|
|
||||||
return this.Socket != null && this.Socket.readyState == WebSocket.OPEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WebSocketConnection.prototype.AddConnectHandler = function(handler)
|
|
||||||
{
|
|
||||||
this.AddMessageHandler("__OnConnect__", handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WebSocketConnection.prototype.AddDisconnectHandler = function(handler)
|
|
||||||
{
|
|
||||||
this.AddMessageHandler("__OnDisconnect__", handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WebSocketConnection.prototype.AddMessageHandler = function(message_name, handler)
|
|
||||||
{
|
|
||||||
// Create the message handler array on-demand
|
|
||||||
if (!(message_name in this.MessageHandlers))
|
|
||||||
this.MessageHandlers[message_name] = [ ];
|
|
||||||
this.MessageHandlers[message_name].push(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WebSocketConnection.prototype.Connect = function(address)
|
|
||||||
{
|
|
||||||
// Abandon previous connection attempt
|
|
||||||
this.Disconnect();
|
|
||||||
|
|
||||||
Log(this, "Connecting to " + address);
|
|
||||||
|
|
||||||
this.Socket = new WebSocket(address);
|
|
||||||
this.Socket.binaryType = "arraybuffer";
|
|
||||||
this.Socket.onopen = Bind(OnOpen, this);
|
|
||||||
this.Socket.onmessage = Bind(OnMessage, this);
|
|
||||||
this.Socket.onclose = Bind(OnClose, this);
|
|
||||||
this.Socket.onerror = Bind(OnError, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WebSocketConnection.prototype.Disconnect = function()
|
|
||||||
{
|
|
||||||
Log(this, "Disconnecting");
|
|
||||||
if (this.Socket != null)
|
|
||||||
{
|
|
||||||
this.Socket.close();
|
|
||||||
this.Socket = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WebSocketConnection.prototype.Send = function(msg)
|
|
||||||
{
|
|
||||||
if (this.Connected())
|
|
||||||
this.Socket.send(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function Log(self, message)
|
|
||||||
{
|
|
||||||
self.Console.Log(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function CallMessageHandlers(self, message_name, data_view, length)
|
|
||||||
{
|
|
||||||
if (message_name in self.MessageHandlers)
|
|
||||||
{
|
|
||||||
var handlers = self.MessageHandlers[message_name];
|
|
||||||
for (var i in handlers)
|
|
||||||
handlers[i](self, data_view, length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnOpen(self, event)
|
|
||||||
{
|
|
||||||
Log(self, "Connected");
|
|
||||||
CallMessageHandlers(self, "__OnConnect__");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnClose(self, event)
|
|
||||||
{
|
|
||||||
// Clear all references
|
|
||||||
self.Socket.onopen = null;
|
|
||||||
self.Socket.onmessage = null;
|
|
||||||
self.Socket.onclose = null;
|
|
||||||
self.Socket.onerror = null;
|
|
||||||
self.Socket = null;
|
|
||||||
|
|
||||||
Log(self, "Disconnected");
|
|
||||||
CallMessageHandlers(self, "__OnDisconnect__");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnError(self, event)
|
|
||||||
{
|
|
||||||
Log(self, "Connection Error ");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMessage(self, event)
|
|
||||||
{
|
|
||||||
let data_view = new DataView(event.data);
|
|
||||||
let data_view_reader = new DataViewReader(data_view, 0);
|
|
||||||
self.CallMessageHandlers(data_view_reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
WebSocketConnection.prototype.CallMessageHandlers = function(data_view_reader)
|
|
||||||
{
|
|
||||||
// Decode standard message header
|
|
||||||
const id = data_view_reader.GetStringOfLength(4);
|
|
||||||
const length = data_view_reader.GetUInt32();
|
|
||||||
|
|
||||||
// Pass the length of the message left to parse
|
|
||||||
CallMessageHandlers(this, id, data_view_reader, length - 8);
|
|
||||||
|
|
||||||
return [ id, length ];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return WebSocketConnection;
|
|
||||||
})();
|
|
8
profiler/vis/Code/tsconfig.json
vendored
8
profiler/vis/Code/tsconfig.json
vendored
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"outFile": "out.js",
|
|
||||||
"allowJs": true,
|
|
||||||
"sourceMap": true
|
|
||||||
},
|
|
||||||
"include": ["ThreadFrame.js"]
|
|
||||||
}
|
|
93
profiler/vis/Styles/Fonts/FiraCode/LICENSE
vendored
93
profiler/vis/Styles/Fonts/FiraCode/LICENSE
vendored
|
@ -1,93 +0,0 @@
|
||||||
Copyright (c) 2014, The Fira Code Project Authors (https://github.com/tonsky/FiraCode)
|
|
||||||
|
|
||||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
||||||
This license is copied below, and is also available with a FAQ at:
|
|
||||||
http://scripts.sil.org/OFL
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------------------------------
|
|
||||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
||||||
-----------------------------------------------------------
|
|
||||||
|
|
||||||
PREAMBLE
|
|
||||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
||||||
development of collaborative font projects, to support the font creation
|
|
||||||
efforts of academic and linguistic communities, and to provide a free and
|
|
||||||
open framework in which fonts may be shared and improved in partnership
|
|
||||||
with others.
|
|
||||||
|
|
||||||
The OFL allows the licensed fonts to be used, studied, modified and
|
|
||||||
redistributed freely as long as they are not sold by themselves. The
|
|
||||||
fonts, including any derivative works, can be bundled, embedded,
|
|
||||||
redistributed and/or sold with any software provided that any reserved
|
|
||||||
names are not used by derivative works. The fonts and derivatives,
|
|
||||||
however, cannot be released under any other type of license. The
|
|
||||||
requirement for fonts to remain under this license does not apply
|
|
||||||
to any document created using the fonts or their derivatives.
|
|
||||||
|
|
||||||
DEFINITIONS
|
|
||||||
"Font Software" refers to the set of files released by the Copyright
|
|
||||||
Holder(s) under this license and clearly marked as such. This may
|
|
||||||
include source files, build scripts and documentation.
|
|
||||||
|
|
||||||
"Reserved Font Name" refers to any names specified as such after the
|
|
||||||
copyright statement(s).
|
|
||||||
|
|
||||||
"Original Version" refers to the collection of Font Software components as
|
|
||||||
distributed by the Copyright Holder(s).
|
|
||||||
|
|
||||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
||||||
or substituting -- in part or in whole -- any of the components of the
|
|
||||||
Original Version, by changing formats or by porting the Font Software to a
|
|
||||||
new environment.
|
|
||||||
|
|
||||||
"Author" refers to any designer, engineer, programmer, technical
|
|
||||||
writer or other person who contributed to the Font Software.
|
|
||||||
|
|
||||||
PERMISSION & CONDITIONS
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
||||||
redistribute, and sell modified and unmodified copies of the Font
|
|
||||||
Software, subject to the following conditions:
|
|
||||||
|
|
||||||
1) Neither the Font Software nor any of its individual components,
|
|
||||||
in Original or Modified Versions, may be sold by itself.
|
|
||||||
|
|
||||||
2) Original or Modified Versions of the Font Software may be bundled,
|
|
||||||
redistributed and/or sold with any software, provided that each copy
|
|
||||||
contains the above copyright notice and this license. These can be
|
|
||||||
included either as stand-alone text files, human-readable headers or
|
|
||||||
in the appropriate machine-readable metadata fields within text or
|
|
||||||
binary files as long as those fields can be easily viewed by the user.
|
|
||||||
|
|
||||||
3) No Modified Version of the Font Software may use the Reserved Font
|
|
||||||
Name(s) unless explicit written permission is granted by the corresponding
|
|
||||||
Copyright Holder. This restriction only applies to the primary font name as
|
|
||||||
presented to the users.
|
|
||||||
|
|
||||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
||||||
Software shall not be used to promote, endorse or advertise any
|
|
||||||
Modified Version, except to acknowledge the contribution(s) of the
|
|
||||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
||||||
permission.
|
|
||||||
|
|
||||||
5) The Font Software, modified or unmodified, in part or in whole,
|
|
||||||
must be distributed entirely under this license, and must not be
|
|
||||||
distributed under any other license. The requirement for fonts to
|
|
||||||
remain under this license does not apply to any document created
|
|
||||||
using the Font Software.
|
|
||||||
|
|
||||||
TERMINATION
|
|
||||||
This license becomes null and void if any of the above conditions are
|
|
||||||
not met.
|
|
||||||
|
|
||||||
DISCLAIMER
|
|
||||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
||||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
||||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
||||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
||||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
237
profiler/vis/Styles/Remotery.css
vendored
237
profiler/vis/Styles/Remotery.css
vendored
|
@ -1,237 +0,0 @@
|
||||||
|
|
||||||
body
|
|
||||||
{
|
|
||||||
/* Take up the full page */
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0px;
|
|
||||||
|
|
||||||
background-color: #999;
|
|
||||||
|
|
||||||
touch-action: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Override default container style to remove 3D effect */
|
|
||||||
.Container
|
|
||||||
{
|
|
||||||
border: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Override default edit box style to remove 3D effect */
|
|
||||||
.EditBox
|
|
||||||
{
|
|
||||||
border: none;
|
|
||||||
box-shadow: none;
|
|
||||||
width:200;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@font-face
|
|
||||||
{
|
|
||||||
font-family: "LocalFiraCode";
|
|
||||||
src:url("Fonts/FiraCode/FiraCode-Regular.ttf");
|
|
||||||
}
|
|
||||||
|
|
||||||
.ConsoleText
|
|
||||||
{
|
|
||||||
overflow:auto;
|
|
||||||
color: #BBB;
|
|
||||||
font: 10px LocalFiraCode;
|
|
||||||
margin: 3px;
|
|
||||||
white-space: pre;
|
|
||||||
line-height:14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.PingContainer
|
|
||||||
{
|
|
||||||
background-color: #F55;
|
|
||||||
border-radius: 2px;
|
|
||||||
|
|
||||||
/* Transition from green is gradual */
|
|
||||||
transition: background-color 0.25s ease-in;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.PingContainerActive
|
|
||||||
{
|
|
||||||
background-color: #5F5;
|
|
||||||
|
|
||||||
/* Transition to green is instant */
|
|
||||||
transition: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.GridNameHeader
|
|
||||||
{
|
|
||||||
position: absolute;
|
|
||||||
display: inline-block;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
background:rgb(48, 48, 48);
|
|
||||||
|
|
||||||
color: #BBB;
|
|
||||||
font: 9px Verdana;
|
|
||||||
|
|
||||||
padding: 1px 1px 1px 2px;
|
|
||||||
border: 1px solid;
|
|
||||||
|
|
||||||
border-top-color:#555;
|
|
||||||
border-left-color:#555;
|
|
||||||
border-bottom-color:#111;
|
|
||||||
border-right-color:#111;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TimelineBox
|
|
||||||
{
|
|
||||||
/* Following style generally copies GridRowCell.GridGroup from BrowserLib */
|
|
||||||
|
|
||||||
padding: 1px 1px 1px 2px;
|
|
||||||
margin: 1px;
|
|
||||||
|
|
||||||
border: 1px solid;
|
|
||||||
border-radius: 2px;
|
|
||||||
border-top-color:#555;
|
|
||||||
border-left-color:#555;
|
|
||||||
border-bottom-color:#111;
|
|
||||||
border-right-color:#111;
|
|
||||||
|
|
||||||
background: #222;
|
|
||||||
|
|
||||||
font: 9px Verdana;
|
|
||||||
color: #BBB;
|
|
||||||
}
|
|
||||||
.TimelineRow
|
|
||||||
{
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.TimelineRowCheckbox
|
|
||||||
{
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
.TimelineRowCheck
|
|
||||||
{
|
|
||||||
/* Pull .TimelineRowExpand to the right of the checkbox */
|
|
||||||
float:left;
|
|
||||||
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
}
|
|
||||||
.TimelineRowExpand
|
|
||||||
{
|
|
||||||
/* Pull .TimelineRowLabel to the right of +/- buttons */
|
|
||||||
float:left;
|
|
||||||
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
}
|
|
||||||
.TimelineRowExpandButton
|
|
||||||
{
|
|
||||||
width: 11px;
|
|
||||||
height: 12px;
|
|
||||||
|
|
||||||
color: #333;
|
|
||||||
|
|
||||||
border: 1px solid;
|
|
||||||
|
|
||||||
border-top-color:#F4F4F4;
|
|
||||||
border-left-color:#F4F4F4;
|
|
||||||
border-bottom-color:#8E8F8F;
|
|
||||||
border-right-color:#8E8F8F;
|
|
||||||
|
|
||||||
/* Top-right to bottom-left grey background gradient */
|
|
||||||
background: #f6f6f6; /* Old browsers */
|
|
||||||
background: -moz-linear-gradient(-45deg, #f6f6f6 0%, #abaeb2 100%); /* FF3.6+ */
|
|
||||||
background: -webkit-gradient(linear, left top, right bottom, color-stop(0%,#f6f6f6), color-stop(100%,#abaeb2)); /* Chrome,Safari4+ */
|
|
||||||
background: -webkit-linear-gradient(-45deg, #f6f6f6 0%,#abaeb2 100%); /* Chrome10+,Safari5.1+ */
|
|
||||||
background: -o-linear-gradient(-45deg, #f6f6f6 0%,#abaeb2 100%); /* Opera 11.10+ */
|
|
||||||
background: -ms-linear-gradient(-45deg, #f6f6f6 0%,#abaeb2 100%); /* IE10+ */
|
|
||||||
background: linear-gradient(135deg, #f6f6f6 0%,#abaeb2 100%); /* W3C */
|
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f6f6f6', endColorstr='#abaeb2',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */
|
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: center;
|
|
||||||
}
|
|
||||||
.TimelineRowExpandButton:hover
|
|
||||||
{
|
|
||||||
border-top-color:#79C6F9;
|
|
||||||
border-left-color:#79C6F9;
|
|
||||||
border-bottom-color:#385D72;
|
|
||||||
border-right-color:#385D72;
|
|
||||||
|
|
||||||
/* Top-right to bottom-left blue background gradient, matching border */
|
|
||||||
background: #f3f3f3; /* Old browsers */
|
|
||||||
background: -moz-linear-gradient(-45deg, #f3f3f3 0%, #79c6f9 100%); /* FF3.6+ */
|
|
||||||
background: -webkit-gradient(linear, left top, right bottom, color-stop(0%,#f3f3f3), color-stop(100%,#79c6f9)); /* Chrome,Safari4+ */
|
|
||||||
background: -webkit-linear-gradient(-45deg, #f3f3f3 0%,#79c6f9 100%); /* Chrome10+,Safari5.1+ */
|
|
||||||
background: -o-linear-gradient(-45deg, #f3f3f3 0%,#79c6f9 100%); /* Opera 11.10+ */
|
|
||||||
background: -ms-linear-gradient(-45deg, #f3f3f3 0%,#79c6f9 100%); /* IE10+ */
|
|
||||||
background: linear-gradient(135deg, #f3f3f3 0%,#79c6f9 100%); /* W3C */
|
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f3f3f3', endColorstr='#79c6f9',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */
|
|
||||||
}
|
|
||||||
.TimelineRowExpandButtonActive
|
|
||||||
{
|
|
||||||
/* Simple means of shifting text within a div to the bottom-right */
|
|
||||||
padding-left:1px;
|
|
||||||
padding-top:1px;
|
|
||||||
width:10px;
|
|
||||||
height:11px;
|
|
||||||
}
|
|
||||||
.TimelineRowLabel
|
|
||||||
{
|
|
||||||
float:left;
|
|
||||||
|
|
||||||
width: 140px;
|
|
||||||
height: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TimelineContainer
|
|
||||||
{
|
|
||||||
}
|
|
||||||
.TimelineLabels
|
|
||||||
{
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
border: 0;
|
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
|
||||||
.TimelineLabelScrollClipper
|
|
||||||
{
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
border: 0;
|
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.DropZone
|
|
||||||
{
|
|
||||||
/* Covers the whole page, initially hidden */
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: none;
|
|
||||||
position: fixed;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
|
|
||||||
/* On top of everything possible */
|
|
||||||
z-index: 99999;
|
|
||||||
|
|
||||||
/* Styling for when visible */
|
|
||||||
background: rgba(32, 4, 136, 0.25);
|
|
||||||
border: 3px dashed white;
|
|
||||||
|
|
||||||
/* Styling for text when visible */
|
|
||||||
color: white;
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
|
||||||
font-size: xx-large;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
|
|
||||||
//
|
|
||||||
// Very basic linear value animation system, for now.
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
namespace("Anim");
|
|
||||||
|
|
||||||
|
|
||||||
Anim.Animation = (function()
|
|
||||||
{
|
|
||||||
var anim_hz = 60;
|
|
||||||
|
|
||||||
|
|
||||||
function Animation(anim_func, start_value, end_value, time, end_callback)
|
|
||||||
{
|
|
||||||
// Setup initial parameters
|
|
||||||
this.StartValue = start_value;
|
|
||||||
this.EndValue = end_value;
|
|
||||||
this.ValueInc = (end_value - start_value) / (time * anim_hz);
|
|
||||||
this.Value = start_value;
|
|
||||||
this.Complete = false;
|
|
||||||
this.EndCallback = end_callback;
|
|
||||||
|
|
||||||
// Cache the update function to prevent recreating the closure
|
|
||||||
var self = this;
|
|
||||||
this.AnimFunc = anim_func;
|
|
||||||
this.AnimUpdate = function() { Update(self); }
|
|
||||||
|
|
||||||
// Call for the start value
|
|
||||||
this.AnimUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function Update(self)
|
|
||||||
{
|
|
||||||
// Queue up the next frame immediately
|
|
||||||
var id = window.setTimeout(self.AnimUpdate, 1000 / anim_hz);
|
|
||||||
|
|
||||||
// Linear step the value and check for completion
|
|
||||||
self.Value += self.ValueInc;
|
|
||||||
if (Math.abs(self.Value - self.EndValue) < 0.01)
|
|
||||||
{
|
|
||||||
self.Value = self.EndValue;
|
|
||||||
self.Complete = true;
|
|
||||||
|
|
||||||
if (self.EndCallback)
|
|
||||||
self.EndCallback();
|
|
||||||
|
|
||||||
window.clearTimeout(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass to the animation function
|
|
||||||
self.AnimFunc(self.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return Animation;
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
Anim.Animate = function(anim_func, start_value, end_value, time, end_callback)
|
|
||||||
{
|
|
||||||
return new Anim.Animation(anim_func, start_value, end_value, time, end_callback);
|
|
||||||
}
|
|
92
profiler/vis/extern/BrowserLib/Core/Code/Bind.js
vendored
92
profiler/vis/extern/BrowserLib/Core/Code/Bind.js
vendored
|
@ -1,92 +0,0 @@
|
||||||
//
|
|
||||||
// This will generate a closure for the given function and optionally bind an arbitrary number of
|
|
||||||
// its initial arguments to specific values.
|
|
||||||
//
|
|
||||||
// Parameters:
|
|
||||||
//
|
|
||||||
// 0: Either the function scope or the function.
|
|
||||||
// 1: If 0 is the function scope, this is the function.
|
|
||||||
// Otherwise it's the start of the optional bound argument list.
|
|
||||||
// 2: Start of the optional bound argument list if 1 is the function.
|
|
||||||
//
|
|
||||||
// Examples:
|
|
||||||
//
|
|
||||||
// function GlobalFunction(p0, p1, p2) { }
|
|
||||||
// function ThisFunction(p0, p1, p2) { }
|
|
||||||
//
|
|
||||||
// var a = Bind("GlobalFunction");
|
|
||||||
// var b = Bind(this, "ThisFunction");
|
|
||||||
// var c = Bind("GlobalFunction", BoundParam0, BoundParam1);
|
|
||||||
// var d = Bind(this, "ThisFunction", BoundParam0, BoundParam1);
|
|
||||||
// var e = Bind(GlobalFunction);
|
|
||||||
// var f = Bind(this, ThisFunction);
|
|
||||||
// var g = Bind(GlobalFunction, BoundParam0, BoundParam1);
|
|
||||||
// var h = Bind(this, ThisFunction, BoundParam0, BoundParam1);
|
|
||||||
//
|
|
||||||
// a(0, 1, 2);
|
|
||||||
// b(0, 1, 2);
|
|
||||||
// c(2);
|
|
||||||
// d(2);
|
|
||||||
// e(0, 1, 2);
|
|
||||||
// f(0, 1, 2);
|
|
||||||
// g(2);
|
|
||||||
// h(2);
|
|
||||||
//
|
|
||||||
function Bind()
|
|
||||||
{
|
|
||||||
// No closure to define?
|
|
||||||
if (arguments.length == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Figure out which of the 4 call types is being used to bind
|
|
||||||
// Locate scope, function and bound parameter start index
|
|
||||||
|
|
||||||
if (typeof(arguments[0]) == "string")
|
|
||||||
{
|
|
||||||
var scope = window;
|
|
||||||
var func = window[arguments[0]];
|
|
||||||
var start = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (typeof(arguments[0]) == "function")
|
|
||||||
{
|
|
||||||
var scope = window;
|
|
||||||
var func = arguments[0];
|
|
||||||
var start = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (typeof(arguments[1]) == "string")
|
|
||||||
{
|
|
||||||
var scope = arguments[0];
|
|
||||||
var func = scope[arguments[1]];
|
|
||||||
var start = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (typeof(arguments[1]) == "function")
|
|
||||||
{
|
|
||||||
var scope = arguments[0];
|
|
||||||
var func = arguments[1];
|
|
||||||
var start = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// unknown
|
|
||||||
console.log("Bind() ERROR: Unknown bind parameter configuration");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the arguments list to an array
|
|
||||||
var arg_array = Array.prototype.slice.call(arguments, start);
|
|
||||||
start = arg_array.length;
|
|
||||||
|
|
||||||
return function()
|
|
||||||
{
|
|
||||||
// Concatenate incoming arguments
|
|
||||||
for (var i = 0; i < arguments.length; i++)
|
|
||||||
arg_array[start + i] = arguments[i];
|
|
||||||
|
|
||||||
// Call the function in the given scope with the new arguments
|
|
||||||
return func.apply(scope, arg_array);
|
|
||||||
}
|
|
||||||
}
|
|
218
profiler/vis/extern/BrowserLib/Core/Code/Convert.js
vendored
218
profiler/vis/extern/BrowserLib/Core/Code/Convert.js
vendored
|
@ -1,218 +0,0 @@
|
||||||
|
|
||||||
namespace("Convert");
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Convert between utf8 and b64 without raising character out of range exceptions with unicode strings
|
|
||||||
// Technique described here: http://monsur.hossa.in/2012/07/20/utf-8-in-javascript.html
|
|
||||||
//
|
|
||||||
Convert.utf8string_to_b64string = function(str)
|
|
||||||
{
|
|
||||||
return btoa(unescape(encodeURIComponent(str)));
|
|
||||||
}
|
|
||||||
Convert.b64string_to_utf8string = function(str)
|
|
||||||
{
|
|
||||||
return decodeURIComponent(escape(atob(str)));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// More general approach, converting between byte arrays and b64
|
|
||||||
// Info here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding
|
|
||||||
//
|
|
||||||
Convert.b64string_to_Uint8Array = function(sBase64, nBlocksSize)
|
|
||||||
{
|
|
||||||
function b64ToUint6 (nChr)
|
|
||||||
{
|
|
||||||
return nChr > 64 && nChr < 91 ?
|
|
||||||
nChr - 65
|
|
||||||
: nChr > 96 && nChr < 123 ?
|
|
||||||
nChr - 71
|
|
||||||
: nChr > 47 && nChr < 58 ?
|
|
||||||
nChr + 4
|
|
||||||
: nChr === 43 ?
|
|
||||||
62
|
|
||||||
: nChr === 47 ?
|
|
||||||
63
|
|
||||||
:
|
|
||||||
0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var
|
|
||||||
sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""),
|
|
||||||
nInLen = sB64Enc.length,
|
|
||||||
nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2,
|
|
||||||
taBytes = new Uint8Array(nOutLen);
|
|
||||||
|
|
||||||
for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++)
|
|
||||||
{
|
|
||||||
nMod4 = nInIdx & 3;
|
|
||||||
nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4;
|
|
||||||
if (nMod4 === 3 || nInLen - nInIdx === 1)
|
|
||||||
{
|
|
||||||
for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++)
|
|
||||||
taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
|
|
||||||
nUint24 = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return taBytes;
|
|
||||||
}
|
|
||||||
Convert.Uint8Array_to_b64string = function(aBytes)
|
|
||||||
{
|
|
||||||
function uint6ToB64 (nUint6)
|
|
||||||
{
|
|
||||||
return nUint6 < 26 ?
|
|
||||||
nUint6 + 65
|
|
||||||
: nUint6 < 52 ?
|
|
||||||
nUint6 + 71
|
|
||||||
: nUint6 < 62 ?
|
|
||||||
nUint6 - 4
|
|
||||||
: nUint6 === 62 ?
|
|
||||||
43
|
|
||||||
: nUint6 === 63 ?
|
|
||||||
47
|
|
||||||
:
|
|
||||||
65;
|
|
||||||
}
|
|
||||||
|
|
||||||
var nMod3, sB64Enc = "";
|
|
||||||
|
|
||||||
for (var nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++)
|
|
||||||
{
|
|
||||||
nMod3 = nIdx % 3;
|
|
||||||
if (nIdx > 0 && (nIdx * 4 / 3) % 76 === 0)
|
|
||||||
sB64Enc += "\r\n";
|
|
||||||
nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24);
|
|
||||||
if (nMod3 === 2 || aBytes.length - nIdx === 1)
|
|
||||||
{
|
|
||||||
sB64Enc += String.fromCharCode(uint6ToB64(nUint24 >>> 18 & 63), uint6ToB64(nUint24 >>> 12 & 63), uint6ToB64(nUint24 >>> 6 & 63), uint6ToB64(nUint24 & 63));
|
|
||||||
nUint24 = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sB64Enc.replace(/A(?=A$|$)/g, "=");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Unicode and arbitrary value safe conversion between strings and Uint8Arrays
|
|
||||||
//
|
|
||||||
Convert.Uint8Array_to_string = function(aBytes)
|
|
||||||
{
|
|
||||||
var sView = "";
|
|
||||||
|
|
||||||
for (var nPart, nLen = aBytes.length, nIdx = 0; nIdx < nLen; nIdx++)
|
|
||||||
{
|
|
||||||
nPart = aBytes[nIdx];
|
|
||||||
sView += String.fromCharCode(
|
|
||||||
nPart > 251 && nPart < 254 && nIdx + 5 < nLen ? /* six bytes */
|
|
||||||
/* (nPart - 252 << 32) is not possible in ECMAScript! So...: */
|
|
||||||
(nPart - 252) * 1073741824 + (aBytes[++nIdx] - 128 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
|
|
||||||
: nPart > 247 && nPart < 252 && nIdx + 4 < nLen ? /* five bytes */
|
|
||||||
(nPart - 248 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
|
|
||||||
: nPart > 239 && nPart < 248 && nIdx + 3 < nLen ? /* four bytes */
|
|
||||||
(nPart - 240 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
|
|
||||||
: nPart > 223 && nPart < 240 && nIdx + 2 < nLen ? /* three bytes */
|
|
||||||
(nPart - 224 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
|
|
||||||
: nPart > 191 && nPart < 224 && nIdx + 1 < nLen ? /* two bytes */
|
|
||||||
(nPart - 192 << 6) + aBytes[++nIdx] - 128
|
|
||||||
: /* nPart < 127 ? */ /* one byte */
|
|
||||||
nPart
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sView;
|
|
||||||
}
|
|
||||||
Convert.string_to_Uint8Array = function(sDOMStr)
|
|
||||||
{
|
|
||||||
var aBytes, nChr, nStrLen = sDOMStr.length, nArrLen = 0;
|
|
||||||
|
|
||||||
/* mapping... */
|
|
||||||
|
|
||||||
for (var nMapIdx = 0; nMapIdx < nStrLen; nMapIdx++)
|
|
||||||
{
|
|
||||||
nChr = sDOMStr.charCodeAt(nMapIdx);
|
|
||||||
nArrLen += nChr < 0x80 ? 1 : nChr < 0x800 ? 2 : nChr < 0x10000 ? 3 : nChr < 0x200000 ? 4 : nChr < 0x4000000 ? 5 : 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
aBytes = new Uint8Array(nArrLen);
|
|
||||||
|
|
||||||
/* transcription... */
|
|
||||||
|
|
||||||
for (var nIdx = 0, nChrIdx = 0; nIdx < nArrLen; nChrIdx++)
|
|
||||||
{
|
|
||||||
nChr = sDOMStr.charCodeAt(nChrIdx);
|
|
||||||
if (nChr < 128)
|
|
||||||
{
|
|
||||||
/* one byte */
|
|
||||||
aBytes[nIdx++] = nChr;
|
|
||||||
}
|
|
||||||
else if (nChr < 0x800)
|
|
||||||
{
|
|
||||||
/* two bytes */
|
|
||||||
aBytes[nIdx++] = 192 + (nChr >>> 6);
|
|
||||||
aBytes[nIdx++] = 128 + (nChr & 63);
|
|
||||||
}
|
|
||||||
else if (nChr < 0x10000)
|
|
||||||
{
|
|
||||||
/* three bytes */
|
|
||||||
aBytes[nIdx++] = 224 + (nChr >>> 12);
|
|
||||||
aBytes[nIdx++] = 128 + (nChr >>> 6 & 63);
|
|
||||||
aBytes[nIdx++] = 128 + (nChr & 63);
|
|
||||||
}
|
|
||||||
else if (nChr < 0x200000)
|
|
||||||
{
|
|
||||||
/* four bytes */
|
|
||||||
aBytes[nIdx++] = 240 + (nChr >>> 18);
|
|
||||||
aBytes[nIdx++] = 128 + (nChr >>> 12 & 63);
|
|
||||||
aBytes[nIdx++] = 128 + (nChr >>> 6 & 63);
|
|
||||||
aBytes[nIdx++] = 128 + (nChr & 63);
|
|
||||||
}
|
|
||||||
else if (nChr < 0x4000000)
|
|
||||||
{
|
|
||||||
/* five bytes */
|
|
||||||
aBytes[nIdx++] = 248 + (nChr >>> 24);
|
|
||||||
aBytes[nIdx++] = 128 + (nChr >>> 18 & 63);
|
|
||||||
aBytes[nIdx++] = 128 + (nChr >>> 12 & 63);
|
|
||||||
aBytes[nIdx++] = 128 + (nChr >>> 6 & 63);
|
|
||||||
aBytes[nIdx++] = 128 + (nChr & 63);
|
|
||||||
}
|
|
||||||
else /* if (nChr <= 0x7fffffff) */
|
|
||||||
{
|
|
||||||
/* six bytes */
|
|
||||||
aBytes[nIdx++] = 252 + /* (nChr >>> 32) is not possible in ECMAScript! So...: */ (nChr / 1073741824);
|
|
||||||
aBytes[nIdx++] = 128 + (nChr >>> 24 & 63);
|
|
||||||
aBytes[nIdx++] = 128 + (nChr >>> 18 & 63);
|
|
||||||
aBytes[nIdx++] = 128 + (nChr >>> 12 & 63);
|
|
||||||
aBytes[nIdx++] = 128 + (nChr >>> 6 & 63);
|
|
||||||
aBytes[nIdx++] = 128 + (nChr & 63);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return aBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Converts all characters in a string that have equivalent entities to their ampersand/entity names.
|
|
||||||
// Based on https://gist.github.com/jonathantneal/6093551
|
|
||||||
//
|
|
||||||
Convert.string_to_html_entities = (function()
|
|
||||||
{
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var data = '34quot38amp39apos60lt62gt160nbsp161iexcl162cent163pound164curren165yen166brvbar167sect168uml169copy170ordf171laquo172not173shy174reg175macr176deg177plusmn178sup2179sup3180acute181micro182para183middot184cedil185sup1186ordm187raquo188frac14189frac12190frac34191iquest192Agrave193Aacute194Acirc195Atilde196Auml197Aring198AElig199Ccedil200Egrave201Eacute202Ecirc203Euml204Igrave205Iacute206Icirc207Iuml208ETH209Ntilde210Ograve211Oacute212Ocirc213Otilde214Ouml215times216Oslash217Ugrave218Uacute219Ucirc220Uuml221Yacute222THORN223szlig224agrave225aacute226acirc227atilde228auml229aring230aelig231ccedil232egrave233eacute234ecirc235euml236igrave237iacute238icirc239iuml240eth241ntilde242ograve243oacute244ocirc245otilde246ouml247divide248oslash249ugrave250uacute251ucirc252uuml253yacute254thorn255yuml402fnof913Alpha914Beta915Gamma916Delta917Epsilon918Zeta919Eta920Theta921Iota922Kappa923Lambda924Mu925Nu926Xi927Omicron928Pi929Rho931Sigma932Tau933Upsilon934Phi935Chi936Psi937Omega945alpha946beta947gamma948delta949epsilon950zeta951eta952theta953iota954kappa955lambda956mu957nu958xi959omicron960pi961rho962sigmaf963sigma964tau965upsilon966phi967chi968psi969omega977thetasym978upsih982piv8226bull8230hellip8242prime8243Prime8254oline8260frasl8472weierp8465image8476real8482trade8501alefsym8592larr8593uarr8594rarr8595darr8596harr8629crarr8656lArr8657uArr8658rArr8659dArr8660hArr8704forall8706part8707exist8709empty8711nabla8712isin8713notin8715ni8719prod8721sum8722minus8727lowast8730radic8733prop8734infin8736ang8743and8744or8745cap8746cup8747int8756there48764sim8773cong8776asymp8800ne8801equiv8804le8805ge8834sub8835sup8836nsub8838sube8839supe8853oplus8855otimes8869perp8901sdot8968lceil8969rceil8970lfloor8971rfloor9001lang9002rang9674loz9824spades9827clubs9829hearts9830diams338OElig339oelig352Scaron353scaron376Yuml710circ732tilde8194ensp8195emsp8201thinsp8204zwnj8205zwj8206lrm8207rlm8211ndash8212mdash8216lsquo8217rsquo8218sbquo8220ldquo8221rdquo8222bdquo8224dagger8225Dagger8240permil8249lsaquo8250rsaquo8364euro';
|
|
||||||
var charCodes = data.split(/[A-z]+/);
|
|
||||||
var entities = data.split(/\d+/).slice(1);
|
|
||||||
|
|
||||||
return function encodeHTMLEntities(text)
|
|
||||||
{
|
|
||||||
return text.replace(/[\u00A0-\u2666<>"'&]/g, function (match)
|
|
||||||
{
|
|
||||||
var charCode = String(match.charCodeAt(0));
|
|
||||||
var index = charCodes.indexOf(charCode);
|
|
||||||
return '&' + (entities[index] ? entities[index] : '#' + charCode) + ';';
|
|
||||||
});
|
|
||||||
};
|
|
||||||
})();
|
|
26
profiler/vis/extern/BrowserLib/Core/Code/Core.js
vendored
26
profiler/vis/extern/BrowserLib/Core/Code/Core.js
vendored
|
@ -1,26 +0,0 @@
|
||||||
|
|
||||||
// TODO: requires function for checking existence of dependencies
|
|
||||||
|
|
||||||
|
|
||||||
function namespace(name)
|
|
||||||
{
|
|
||||||
// Ensure all nested namespaces are created only once
|
|
||||||
|
|
||||||
var ns_list = name.split(".");
|
|
||||||
var parent_ns = window;
|
|
||||||
|
|
||||||
for (var i in ns_list)
|
|
||||||
{
|
|
||||||
var ns_name = ns_list[i];
|
|
||||||
if (!(ns_name in parent_ns))
|
|
||||||
parent_ns[ns_name] = { };
|
|
||||||
|
|
||||||
parent_ns = parent_ns[ns_name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function multiline(fn)
|
|
||||||
{
|
|
||||||
return fn.toString().split(/\n/).slice(1, -1).join("\n");
|
|
||||||
}
|
|
526
profiler/vis/extern/BrowserLib/Core/Code/DOM.js
vendored
526
profiler/vis/extern/BrowserLib/Core/Code/DOM.js
vendored
|
@ -1,526 +0,0 @@
|
||||||
|
|
||||||
namespace("DOM.Node");
|
|
||||||
namespace("DOM.Event");
|
|
||||||
namespace("DOM.Applet");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// =====================================================================================================================
|
|
||||||
// ----- DOCUMENT NODE/ELEMENT EXTENSIONS ------------------------------------------------------------------------------
|
|
||||||
// =====================================================================================================================
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DOM.Node.Get = function(id)
|
|
||||||
{
|
|
||||||
return document.getElementById(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Set node position
|
|
||||||
//
|
|
||||||
DOM.Node.SetPosition = function(node, position)
|
|
||||||
{
|
|
||||||
node.style.left = position[0];
|
|
||||||
node.style.top = position[1];
|
|
||||||
}
|
|
||||||
DOM.Node.SetX = function(node, x)
|
|
||||||
{
|
|
||||||
node.style.left = x;
|
|
||||||
}
|
|
||||||
DOM.Node.SetY = function(node, y)
|
|
||||||
{
|
|
||||||
node.style.top = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Get the absolute position of a HTML element on the page
|
|
||||||
//
|
|
||||||
DOM.Node.GetPosition = function(element, account_for_scroll)
|
|
||||||
{
|
|
||||||
// Recurse up through parents, summing offsets from their parent
|
|
||||||
var x = 0, y = 0;
|
|
||||||
for (var node = element; node != null; node = node.offsetParent)
|
|
||||||
{
|
|
||||||
x += node.offsetLeft;
|
|
||||||
y += node.offsetTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (account_for_scroll)
|
|
||||||
{
|
|
||||||
// Walk up the hierarchy subtracting away any scrolling
|
|
||||||
for (var node = element; node != document.body; node = node.parentNode)
|
|
||||||
{
|
|
||||||
x -= node.scrollLeft;
|
|
||||||
y -= node.scrollTop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [x, y];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Set node size
|
|
||||||
//
|
|
||||||
DOM.Node.SetSize = function(node, size)
|
|
||||||
{
|
|
||||||
node.style.width = size[0];
|
|
||||||
node.style.height = size[1];
|
|
||||||
}
|
|
||||||
DOM.Node.SetWidth = function(node, width)
|
|
||||||
{
|
|
||||||
node.style.width = width;
|
|
||||||
}
|
|
||||||
DOM.Node.SetHeight = function(node, height)
|
|
||||||
{
|
|
||||||
node.style.height = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Get node OFFSET size:
|
|
||||||
// clientX includes padding
|
|
||||||
// offsetX includes padding and borders
|
|
||||||
// scrollX includes padding, borders and size of contained node
|
|
||||||
//
|
|
||||||
DOM.Node.GetSize = function(node)
|
|
||||||
{
|
|
||||||
return [ node.offsetWidth, node.offsetHeight ];
|
|
||||||
}
|
|
||||||
DOM.Node.GetWidth = function(node)
|
|
||||||
{
|
|
||||||
return node.offsetWidth;
|
|
||||||
}
|
|
||||||
DOM.Node.GetHeight = function(node)
|
|
||||||
{
|
|
||||||
return node.offsetHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Set node opacity
|
|
||||||
//
|
|
||||||
DOM.Node.SetOpacity = function(node, value)
|
|
||||||
{
|
|
||||||
node.style.opacity = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
DOM.Node.SetColour = function(node, colour)
|
|
||||||
{
|
|
||||||
node.style.color = colour;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Hide a node by completely disabling its rendering (it no longer contributes to document layout)
|
|
||||||
//
|
|
||||||
DOM.Node.Hide = function(node)
|
|
||||||
{
|
|
||||||
node.style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Show a node by restoring its influcen in document layout
|
|
||||||
//
|
|
||||||
DOM.Node.Show = function(node)
|
|
||||||
{
|
|
||||||
node.style.display = "block";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Add a CSS class to a HTML element, specified last
|
|
||||||
//
|
|
||||||
DOM.Node.AddClass = function(node, class_name)
|
|
||||||
{
|
|
||||||
// Ensure the class hasn't already been added
|
|
||||||
DOM.Node.RemoveClass(node, class_name);
|
|
||||||
node.className += " " + class_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Remove a CSS class from a HTML element
|
|
||||||
//
|
|
||||||
DOM.Node.RemoveClass = function(node, class_name)
|
|
||||||
{
|
|
||||||
// Remove all variations of where the class name can be in the string list
|
|
||||||
var regexp = new RegExp("\\b" + class_name + "\\b");
|
|
||||||
node.className = node.className.replace(regexp, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Check to see if a HTML element contains a class
|
|
||||||
//
|
|
||||||
DOM.Node.HasClass = function(node, class_name)
|
|
||||||
{
|
|
||||||
var regexp = new RegExp("\\b" + class_name + "\\b");
|
|
||||||
return regexp.test(node.className);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Recursively search for a node with the given class name
|
|
||||||
//
|
|
||||||
DOM.Node.FindWithClass = function(parent_node, class_name, index)
|
|
||||||
{
|
|
||||||
// Search the children looking for a node with the given class name
|
|
||||||
for (var i in parent_node.childNodes)
|
|
||||||
{
|
|
||||||
var node = parent_node.childNodes[i];
|
|
||||||
if (DOM.Node.HasClass(node, class_name))
|
|
||||||
{
|
|
||||||
if (index === undefined || index-- == 0)
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recurse into children
|
|
||||||
node = DOM.Node.FindWithClass(node, class_name);
|
|
||||||
if (node != null)
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Check to see if one node logically contains another
|
|
||||||
//
|
|
||||||
DOM.Node.Contains = function(node, container_node)
|
|
||||||
{
|
|
||||||
while (node != null && node != container_node)
|
|
||||||
node = node.parentNode;
|
|
||||||
return node != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Create the HTML nodes specified in the text passed in
|
|
||||||
// Assumes there is only one root node in the text
|
|
||||||
//
|
|
||||||
DOM.Node.CreateHTML = function(html)
|
|
||||||
{
|
|
||||||
var div = document.createElement("div");
|
|
||||||
div.innerHTML = html;
|
|
||||||
|
|
||||||
// First child may be a text node, followed by the created HTML
|
|
||||||
var child = div.firstChild;
|
|
||||||
if (child != null && child.nodeType == 3)
|
|
||||||
child = child.nextSibling;
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Make a copy of a HTML element, making it visible and clearing its ID to ensure it's not a duplicate
|
|
||||||
//
|
|
||||||
DOM.Node.Clone = function(name)
|
|
||||||
{
|
|
||||||
// Get the template element and clone it, making sure it's renderable
|
|
||||||
var node = DOM.Node.Get(name);
|
|
||||||
node = node.cloneNode(true);
|
|
||||||
node.id = null;
|
|
||||||
node.style.display = "block";
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Append an arbitrary block of HTML to an existing node
|
|
||||||
//
|
|
||||||
DOM.Node.AppendHTML = function(node, html)
|
|
||||||
{
|
|
||||||
var child = DOM.Node.CreateHTML(html);
|
|
||||||
node.appendChild(child);
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Append a div that clears the float style
|
|
||||||
//
|
|
||||||
DOM.Node.AppendClearFloat = function(node)
|
|
||||||
{
|
|
||||||
var div = document.createElement("div");
|
|
||||||
div.style.clear = "both";
|
|
||||||
node.appendChild(div);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Check to see that the object passed in is an instance of a DOM node
|
|
||||||
//
|
|
||||||
DOM.Node.IsNode = function(object)
|
|
||||||
{
|
|
||||||
return object instanceof Element;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Create an "iframe shim" so that elements within it render over a Java Applet
|
|
||||||
// http://web.archive.org/web/20110707212850/http://www.oratransplant.nl/2007/10/26/using-iframe-shim-to-partly-cover-a-java-applet/
|
|
||||||
//
|
|
||||||
DOM.Node.CreateShim = function(parent)
|
|
||||||
{
|
|
||||||
var shimmer = document.createElement("iframe");
|
|
||||||
|
|
||||||
// Position the shimmer so that it's the same location/size as its parent
|
|
||||||
shimmer.style.position = "fixed";
|
|
||||||
shimmer.style.left = parent.style.left;
|
|
||||||
shimmer.style.top = parent.style.top;
|
|
||||||
shimmer.style.width = parent.offsetWidth;
|
|
||||||
shimmer.style.height = parent.offsetHeight;
|
|
||||||
|
|
||||||
// We want the shimmer to be one level below its contents
|
|
||||||
shimmer.style.zIndex = parent.style.zIndex - 1;
|
|
||||||
|
|
||||||
// Ensure its empty
|
|
||||||
shimmer.setAttribute("frameborder", "0");
|
|
||||||
shimmer.setAttribute("src", "");
|
|
||||||
|
|
||||||
// Add to the document and the parent
|
|
||||||
document.body.appendChild(shimmer);
|
|
||||||
parent.Shimmer = shimmer;
|
|
||||||
return shimmer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// =====================================================================================================================
|
|
||||||
// ----- EVENT HANDLING EXTENSIONS -------------------------------------------------------------------------------------
|
|
||||||
// =====================================================================================================================
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Retrieves the event from the first parameter passed into an HTML event
|
|
||||||
//
|
|
||||||
DOM.Event.Get = function(evt)
|
|
||||||
{
|
|
||||||
// Internet explorer doesn't pass the event
|
|
||||||
return window.event || evt;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Retrieves the element that triggered an event from the event object
|
|
||||||
//
|
|
||||||
DOM.Event.GetNode = function(evt)
|
|
||||||
{
|
|
||||||
evt = DOM.Event.Get(evt);
|
|
||||||
|
|
||||||
// Get the target element
|
|
||||||
var element;
|
|
||||||
if (evt.target)
|
|
||||||
element = evt.target;
|
|
||||||
else if (e.srcElement)
|
|
||||||
element = evt.srcElement;
|
|
||||||
|
|
||||||
// Default Safari bug
|
|
||||||
if (element.nodeType == 3)
|
|
||||||
element = element.parentNode;
|
|
||||||
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Stop default action for an event
|
|
||||||
//
|
|
||||||
DOM.Event.StopDefaultAction = function(evt)
|
|
||||||
{
|
|
||||||
if (evt && evt.preventDefault)
|
|
||||||
evt.preventDefault();
|
|
||||||
else if (window.event && window.event.returnValue)
|
|
||||||
window.event.returnValue = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Stops events bubbling up to parent event handlers
|
|
||||||
//
|
|
||||||
DOM.Event.StopPropagation = function(evt)
|
|
||||||
{
|
|
||||||
evt = DOM.Event.Get(evt);
|
|
||||||
if (evt)
|
|
||||||
{
|
|
||||||
evt.cancelBubble = true;
|
|
||||||
if (evt.stopPropagation)
|
|
||||||
evt.stopPropagation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Stop both event default action and propagation
|
|
||||||
//
|
|
||||||
DOM.Event.StopAll = function(evt)
|
|
||||||
{
|
|
||||||
DOM.Event.StopDefaultAction(evt);
|
|
||||||
DOM.Event.StopPropagation(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Adds an event handler to an event
|
|
||||||
//
|
|
||||||
DOM.Event.AddHandler = function(obj, evt, func)
|
|
||||||
{
|
|
||||||
if (obj)
|
|
||||||
{
|
|
||||||
if (obj.addEventListener)
|
|
||||||
obj.addEventListener(evt, func, false);
|
|
||||||
else if (obj.attachEvent)
|
|
||||||
obj.attachEvent("on" + evt, func);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Removes an event handler from an event
|
|
||||||
//
|
|
||||||
DOM.Event.RemoveHandler = function(obj, evt, func)
|
|
||||||
{
|
|
||||||
if (obj)
|
|
||||||
{
|
|
||||||
if (obj.removeEventListener)
|
|
||||||
obj.removeEventListener(evt, func, false);
|
|
||||||
else if (obj.detachEvent)
|
|
||||||
obj.detachEvent("on" + evt, func);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Get the position of the mouse cursor, page relative
|
|
||||||
//
|
|
||||||
DOM.Event.GetMousePosition = function(evt)
|
|
||||||
{
|
|
||||||
evt = DOM.Event.Get(evt);
|
|
||||||
|
|
||||||
var px = 0;
|
|
||||||
var py = 0;
|
|
||||||
if (evt.pageX || evt.pageY)
|
|
||||||
{
|
|
||||||
px = evt.pageX;
|
|
||||||
py = evt.pageY;
|
|
||||||
}
|
|
||||||
else if (evt.clientX || evt.clientY)
|
|
||||||
{
|
|
||||||
px = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
|
|
||||||
py = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [px, py];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Get the list of files attached to a drop event
|
|
||||||
//
|
|
||||||
DOM.Event.GetDropFiles = function(evt)
|
|
||||||
{
|
|
||||||
let files = [];
|
|
||||||
if (evt.dataTransfer.items)
|
|
||||||
{
|
|
||||||
for (let i = 0; i < evt.dataTransfer.items.length; i++)
|
|
||||||
{
|
|
||||||
if (evt.dataTransfer.items[i].kind === 'file')
|
|
||||||
{
|
|
||||||
files.push(evt.dataTransfer.items[i].getAsFile());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (let i = 0; i < evt.dataTransfer.files.length; i++)
|
|
||||||
{
|
|
||||||
files.push(evt.dataTransfer.files[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// =====================================================================================================================
|
|
||||||
// ----- JAVA APPLET EXTENSIONS ----------------------------------------------------------------------------------------
|
|
||||||
// =====================================================================================================================
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Create an applet element for loading a Java applet, attaching it to the specified node
|
|
||||||
//
|
|
||||||
DOM.Applet.Load = function(dest_id, id, code, archive)
|
|
||||||
{
|
|
||||||
// Lookup the applet destination
|
|
||||||
var dest = DOM.Node.Get(dest_id);
|
|
||||||
if (!dest)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Construct the applet element and add it to the destination
|
|
||||||
Debug.Log("Injecting applet DOM code");
|
|
||||||
var applet = "<applet id='" + id + "' code='" + code + "' archive='" + archive + "'";
|
|
||||||
applet += " width='" + dest.offsetWidth + "' height='" + dest.offsetHeight + "'>";
|
|
||||||
applet += "</applet>";
|
|
||||||
dest.innerHTML = applet;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Moves and resizes a named applet so that it fits in the destination div element.
|
|
||||||
// The applet must be contained by a div element itself. This container div is moved along
|
|
||||||
// with the applet.
|
|
||||||
//
|
|
||||||
DOM.Applet.Move = function(dest_div, applet, z_index, hide)
|
|
||||||
{
|
|
||||||
if (!applet || !dest_div)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Before modifying any location information, hide the applet so that it doesn't render over
|
|
||||||
// any newly visible elements that appear while the location information is being modified.
|
|
||||||
if (hide)
|
|
||||||
applet.style.visibility = "hidden";
|
|
||||||
|
|
||||||
// Get its view rect
|
|
||||||
var pos = DOM.Node.GetPosition(dest_div);
|
|
||||||
var w = dest_div.offsetWidth;
|
|
||||||
var h = dest_div.offsetHeight;
|
|
||||||
|
|
||||||
// It needs to be embedded in a <div> for correct scale/position adjustment
|
|
||||||
var container = applet.parentNode;
|
|
||||||
if (!container || container.localName != "div")
|
|
||||||
{
|
|
||||||
Debug.Log("ERROR: Couldn't find source applet's div container");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reposition and resize the containing div element
|
|
||||||
container.style.left = pos[0];
|
|
||||||
container.style.top = pos[1];
|
|
||||||
container.style.width = w;
|
|
||||||
container.style.height = h;
|
|
||||||
container.style.zIndex = z_index;
|
|
||||||
|
|
||||||
// Resize the applet itself
|
|
||||||
applet.style.width = w;
|
|
||||||
applet.style.height = h;
|
|
||||||
|
|
||||||
// Everything modified, safe to show
|
|
||||||
applet.style.visibility = "visible";
|
|
||||||
}
|
|
149
profiler/vis/extern/BrowserLib/Core/Code/Keyboard.js
vendored
149
profiler/vis/extern/BrowserLib/Core/Code/Keyboard.js
vendored
|
@ -1,149 +0,0 @@
|
||||||
|
|
||||||
namespace("Keyboard")
|
|
||||||
|
|
||||||
|
|
||||||
// =====================================================================================================================
|
|
||||||
// Key codes copied from closure-library
|
|
||||||
// https://code.google.com/p/closure-library/source/browse/closure/goog/events/keycodes.js
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
|
||||||
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
// http://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.
|
|
||||||
Keyboard.Codes = {
|
|
||||||
WIN_KEY_FF_LINUX : 0,
|
|
||||||
MAC_ENTER : 3,
|
|
||||||
BACKSPACE : 8,
|
|
||||||
TAB : 9,
|
|
||||||
NUM_CENTER : 12, // NUMLOCK on FF/Safari Mac
|
|
||||||
ENTER : 13,
|
|
||||||
SHIFT : 16,
|
|
||||||
CTRL : 17,
|
|
||||||
ALT : 18,
|
|
||||||
PAUSE : 19,
|
|
||||||
CAPS_LOCK : 20,
|
|
||||||
ESC : 27,
|
|
||||||
SPACE : 32,
|
|
||||||
PAGE_UP : 33, // also NUM_NORTH_EAST
|
|
||||||
PAGE_DOWN : 34, // also NUM_SOUTH_EAST
|
|
||||||
END : 35, // also NUM_SOUTH_WEST
|
|
||||||
HOME : 36, // also NUM_NORTH_WEST
|
|
||||||
LEFT : 37, // also NUM_WEST
|
|
||||||
UP : 38, // also NUM_NORTH
|
|
||||||
RIGHT : 39, // also NUM_EAST
|
|
||||||
DOWN : 40, // also NUM_SOUTH
|
|
||||||
PRINT_SCREEN : 44,
|
|
||||||
INSERT : 45, // also NUM_INSERT
|
|
||||||
DELETE : 46, // also NUM_DELETE
|
|
||||||
ZERO : 48,
|
|
||||||
ONE : 49,
|
|
||||||
TWO : 50,
|
|
||||||
THREE : 51,
|
|
||||||
FOUR : 52,
|
|
||||||
FIVE : 53,
|
|
||||||
SIX : 54,
|
|
||||||
SEVEN : 55,
|
|
||||||
EIGHT : 56,
|
|
||||||
NINE : 57,
|
|
||||||
FF_SEMICOLON : 59, // Firefox (Gecko) fires this for semicolon instead of 186
|
|
||||||
FF_EQUALS : 61, // Firefox (Gecko) fires this for equals instead of 187
|
|
||||||
FF_DASH : 173, // Firefox (Gecko) fires this for dash instead of 189
|
|
||||||
QUESTION_MARK : 63, // needs localization
|
|
||||||
A : 65,
|
|
||||||
B : 66,
|
|
||||||
C : 67,
|
|
||||||
D : 68,
|
|
||||||
E : 69,
|
|
||||||
F : 70,
|
|
||||||
G : 71,
|
|
||||||
H : 72,
|
|
||||||
I : 73,
|
|
||||||
J : 74,
|
|
||||||
K : 75,
|
|
||||||
L : 76,
|
|
||||||
M : 77,
|
|
||||||
N : 78,
|
|
||||||
O : 79,
|
|
||||||
P : 80,
|
|
||||||
Q : 81,
|
|
||||||
R : 82,
|
|
||||||
S : 83,
|
|
||||||
T : 84,
|
|
||||||
U : 85,
|
|
||||||
V : 86,
|
|
||||||
W : 87,
|
|
||||||
X : 88,
|
|
||||||
Y : 89,
|
|
||||||
Z : 90,
|
|
||||||
META : 91, // WIN_KEY_LEFT
|
|
||||||
WIN_KEY_RIGHT : 92,
|
|
||||||
CONTEXT_MENU : 93,
|
|
||||||
NUM_ZERO : 96,
|
|
||||||
NUM_ONE : 97,
|
|
||||||
NUM_TWO : 98,
|
|
||||||
NUM_THREE : 99,
|
|
||||||
NUM_FOUR : 100,
|
|
||||||
NUM_FIVE : 101,
|
|
||||||
NUM_SIX : 102,
|
|
||||||
NUM_SEVEN : 103,
|
|
||||||
NUM_EIGHT : 104,
|
|
||||||
NUM_NINE : 105,
|
|
||||||
NUM_MULTIPLY : 106,
|
|
||||||
NUM_PLUS : 107,
|
|
||||||
NUM_MINUS : 109,
|
|
||||||
NUM_PERIOD : 110,
|
|
||||||
NUM_DIVISION : 111,
|
|
||||||
F1 : 112,
|
|
||||||
F2 : 113,
|
|
||||||
F3 : 114,
|
|
||||||
F4 : 115,
|
|
||||||
F5 : 116,
|
|
||||||
F6 : 117,
|
|
||||||
F7 : 118,
|
|
||||||
F8 : 119,
|
|
||||||
F9 : 120,
|
|
||||||
F10 : 121,
|
|
||||||
F11 : 122,
|
|
||||||
F12 : 123,
|
|
||||||
NUMLOCK : 144,
|
|
||||||
SCROLL_LOCK : 145,
|
|
||||||
|
|
||||||
// OS-specific media keys like volume controls and browser controls.
|
|
||||||
FIRST_MEDIA_KEY : 166,
|
|
||||||
LAST_MEDIA_KEY : 183,
|
|
||||||
|
|
||||||
SEMICOLON : 186, // needs localization
|
|
||||||
DASH : 189, // needs localization
|
|
||||||
EQUALS : 187, // needs localization
|
|
||||||
COMMA : 188, // needs localization
|
|
||||||
PERIOD : 190, // needs localization
|
|
||||||
SLASH : 191, // needs localization
|
|
||||||
APOSTROPHE : 192, // needs localization
|
|
||||||
TILDE : 192, // needs localization
|
|
||||||
SINGLE_QUOTE : 222, // needs localization
|
|
||||||
OPEN_SQUARE_BRACKET : 219, // needs localization
|
|
||||||
BACKSLASH : 220, // needs localization
|
|
||||||
CLOSE_SQUARE_BRACKET: 221, // needs localization
|
|
||||||
WIN_KEY : 224,
|
|
||||||
MAC_FF_META : 224, // Firefox (Gecko) fires this for the meta key instead of 91
|
|
||||||
MAC_WK_CMD_LEFT : 91, // WebKit Left Command key fired, same as META
|
|
||||||
MAC_WK_CMD_RIGHT : 93, // WebKit Right Command key fired, different from META
|
|
||||||
WIN_IME : 229,
|
|
||||||
|
|
||||||
// We've seen users whose machines fire this keycode at regular one
|
|
||||||
// second intervals. The common thread among these users is that
|
|
||||||
// they're all using Dell Inspiron laptops, so we suspect that this
|
|
||||||
// indicates a hardware/bios problem.
|
|
||||||
// http://en.community.dell.com/support-forums/laptop/f/3518/p/19285957/19523128.aspx
|
|
||||||
PHANTOM : 255
|
|
||||||
};
|
|
||||||
// =====================================================================================================================
|
|
|
@ -1,40 +0,0 @@
|
||||||
|
|
||||||
namespace("LocalStore");
|
|
||||||
|
|
||||||
|
|
||||||
LocalStore.Set = function(class_name, class_id, variable_id, data)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (typeof(Storage) != "undefined")
|
|
||||||
{
|
|
||||||
var name = class_name + "_" + class_id + "_" + variable_id;
|
|
||||||
localStorage[name] = JSON.stringify(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
console.log("Local Storage Set Error: " + e.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LocalStore.Get = function(class_name, class_id, variable_id, default_data)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (typeof(Storage) != "undefined")
|
|
||||||
{
|
|
||||||
var name = class_name + "_" + class_id + "_" + variable_id;
|
|
||||||
var data = localStorage[name]
|
|
||||||
if (data)
|
|
||||||
return JSON.parse(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
console.log("Local Storage Get Error: " + e.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return default_data;
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
|
|
||||||
namespace("Mouse");
|
|
||||||
|
|
||||||
|
|
||||||
Mouse.State =(function()
|
|
||||||
{
|
|
||||||
function State(event)
|
|
||||||
{
|
|
||||||
// Get button press states
|
|
||||||
if (typeof event.buttons != "undefined")
|
|
||||||
{
|
|
||||||
// Firefox
|
|
||||||
this.Left = (event.buttons & 1) != 0;
|
|
||||||
this.Right = (event.buttons & 2) != 0;
|
|
||||||
this.Middle = (event.buttons & 4) != 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Chrome
|
|
||||||
this.Left = (event.button == 0);
|
|
||||||
this.Middle = (event.button == 1);
|
|
||||||
this.Right = (event.button == 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get page-relative mouse position
|
|
||||||
this.Position = DOM.Event.GetMousePosition(event);
|
|
||||||
|
|
||||||
// Get wheel delta
|
|
||||||
var delta = 0;
|
|
||||||
if (event.wheelDelta)
|
|
||||||
delta = event.wheelDelta / 120; // IE/Opera
|
|
||||||
else if (event.detail)
|
|
||||||
delta = -event.detail / 3; // Mozilla
|
|
||||||
this.WheelDelta = delta;
|
|
||||||
|
|
||||||
// Get the mouse position delta
|
|
||||||
// Requires Pointer Lock API support
|
|
||||||
this.PositionDelta = [
|
|
||||||
event.movementX || event.mozMovementX || event.webkitMovementX || 0,
|
|
||||||
event.movementY || event.mozMovementY || event.webkitMovementY || 0
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return State;
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Basic Pointer Lock API support
|
|
||||||
// https://developer.mozilla.org/en-US/docs/WebAPI/Pointer_Lock
|
|
||||||
// http://www.chromium.org/developers/design-documents/mouse-lock
|
|
||||||
//
|
|
||||||
// Note that API has not been standardised yet so browsers can implement functions with prefixes
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
Mouse.PointerLockSupported = function()
|
|
||||||
{
|
|
||||||
return 'pointerLockElement' in document || 'mozPointerLockElement' in document || 'webkitPointerLockElement' in document;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Mouse.RequestPointerLock = function(element)
|
|
||||||
{
|
|
||||||
element.requestPointerLock = element.requestPointerLock || element.mozRequestPointerLock || element.webkitRequestPointerLock;
|
|
||||||
if (element.requestPointerLock)
|
|
||||||
element.requestPointerLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Mouse.ExitPointerLock = function()
|
|
||||||
{
|
|
||||||
document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock || document.webkitExitPointerLock;
|
|
||||||
if (document.exitPointerLock)
|
|
||||||
document.exitPointerLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Can use this element to detect whether pointer lock is enabled (returns non-null)
|
|
||||||
Mouse.PointerLockElement = function()
|
|
||||||
{
|
|
||||||
return document.pointerLockElement || document.mozPointerLockElement || document.webkitPointerLockElement;
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
|
|
||||||
namespace("Hash");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JS Implementation of MurmurHash3 (r136) (as of May 20, 2011)
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:gary.court@gmail.com">Gary Court</a>
|
|
||||||
* @see http://github.com/garycourt/murmurhash-js
|
|
||||||
* @author <a href="mailto:aappleby@gmail.com">Austin Appleby</a>
|
|
||||||
* @see http://sites.google.com/site/murmurhash/
|
|
||||||
*
|
|
||||||
* @param {string} key ASCII only
|
|
||||||
* @param {number} seed Positive integer only
|
|
||||||
* @return {number} 32-bit positive integer hash
|
|
||||||
*/
|
|
||||||
|
|
||||||
Hash.Murmur3 = function(key, seed)
|
|
||||||
{
|
|
||||||
var remainder, bytes, h1, h1b, c1, c1b, c2, c2b, k1, i;
|
|
||||||
|
|
||||||
remainder = key.length & 3; // key.length % 4
|
|
||||||
bytes = key.length - remainder;
|
|
||||||
h1 = seed;
|
|
||||||
c1 = 0xcc9e2d51;
|
|
||||||
c2 = 0x1b873593;
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
while (i < bytes) {
|
|
||||||
k1 =
|
|
||||||
((key.charCodeAt(i) & 0xff)) |
|
|
||||||
((key.charCodeAt(++i) & 0xff) << 8) |
|
|
||||||
((key.charCodeAt(++i) & 0xff) << 16) |
|
|
||||||
((key.charCodeAt(++i) & 0xff) << 24);
|
|
||||||
++i;
|
|
||||||
|
|
||||||
k1 = ((((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16))) & 0xffffffff;
|
|
||||||
k1 = (k1 << 15) | (k1 >>> 17);
|
|
||||||
k1 = ((((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16))) & 0xffffffff;
|
|
||||||
|
|
||||||
h1 ^= k1;
|
|
||||||
h1 = (h1 << 13) | (h1 >>> 19);
|
|
||||||
h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff;
|
|
||||||
h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
k1 = 0;
|
|
||||||
|
|
||||||
switch (remainder) {
|
|
||||||
case 3: k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
|
|
||||||
case 2: k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
|
|
||||||
case 1: k1 ^= (key.charCodeAt(i) & 0xff);
|
|
||||||
|
|
||||||
k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
|
|
||||||
k1 = (k1 << 15) | (k1 >>> 17);
|
|
||||||
k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
|
|
||||||
h1 ^= k1;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 ^= key.length;
|
|
||||||
|
|
||||||
h1 ^= h1 >>> 16;
|
|
||||||
h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
|
|
||||||
h1 ^= h1 >>> 13;
|
|
||||||
h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff;
|
|
||||||
h1 ^= h1 >>> 16;
|
|
||||||
|
|
||||||
return h1 >>> 0;
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
|
|
||||||
namespace("WM");
|
|
||||||
|
|
||||||
|
|
||||||
WM.Button = (function()
|
|
||||||
{
|
|
||||||
var template_html = "<div class='Button notextsel'></div>";
|
|
||||||
|
|
||||||
|
|
||||||
function Button(text, x, y, opts)
|
|
||||||
{
|
|
||||||
this.OnClick = null;
|
|
||||||
this.Toggle = opts && opts.toggle;
|
|
||||||
|
|
||||||
this.Node = DOM.Node.CreateHTML(template_html);
|
|
||||||
|
|
||||||
// Set node dimensions
|
|
||||||
this.SetPosition(x, y);
|
|
||||||
if (opts && opts.w && opts.h)
|
|
||||||
this.SetSize(opts.w, opts.h);
|
|
||||||
|
|
||||||
// Override the default class name
|
|
||||||
if (opts && opts.class)
|
|
||||||
this.Node.className = opts.class;
|
|
||||||
|
|
||||||
this.SetText(text);
|
|
||||||
|
|
||||||
// Create the mouse press event handlers
|
|
||||||
DOM.Event.AddHandler(this.Node, "mousedown", Bind(OnMouseDown, this));
|
|
||||||
this.OnMouseOutDelegate = Bind(OnMouseUp, this, false);
|
|
||||||
this.OnMouseUpDelegate = Bind(OnMouseUp, this, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Button.prototype.SetPosition = function(x, y)
|
|
||||||
{
|
|
||||||
this.Position = [ x, y ];
|
|
||||||
DOM.Node.SetPosition(this.Node, this.Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Button.prototype.SetSize = function(w, h)
|
|
||||||
{
|
|
||||||
this.Size = [ w, h ];
|
|
||||||
DOM.Node.SetSize(this.Node, this.Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Button.prototype.SetText = function(text)
|
|
||||||
{
|
|
||||||
this.Node.innerHTML = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Button.prototype.SetOnClick = function(on_click)
|
|
||||||
{
|
|
||||||
this.OnClick = on_click;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Button.prototype.SetState = function(pressed)
|
|
||||||
{
|
|
||||||
if (pressed)
|
|
||||||
DOM.Node.AddClass(this.Node, "ButtonHeld");
|
|
||||||
else
|
|
||||||
DOM.Node.RemoveClass(this.Node, "ButtonHeld");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Button.prototype.ToggleState = function()
|
|
||||||
{
|
|
||||||
if (DOM.Node.HasClass(this.Node, "ButtonHeld"))
|
|
||||||
this.SetState(false);
|
|
||||||
else
|
|
||||||
this.SetState(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Button.prototype.IsPressed = function()
|
|
||||||
{
|
|
||||||
return DOM.Node.HasClass(this.Node, "ButtonHeld");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMouseDown(self, evt)
|
|
||||||
{
|
|
||||||
// Decide how to set the button state
|
|
||||||
if (self.Toggle)
|
|
||||||
self.ToggleState();
|
|
||||||
else
|
|
||||||
self.SetState(true);
|
|
||||||
|
|
||||||
// Activate release handlers
|
|
||||||
DOM.Event.AddHandler(self.Node, "mouseout", self.OnMouseOutDelegate);
|
|
||||||
DOM.Event.AddHandler(self.Node, "mouseup", self.OnMouseUpDelegate);
|
|
||||||
|
|
||||||
DOM.Event.StopAll(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMouseUp(self, confirm, evt)
|
|
||||||
{
|
|
||||||
if (confirm)
|
|
||||||
{
|
|
||||||
// Only release for non-toggles
|
|
||||||
if (!self.Toggle)
|
|
||||||
self.SetState(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Decide how to set the button state
|
|
||||||
if (self.Toggle)
|
|
||||||
self.ToggleState();
|
|
||||||
else
|
|
||||||
self.SetState(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove release handlers
|
|
||||||
DOM.Event.RemoveHandler(self.Node, "mouseout", self.OnMouseOutDelegate);
|
|
||||||
DOM.Event.RemoveHandler(self.Node, "mouseup", self.OnMouseUpDelegate);
|
|
||||||
|
|
||||||
// Call the click handler if this is a button press
|
|
||||||
if (confirm && self.OnClick)
|
|
||||||
self.OnClick(self);
|
|
||||||
|
|
||||||
DOM.Event.StopAll(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return Button;
|
|
||||||
})();
|
|
|
@ -1,237 +0,0 @@
|
||||||
|
|
||||||
namespace("WM");
|
|
||||||
|
|
||||||
|
|
||||||
WM.ComboBoxPopup = (function()
|
|
||||||
{
|
|
||||||
var body_template_html = "<div class='ComboBoxPopup'></div>";
|
|
||||||
|
|
||||||
var item_template_html = " \
|
|
||||||
<div class='ComboBoxPopupItem notextsel'> \
|
|
||||||
<div class='ComboBoxPopupItemText'></div> \
|
|
||||||
<div class='ComboBoxPopupItemIcon'><img src='BrowserLibImages/tick.gif'></div> \
|
|
||||||
<div style='clear:both'></div> \
|
|
||||||
</div>";
|
|
||||||
|
|
||||||
|
|
||||||
function ComboBoxPopup(combo_box)
|
|
||||||
{
|
|
||||||
this.ComboBox = combo_box;
|
|
||||||
this.ParentNode = combo_box.Node;
|
|
||||||
this.ValueNodes = [ ];
|
|
||||||
|
|
||||||
// Create the template node
|
|
||||||
this.Node = DOM.Node.CreateHTML(body_template_html);
|
|
||||||
|
|
||||||
DOM.Event.AddHandler(this.Node, "mousedown", Bind(SelectItem, this));
|
|
||||||
this.CancelDelegate = Bind(this, "Cancel");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ComboBoxPopup.prototype.SetValues = function(values)
|
|
||||||
{
|
|
||||||
// Clear existing values
|
|
||||||
this.Node.innerHTML = "";
|
|
||||||
|
|
||||||
// Generate HTML nodes for each value
|
|
||||||
this.ValueNodes = [ ];
|
|
||||||
for (var i in values)
|
|
||||||
{
|
|
||||||
var item_node = DOM.Node.CreateHTML(item_template_html);
|
|
||||||
var text_node = DOM.Node.FindWithClass(item_node, "ComboBoxPopupItemText");
|
|
||||||
|
|
||||||
item_node.Value = values[i];
|
|
||||||
text_node.innerHTML = values[i];
|
|
||||||
|
|
||||||
this.Node.appendChild(item_node);
|
|
||||||
this.ValueNodes.push(item_node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ComboBoxPopup.prototype.Show = function(selection_index)
|
|
||||||
{
|
|
||||||
// Initially match the position of the parent node
|
|
||||||
var pos = DOM.Node.GetPosition(this.ParentNode);
|
|
||||||
DOM.Node.SetPosition(this.Node, pos);
|
|
||||||
|
|
||||||
// Take the width/z-index from the parent node
|
|
||||||
this.Node.style.width = this.ParentNode.offsetWidth;
|
|
||||||
this.Node.style.zIndex = this.ParentNode.style.zIndex + 1;
|
|
||||||
|
|
||||||
// Setup event handlers
|
|
||||||
DOM.Event.AddHandler(document.body, "mousedown", this.CancelDelegate);
|
|
||||||
|
|
||||||
// Show the popup so that the HTML layout engine kicks in before
|
|
||||||
// the layout info is used below
|
|
||||||
this.ParentNode.appendChild(this.Node);
|
|
||||||
|
|
||||||
// Show/hide the tick image based on which node is selected
|
|
||||||
for (var i in this.ValueNodes)
|
|
||||||
{
|
|
||||||
var node = this.ValueNodes[i];
|
|
||||||
var icon_node = DOM.Node.FindWithClass(node, "ComboBoxPopupItemIcon");
|
|
||||||
|
|
||||||
if (i == selection_index)
|
|
||||||
{
|
|
||||||
icon_node.style.display = "block";
|
|
||||||
|
|
||||||
// Also, shift the popup up so that the mouse is over the selected item and is highlighted
|
|
||||||
var item_pos = DOM.Node.GetPosition(this.ValueNodes[selection_index]);
|
|
||||||
var diff_pos = [ item_pos[0] - pos[0], item_pos[1] - pos[1] ];
|
|
||||||
pos = [ pos[0] - diff_pos[0], pos[1] - diff_pos[1] ];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
icon_node.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DOM.Node.SetPosition(this.Node, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ComboBoxPopup.prototype.Hide = function()
|
|
||||||
{
|
|
||||||
DOM.Event.RemoveHandler(document.body, "mousedown", this.CancelDelegate);
|
|
||||||
this.ParentNode.removeChild(this.Node);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function SelectItem(self, evt)
|
|
||||||
{
|
|
||||||
// Search for which item node is being clicked on
|
|
||||||
var node = DOM.Event.GetNode(evt);
|
|
||||||
for (var i in self.ValueNodes)
|
|
||||||
{
|
|
||||||
var value_node = self.ValueNodes[i];
|
|
||||||
if (DOM.Node.Contains(node, value_node))
|
|
||||||
{
|
|
||||||
// Set the value on the combo box
|
|
||||||
self.ComboBox.SetValue(value_node.Value);
|
|
||||||
self.Hide();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function Cancel(self, evt)
|
|
||||||
{
|
|
||||||
// Don't cancel if the mouse up is anywhere on the popup or combo box
|
|
||||||
var node = DOM.Event.GetNode(evt);
|
|
||||||
if (!DOM.Node.Contains(node, self.Node) &&
|
|
||||||
!DOM.Node.Contains(node, self.ParentNode))
|
|
||||||
{
|
|
||||||
self.Hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
DOM.Event.StopAll(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return ComboBoxPopup;
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
WM.ComboBox = (function()
|
|
||||||
{
|
|
||||||
var template_html = " \
|
|
||||||
<div class='ComboBox'> \
|
|
||||||
<div class='ComboBoxText notextsel'></div> \
|
|
||||||
<div class='ComboBoxIcon'><img src='BrowserLibImages/up_down.gif'></div> \
|
|
||||||
<div style='clear:both'></div> \
|
|
||||||
</div>";
|
|
||||||
|
|
||||||
|
|
||||||
function ComboBox()
|
|
||||||
{
|
|
||||||
this.OnChange = null;
|
|
||||||
|
|
||||||
// Create the template node and locate key nodes
|
|
||||||
this.Node = DOM.Node.CreateHTML(template_html);
|
|
||||||
this.TextNode = DOM.Node.FindWithClass(this.Node, "ComboBoxText");
|
|
||||||
|
|
||||||
// Create a reusable popup
|
|
||||||
this.Popup = new WM.ComboBoxPopup(this);
|
|
||||||
|
|
||||||
// Set an empty set of values
|
|
||||||
this.SetValues([]);
|
|
||||||
this.SetValue("<empty>");
|
|
||||||
|
|
||||||
// Create the mouse press event handlers
|
|
||||||
DOM.Event.AddHandler(this.Node, "mousedown", Bind(OnMouseDown, this));
|
|
||||||
this.OnMouseOutDelegate = Bind(OnMouseUp, this, false);
|
|
||||||
this.OnMouseUpDelegate = Bind(OnMouseUp, this, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ComboBox.prototype.SetOnChange = function(on_change)
|
|
||||||
{
|
|
||||||
this.OnChange = on_change;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ComboBox.prototype.SetValues = function(values)
|
|
||||||
{
|
|
||||||
this.Values = values;
|
|
||||||
this.Popup.SetValues(values);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ComboBox.prototype.SetValue = function(value)
|
|
||||||
{
|
|
||||||
// Set the value and its HTML rep
|
|
||||||
var old_value = this.Value;
|
|
||||||
this.Value = value;
|
|
||||||
this.TextNode.innerHTML = value;
|
|
||||||
|
|
||||||
// Call change handler
|
|
||||||
if (this.OnChange)
|
|
||||||
this.OnChange(value, old_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ComboBox.prototype.GetValue = function()
|
|
||||||
{
|
|
||||||
return this.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMouseDown(self, evt)
|
|
||||||
{
|
|
||||||
// If this check isn't made, the click will trigger from the popup, too
|
|
||||||
var node = DOM.Event.GetNode(evt);
|
|
||||||
if (DOM.Node.Contains(node, self.Node))
|
|
||||||
{
|
|
||||||
// Add the depression class and activate release handlers
|
|
||||||
DOM.Node.AddClass(self.Node, "ComboBoxPressed");
|
|
||||||
DOM.Event.AddHandler(self.Node, "mouseout", self.OnMouseOutDelegate);
|
|
||||||
DOM.Event.AddHandler(self.Node, "mouseup", self.OnMouseUpDelegate);
|
|
||||||
|
|
||||||
DOM.Event.StopAll(evt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMouseUp(self, confirm, evt)
|
|
||||||
{
|
|
||||||
// Remove depression class and remove release handlers
|
|
||||||
DOM.Node.RemoveClass(self.Node, "ComboBoxPressed");
|
|
||||||
DOM.Event.RemoveHandler(self.Node, "mouseout", self.OnMouseOutDelegate);
|
|
||||||
DOM.Event.RemoveHandler(self.Node, "mouseup", self.OnMouseUpDelegate);
|
|
||||||
|
|
||||||
// If this is a confirmed press and there are some values in the list, show the popup
|
|
||||||
if (confirm && self.Values.length > 0)
|
|
||||||
{
|
|
||||||
var selection_index = self.Values.indexOf(self.Value);
|
|
||||||
self.Popup.Show(selection_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
DOM.Event.StopAll(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return ComboBox;
|
|
||||||
})();
|
|
|
@ -1,48 +0,0 @@
|
||||||
|
|
||||||
namespace("WM");
|
|
||||||
|
|
||||||
|
|
||||||
WM.Container = (function()
|
|
||||||
{
|
|
||||||
var template_html = "<div class='Container'></div>";
|
|
||||||
|
|
||||||
|
|
||||||
function Container(x, y, w, h)
|
|
||||||
{
|
|
||||||
// Create a simple container node
|
|
||||||
this.Node = DOM.Node.CreateHTML(template_html);
|
|
||||||
this.SetPosition(x, y);
|
|
||||||
this.SetSize(w, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Container.prototype.SetPosition = function(x, y)
|
|
||||||
{
|
|
||||||
this.Position = [ x, y ];
|
|
||||||
DOM.Node.SetPosition(this.Node, this.Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Container.prototype.SetSize = function(w, h)
|
|
||||||
{
|
|
||||||
this.Size = [ w, h ];
|
|
||||||
DOM.Node.SetSize(this.Node, this.Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Container.prototype.AddControlNew = function(control)
|
|
||||||
{
|
|
||||||
control.ParentNode = this.Node;
|
|
||||||
this.Node.appendChild(control.Node);
|
|
||||||
return control;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Container.prototype.ClearControls = function()
|
|
||||||
{
|
|
||||||
this.Node.innerHTML = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return Container;
|
|
||||||
})();
|
|
|
@ -1,119 +0,0 @@
|
||||||
|
|
||||||
namespace("WM");
|
|
||||||
|
|
||||||
|
|
||||||
WM.EditBox = (function()
|
|
||||||
{
|
|
||||||
var template_html = " \
|
|
||||||
<div class='EditBoxContainer'> \
|
|
||||||
<div class='EditBoxLabel'>Label</div> \
|
|
||||||
<input class='EditBox'> \
|
|
||||||
</div>";
|
|
||||||
|
|
||||||
|
|
||||||
function EditBox(x, y, w, h, label, text)
|
|
||||||
{
|
|
||||||
this.ChangeHandler = null;
|
|
||||||
|
|
||||||
// Create node and locate its internal nodes
|
|
||||||
this.Node = DOM.Node.CreateHTML(template_html);
|
|
||||||
this.LabelNode = DOM.Node.FindWithClass(this.Node, "EditBoxLabel");
|
|
||||||
this.EditNode = DOM.Node.FindWithClass(this.Node, "EditBox");
|
|
||||||
|
|
||||||
// Set label and value
|
|
||||||
this.LabelNode.innerHTML = label;
|
|
||||||
this.SetValue(text);
|
|
||||||
|
|
||||||
this.SetPosition(x, y);
|
|
||||||
this.SetSize(w, h);
|
|
||||||
|
|
||||||
this.PreviousValue = "";
|
|
||||||
|
|
||||||
// Hook up the event handlers
|
|
||||||
DOM.Event.AddHandler(this.EditNode, "focus", Bind(OnFocus, this));
|
|
||||||
DOM.Event.AddHandler(this.EditNode, "keypress", Bind(OnKeyPress, this));
|
|
||||||
DOM.Event.AddHandler(this.EditNode, "keydown", Bind(OnKeyDown, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
EditBox.prototype.SetPosition = function(x, y)
|
|
||||||
{
|
|
||||||
this.Position = [ x, y ];
|
|
||||||
DOM.Node.SetPosition(this.Node, this.Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
EditBox.prototype.SetSize = function(w, h)
|
|
||||||
{
|
|
||||||
this.Size = [ w, h ];
|
|
||||||
DOM.Node.SetSize(this.EditNode, this.Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
EditBox.prototype.SetChangeHandler = function(handler)
|
|
||||||
{
|
|
||||||
this.ChangeHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
EditBox.prototype.SetValue = function(value)
|
|
||||||
{
|
|
||||||
if (this.EditNode)
|
|
||||||
this.EditNode.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
EditBox.prototype.GetValue = function()
|
|
||||||
{
|
|
||||||
if (this.EditNode)
|
|
||||||
return this.EditNode.value;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
EditBox.prototype.LoseFocus = function()
|
|
||||||
{
|
|
||||||
if (this.EditNode)
|
|
||||||
this.EditNode.blur();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnFocus(self, evt)
|
|
||||||
{
|
|
||||||
// Backup on focus
|
|
||||||
self.PreviousValue = self.EditNode.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnKeyPress(self, evt)
|
|
||||||
{
|
|
||||||
// Allow enter to confirm the text only when there's data
|
|
||||||
if (evt.keyCode == 13 && self.EditNode.value != "" && self.ChangeHandler)
|
|
||||||
{
|
|
||||||
var focus = self.ChangeHandler(self.EditNode);
|
|
||||||
if (!focus)
|
|
||||||
self.EditNode.blur();
|
|
||||||
self.PreviousValue = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnKeyDown(self, evt)
|
|
||||||
{
|
|
||||||
// Allow escape to cancel any text changes
|
|
||||||
if (evt.keyCode == 27)
|
|
||||||
{
|
|
||||||
// On initial edit of the input, escape should NOT replace with the empty string
|
|
||||||
if (self.PreviousValue != "")
|
|
||||||
{
|
|
||||||
self.EditNode.value = self.PreviousValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.EditNode.blur();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return EditBox;
|
|
||||||
})();
|
|
|
@ -1,248 +0,0 @@
|
||||||
|
|
||||||
namespace("WM");
|
|
||||||
|
|
||||||
|
|
||||||
WM.GridRows = (function()
|
|
||||||
{
|
|
||||||
function GridRows(parent_object)
|
|
||||||
{
|
|
||||||
this.ParentObject = parent_object;
|
|
||||||
|
|
||||||
// Array of rows in the order they were added
|
|
||||||
this.Rows = [ ];
|
|
||||||
|
|
||||||
// Collection of custom row indexes for fast lookup
|
|
||||||
this.Indexes = { };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
GridRows.prototype.AddIndex = function(cell_field_name)
|
|
||||||
{
|
|
||||||
var index = { };
|
|
||||||
|
|
||||||
// Go through existing rows and add to the index
|
|
||||||
for (var i in this.Rows)
|
|
||||||
{
|
|
||||||
var row = this.Rows[i];
|
|
||||||
if (cell_field_name in row.CellData)
|
|
||||||
{
|
|
||||||
var cell_field = row.CellData[cell_field_name];
|
|
||||||
index[cell_field] = row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Indexes[cell_field_name] = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
GridRows.prototype.ClearIndex = function(index_name)
|
|
||||||
{
|
|
||||||
this.Indexes[index_name] = { };
|
|
||||||
}
|
|
||||||
|
|
||||||
GridRows.prototype.AddRowToIndex = function(index_name, cell_data, row)
|
|
||||||
{
|
|
||||||
this.Indexes[index_name][cell_data] = row;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
GridRows.prototype.Add = function(cell_data, row_classes, cell_classes)
|
|
||||||
{
|
|
||||||
var row = new WM.GridRow(this.ParentObject, cell_data, row_classes, cell_classes);
|
|
||||||
this.Rows.push(row);
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
GridRows.prototype.GetBy = function(cell_field_name, cell_data)
|
|
||||||
{
|
|
||||||
var index = this.Indexes[cell_field_name];
|
|
||||||
return index[cell_data];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
GridRows.prototype.Clear = function()
|
|
||||||
{
|
|
||||||
// Remove all node references from the parent
|
|
||||||
for (var i in this.Rows)
|
|
||||||
{
|
|
||||||
var row = this.Rows[i];
|
|
||||||
row.Parent.BodyNode.removeChild(row.Node);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear all indexes
|
|
||||||
for (var i in this.Indexes)
|
|
||||||
this.Indexes[i] = { };
|
|
||||||
|
|
||||||
this.Rows = [ ];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return GridRows;
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
WM.GridRow = (function()
|
|
||||||
{
|
|
||||||
var template_html = "<div class='GridRow'></div>";
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// 'cell_data' is an object with a variable number of fields.
|
|
||||||
// Any fields prefixed with an underscore are hidden.
|
|
||||||
//
|
|
||||||
function GridRow(parent, cell_data, row_classes, cell_classes)
|
|
||||||
{
|
|
||||||
// Setup data
|
|
||||||
this.Parent = parent;
|
|
||||||
this.IsOpen = true;
|
|
||||||
this.AnimHandle = null;
|
|
||||||
this.Rows = new WM.GridRows(this);
|
|
||||||
this.CellData = cell_data;
|
|
||||||
this.CellNodes = { }
|
|
||||||
|
|
||||||
// Create the main row node
|
|
||||||
this.Node = DOM.Node.CreateHTML(template_html);
|
|
||||||
if (row_classes)
|
|
||||||
DOM.Node.AddClass(this.Node, row_classes);
|
|
||||||
|
|
||||||
// Embed a pointer to the row in the root node so that it can be clicked
|
|
||||||
this.Node.GridRow = this;
|
|
||||||
|
|
||||||
// Create nodes for each required cell
|
|
||||||
for (var attr in this.CellData)
|
|
||||||
{
|
|
||||||
if (this.CellData.hasOwnProperty(attr))
|
|
||||||
{
|
|
||||||
var data = this.CellData[attr];
|
|
||||||
|
|
||||||
// Update any grid row index references
|
|
||||||
if (attr in parent.Rows.Indexes)
|
|
||||||
parent.Rows.AddRowToIndex(attr, data, this);
|
|
||||||
|
|
||||||
// Hide any cells with underscore prefixes
|
|
||||||
if (attr[0] == "_")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Create a node for the cell and add any custom classes
|
|
||||||
var node = DOM.Node.AppendHTML(this.Node, "<div class='GridRowCell'></div>");
|
|
||||||
if (cell_classes && attr in cell_classes)
|
|
||||||
DOM.Node.AddClass(node, cell_classes[attr]);
|
|
||||||
this.CellNodes[attr] = node;
|
|
||||||
|
|
||||||
// If this is a Window Control, add its node to the cell
|
|
||||||
if (data instanceof Object && "Node" in data && DOM.Node.IsNode(data.Node))
|
|
||||||
{
|
|
||||||
data.ParentNode = node;
|
|
||||||
node.appendChild(data.Node);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Otherwise just assign the data as text
|
|
||||||
node.innerHTML = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the body node for any children
|
|
||||||
if (!this.Parent.BodyNode)
|
|
||||||
this.Parent.BodyNode = DOM.Node.AppendHTML(this.Parent.Node, "<div class='GridRowBody'></div>");
|
|
||||||
|
|
||||||
// Add the row to the parent
|
|
||||||
this.Parent.BodyNode.appendChild(this.Node);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
GridRow.prototype.Open = function()
|
|
||||||
{
|
|
||||||
// Don't allow open while animating
|
|
||||||
if (this.AnimHandle == null || this.AnimHandle.Complete)
|
|
||||||
{
|
|
||||||
this.IsOpen = true;
|
|
||||||
|
|
||||||
// Kick off open animation
|
|
||||||
var node = this.BodyNode;
|
|
||||||
this.AnimHandle = Anim.Animate(
|
|
||||||
function (val) { DOM.Node.SetHeight(node, val) },
|
|
||||||
0, this.Height, 0.2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
GridRow.prototype.Close = function()
|
|
||||||
{
|
|
||||||
// Don't allow close while animating
|
|
||||||
if (this.AnimHandle == null || this.AnimHandle.Complete)
|
|
||||||
{
|
|
||||||
this.IsOpen = false;
|
|
||||||
|
|
||||||
// Record height for the next open request
|
|
||||||
this.Height = this.BodyNode.offsetHeight;
|
|
||||||
|
|
||||||
// Kick off close animation
|
|
||||||
var node = this.BodyNode;
|
|
||||||
this.AnimHandle = Anim.Animate(
|
|
||||||
function (val) { DOM.Node.SetHeight(node, val) },
|
|
||||||
this.Height, 0, 0.2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
GridRow.prototype.Toggle = function()
|
|
||||||
{
|
|
||||||
if (this.IsOpen)
|
|
||||||
this.Close();
|
|
||||||
else
|
|
||||||
this.Open();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return GridRow;
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
WM.Grid = (function()
|
|
||||||
{
|
|
||||||
var template_html = " \
|
|
||||||
<div class='Grid'> \
|
|
||||||
<div class='GridBody'></div> \
|
|
||||||
</div>";
|
|
||||||
|
|
||||||
|
|
||||||
function Grid()
|
|
||||||
{
|
|
||||||
this.Rows = new WM.GridRows(this);
|
|
||||||
|
|
||||||
this.Node = DOM.Node.CreateHTML(template_html);
|
|
||||||
this.BodyNode = DOM.Node.FindWithClass(this.Node, "GridBody");
|
|
||||||
|
|
||||||
DOM.Event.AddHandler(this.Node, "dblclick", OnDblClick);
|
|
||||||
|
|
||||||
var mouse_wheel_event = (/Firefox/i.test(navigator.userAgent)) ? "DOMMouseScroll" : "mousewheel";
|
|
||||||
DOM.Event.AddHandler(this.Node, mouse_wheel_event, Bind(OnMouseScroll, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
function OnDblClick(evt)
|
|
||||||
{
|
|
||||||
// Clicked on a header?
|
|
||||||
var node = DOM.Event.GetNode(evt);
|
|
||||||
if (DOM.Node.HasClass(node, "GridRowName"))
|
|
||||||
{
|
|
||||||
// Toggle rows open/close
|
|
||||||
var row = node.parentNode.GridRow;
|
|
||||||
if (row)
|
|
||||||
row.Toggle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMouseScroll(self, evt)
|
|
||||||
{
|
|
||||||
var mouse_state = new Mouse.State(evt);
|
|
||||||
self.Node.scrollTop -= mouse_state.WheelDelta * 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return Grid;
|
|
||||||
})();
|
|
|
@ -1,31 +0,0 @@
|
||||||
|
|
||||||
namespace("WM");
|
|
||||||
|
|
||||||
|
|
||||||
WM.Label = (function()
|
|
||||||
{
|
|
||||||
var template_html = "<div class='Label'></div>";
|
|
||||||
|
|
||||||
|
|
||||||
function Label(x, y, text)
|
|
||||||
{
|
|
||||||
// Create the node
|
|
||||||
this.Node = DOM.Node.CreateHTML(template_html);
|
|
||||||
|
|
||||||
// Allow position to be optional
|
|
||||||
if (x != null && y != null)
|
|
||||||
DOM.Node.SetPosition(this.Node, [x, y]);
|
|
||||||
|
|
||||||
this.SetText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Label.prototype.SetText = function(text)
|
|
||||||
{
|
|
||||||
if (text != null)
|
|
||||||
this.Node.innerHTML = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return Label;
|
|
||||||
})();
|
|
|
@ -1,352 +0,0 @@
|
||||||
|
|
||||||
namespace("WM");
|
|
||||||
|
|
||||||
|
|
||||||
WM.Treeview = (function()
|
|
||||||
{
|
|
||||||
var Margin = 10;
|
|
||||||
|
|
||||||
|
|
||||||
var tree_template_html = " \
|
|
||||||
<div class='Treeview'> \
|
|
||||||
<div class='TreeviewItemChildren' style='width:90%;float:left'></div> \
|
|
||||||
<div class='TreeviewScrollbarInset'> \
|
|
||||||
<div class='TreeviewScrollbar'></div> \
|
|
||||||
</div> \
|
|
||||||
<div style='clear:both'></div> \
|
|
||||||
</div>";
|
|
||||||
|
|
||||||
|
|
||||||
var item_template_html = " \
|
|
||||||
<div class='TreeViewItem basicfont notextsel'> \
|
|
||||||
<img src='' class='TreeviewItemImage'> \
|
|
||||||
<div class='TreeviewItemText'></div> \
|
|
||||||
<div style='clear:both'></div> \
|
|
||||||
<div class='TreeviewItemChildren'></div> \
|
|
||||||
<div style='clear:both'></div> \
|
|
||||||
</div>";
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Remove parent_node (required for stuff that doesn't use the WM yet)
|
|
||||||
function Treeview(x, y, width, height, parent_node)
|
|
||||||
{
|
|
||||||
// Cache initialisation options
|
|
||||||
this.ParentNode = parent_node;
|
|
||||||
this.Position = [ x, y ];
|
|
||||||
this.Size = [ width, height ];
|
|
||||||
|
|
||||||
this.Node = null;
|
|
||||||
this.ScrollbarNode = null;
|
|
||||||
this.SelectedItem = null;
|
|
||||||
this.ContentsNode = null;
|
|
||||||
|
|
||||||
// Setup options
|
|
||||||
this.HighlightOnHover = false;
|
|
||||||
this.EnableScrollbar = true;
|
|
||||||
this.HorizontalLayoutDepth = 1;
|
|
||||||
|
|
||||||
// Generate an empty tree
|
|
||||||
this.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Treeview.prototype.SetHighlightOnHover = function(highlight)
|
|
||||||
{
|
|
||||||
this.HighlightOnHover = highlight;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Treeview.prototype.SetEnableScrollbar = function(enable)
|
|
||||||
{
|
|
||||||
this.EnableScrollbar = enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Treeview.prototype.SetHorizontalLayoutDepth = function(depth)
|
|
||||||
{
|
|
||||||
this.HorizontalLayoutDepth = depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Treeview.prototype.SetNodeSelectedHandler = function(handler)
|
|
||||||
{
|
|
||||||
this.NodeSelectedHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Treeview.prototype.Clear = function()
|
|
||||||
{
|
|
||||||
this.RootItem = new WM.TreeviewItem(this, null, null, null, null);
|
|
||||||
this.GenerateHTML();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Treeview.prototype.Root = function()
|
|
||||||
{
|
|
||||||
return this.RootItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Treeview.prototype.ClearSelection = function()
|
|
||||||
{
|
|
||||||
if (this.SelectedItem != null)
|
|
||||||
{
|
|
||||||
DOM.Node.RemoveClass(this.SelectedItem.Node, "TreeviewItemSelected");
|
|
||||||
this.SelectedItem = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Treeview.prototype.SelectItem = function(item, mouse_pos)
|
|
||||||
{
|
|
||||||
// Notify the select handler
|
|
||||||
if (this.NodeSelectedHandler)
|
|
||||||
this.NodeSelectedHandler(item.Node, this.SelectedItem, item, mouse_pos);
|
|
||||||
|
|
||||||
// Remove highlight from the old selection
|
|
||||||
this.ClearSelection();
|
|
||||||
|
|
||||||
// Swap in new selection and apply highlight
|
|
||||||
this.SelectedItem = item;
|
|
||||||
DOM.Node.AddClass(this.SelectedItem.Node, "TreeviewItemSelected");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Treeview.prototype.GenerateHTML = function()
|
|
||||||
{
|
|
||||||
// Clone the template and locate important nodes
|
|
||||||
var old_node = this.Node;
|
|
||||||
this.Node = DOM.Node.CreateHTML(tree_template_html);
|
|
||||||
this.ChildrenNode = DOM.Node.FindWithClass(this.Node, "TreeviewItemChildren");
|
|
||||||
this.ScrollbarNode = DOM.Node.FindWithClass(this.Node, "TreeviewScrollbar");
|
|
||||||
|
|
||||||
DOM.Node.SetPosition(this.Node, this.Position);
|
|
||||||
DOM.Node.SetSize(this.Node, this.Size);
|
|
||||||
|
|
||||||
// Generate the contents of the treeview
|
|
||||||
GenerateTree(this, this.ChildrenNode, this.RootItem.Children, 0);
|
|
||||||
|
|
||||||
// Cross-browser (?) means of adding a mouse wheel handler
|
|
||||||
var mouse_wheel_event = (/Firefox/i.test(navigator.userAgent)) ? "DOMMouseScroll" : "mousewheel";
|
|
||||||
DOM.Event.AddHandler(this.Node, mouse_wheel_event, Bind(OnMouseScroll, this));
|
|
||||||
|
|
||||||
DOM.Event.AddHandler(this.Node, "dblclick", Bind(OnMouseDoubleClick, this));
|
|
||||||
DOM.Event.AddHandler(this.Node, "mousedown", Bind(OnMouseDown, this));
|
|
||||||
DOM.Event.AddHandler(this.Node, "mouseup", OnMouseUp);
|
|
||||||
|
|
||||||
// Swap in the newly generated control node if it's already been attached to a parent
|
|
||||||
if (old_node && old_node.parentNode)
|
|
||||||
{
|
|
||||||
old_node.parentNode.removeChild(old_node);
|
|
||||||
this.ParentNode.appendChild(this.Node);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.EnableScrollbar)
|
|
||||||
{
|
|
||||||
this.UpdateScrollbar();
|
|
||||||
DOM.Event.AddHandler(this.ScrollbarNode, "mousedown", Bind(OnMouseDown_Scrollbar, this));
|
|
||||||
DOM.Event.AddHandler(this.ScrollbarNode, "mouseup", Bind(OnMouseUp_Scrollbar, this));
|
|
||||||
DOM.Event.AddHandler(this.ScrollbarNode, "mouseout", Bind(OnMouseUp_Scrollbar, this));
|
|
||||||
DOM.Event.AddHandler(this.ScrollbarNode, "mousemove", Bind(OnMouseMove_Scrollbar, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DOM.Node.Hide(DOM.Node.FindWithClass(this.Node, "TreeviewScrollbarInset"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Treeview.prototype.UpdateScrollbar = function()
|
|
||||||
{
|
|
||||||
if (!this.EnableScrollbar)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var scrollbar_scale = Math.min((this.Node.offsetHeight - Margin * 2) / this.ChildrenNode.offsetHeight, 1);
|
|
||||||
this.ScrollbarNode.style.height = parseInt(scrollbar_scale * 100) + "%";
|
|
||||||
|
|
||||||
// Shift the scrollbar container along with the parent window
|
|
||||||
this.ScrollbarNode.parentNode.style.top = this.Node.scrollTop;
|
|
||||||
|
|
||||||
var scroll_fraction = this.Node.scrollTop / (this.Node.scrollHeight - this.Node.offsetHeight);
|
|
||||||
var max_height = this.Node.offsetHeight - Margin;
|
|
||||||
var max_scrollbar_offset = max_height - this.ScrollbarNode.offsetHeight;
|
|
||||||
var scrollbar_offset = scroll_fraction * max_scrollbar_offset;
|
|
||||||
this.ScrollbarNode.style.top = scrollbar_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function GenerateTree(self, parent_node, items, depth)
|
|
||||||
{
|
|
||||||
if (items.length == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
for (var i in items)
|
|
||||||
{
|
|
||||||
var item = items[i];
|
|
||||||
|
|
||||||
// Create the node for this item and locate important nodes
|
|
||||||
var node = DOM.Node.CreateHTML(item_template_html);
|
|
||||||
var img = DOM.Node.FindWithClass(node, "TreeviewItemImage");
|
|
||||||
var text = DOM.Node.FindWithClass(node, "TreeviewItemText");
|
|
||||||
var children = DOM.Node.FindWithClass(node, "TreeviewItemChildren");
|
|
||||||
|
|
||||||
// Attach the item to the node
|
|
||||||
node.TreeviewItem = item;
|
|
||||||
item.Node = node;
|
|
||||||
|
|
||||||
// Add the class which highlights selection on hover
|
|
||||||
if (self.HighlightOnHover)
|
|
||||||
DOM.Node.AddClass(node, "TreeviewItemHover");
|
|
||||||
|
|
||||||
// Instruct the children to wrap around
|
|
||||||
if (depth >= self.HorizontalLayoutDepth)
|
|
||||||
node.style.cssFloat = "left";
|
|
||||||
|
|
||||||
if (item.OpenImage == null || item.CloseImage == null)
|
|
||||||
{
|
|
||||||
// If there no images, remove the image node
|
|
||||||
node.removeChild(img);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Set the image source to open
|
|
||||||
img.src = item.OpenImage.src;
|
|
||||||
img.style.width = item.OpenImage.width;
|
|
||||||
img.style.height = item.OpenImage.height;
|
|
||||||
item.ImageNode = img;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup the text to display
|
|
||||||
text.innerHTML = item.Label;
|
|
||||||
|
|
||||||
// Add the div to the parent and recurse into children
|
|
||||||
parent_node.appendChild(node);
|
|
||||||
GenerateTree(self, children, item.Children, depth + 1);
|
|
||||||
item.ChildrenNode = children;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the wrap-around
|
|
||||||
if (depth >= self.HorizontalLayoutDepth)
|
|
||||||
DOM.Node.AppendClearFloat(parent_node.parentNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMouseScroll(self, evt)
|
|
||||||
{
|
|
||||||
// Get mouse wheel movement
|
|
||||||
var delta = evt.detail ? evt.detail * -1 : evt.wheelDelta;
|
|
||||||
delta *= 8;
|
|
||||||
|
|
||||||
// Scroll the main window with wheel movement and clamp
|
|
||||||
self.Node.scrollTop -= delta;
|
|
||||||
self.Node.scrollTop = Math.min(self.Node.scrollTop, (self.ChildrenNode.offsetHeight - self.Node.offsetHeight) + Margin * 2);
|
|
||||||
|
|
||||||
self.UpdateScrollbar();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMouseDoubleClick(self, evt)
|
|
||||||
{
|
|
||||||
DOM.Event.StopDefaultAction(evt);
|
|
||||||
|
|
||||||
// Get the tree view item being clicked, if any
|
|
||||||
var node = DOM.Event.GetNode(evt);
|
|
||||||
var tvitem = GetTreeviewItemFromNode(self, node);
|
|
||||||
if (tvitem == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (tvitem.Children.length)
|
|
||||||
tvitem.Toggle();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMouseDown(self, evt)
|
|
||||||
{
|
|
||||||
DOM.Event.StopDefaultAction(evt);
|
|
||||||
|
|
||||||
// Get the tree view item being clicked, if any
|
|
||||||
var node = DOM.Event.GetNode(evt);
|
|
||||||
var tvitem = GetTreeviewItemFromNode(self, node);
|
|
||||||
if (tvitem == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// If clicking on the image, expand any children
|
|
||||||
if (node.tagName == "IMG" && tvitem.Children.length)
|
|
||||||
{
|
|
||||||
tvitem.Toggle();
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var mouse_pos = DOM.Event.GetMousePosition(evt);
|
|
||||||
self.SelectItem(tvitem, mouse_pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMouseUp(evt)
|
|
||||||
{
|
|
||||||
// Event handler used merely to stop events bubbling up to containers
|
|
||||||
DOM.Event.StopPropagation(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMouseDown_Scrollbar(self, evt)
|
|
||||||
{
|
|
||||||
self.ScrollbarHeld = true;
|
|
||||||
|
|
||||||
// Cache the mouse height relative to the scrollbar
|
|
||||||
self.LastY = evt.clientY;
|
|
||||||
self.ScrollY = self.Node.scrollTop;
|
|
||||||
|
|
||||||
DOM.Node.AddClass(self.ScrollbarNode, "TreeviewScrollbarHeld");
|
|
||||||
DOM.Event.StopDefaultAction(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMouseUp_Scrollbar(self, evt)
|
|
||||||
{
|
|
||||||
self.ScrollbarHeld = false;
|
|
||||||
DOM.Node.RemoveClass(self.ScrollbarNode, "TreeviewScrollbarHeld");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function OnMouseMove_Scrollbar(self, evt)
|
|
||||||
{
|
|
||||||
if (self.ScrollbarHeld)
|
|
||||||
{
|
|
||||||
var delta_y = evt.clientY - self.LastY;
|
|
||||||
self.LastY = evt.clientY;
|
|
||||||
|
|
||||||
var max_height = self.Node.offsetHeight - Margin;
|
|
||||||
var max_scrollbar_offset = max_height - self.ScrollbarNode.offsetHeight;
|
|
||||||
var max_contents_scroll = self.Node.scrollHeight - self.Node.offsetHeight;
|
|
||||||
var scale = max_contents_scroll / max_scrollbar_offset;
|
|
||||||
|
|
||||||
// Increment the local float variable and assign, as scrollTop is of type int
|
|
||||||
self.ScrollY += delta_y * scale;
|
|
||||||
self.Node.scrollTop = self.ScrollY;
|
|
||||||
self.Node.scrollTop = Math.min(self.Node.scrollTop, (self.ChildrenNode.offsetHeight - self.Node.offsetHeight) + Margin * 2);
|
|
||||||
|
|
||||||
self.UpdateScrollbar();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function GetTreeviewItemFromNode(self, node)
|
|
||||||
{
|
|
||||||
// Walk up toward the tree view node looking for this first item
|
|
||||||
while (node && node != self.Node)
|
|
||||||
{
|
|
||||||
if ("TreeviewItem" in node)
|
|
||||||
return node.TreeviewItem;
|
|
||||||
|
|
||||||
node = node.parentNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Treeview;
|
|
||||||
})();
|
|
|
@ -1,109 +0,0 @@
|
||||||
|
|
||||||
namespace("WM");
|
|
||||||
|
|
||||||
|
|
||||||
WM.TreeviewItem = (function()
|
|
||||||
{
|
|
||||||
function TreeviewItem(treeview, name, data, open_image, close_image)
|
|
||||||
{
|
|
||||||
// Assign members
|
|
||||||
this.Treeview = treeview;
|
|
||||||
this.Label = name;
|
|
||||||
this.Data = data;
|
|
||||||
this.OpenImage = open_image;
|
|
||||||
this.CloseImage = close_image;
|
|
||||||
|
|
||||||
this.Children = [ ];
|
|
||||||
|
|
||||||
// The HTML node wrapping the item and its children
|
|
||||||
this.Node = null;
|
|
||||||
|
|
||||||
// The HTML node storing the image for the open/close state feedback
|
|
||||||
this.ImageNode = null;
|
|
||||||
|
|
||||||
// The HTML node storing just the children
|
|
||||||
this.ChildrenNode = null;
|
|
||||||
|
|
||||||
// Animation handle for opening and closing the child nodes, only used
|
|
||||||
// if the tree view item as children
|
|
||||||
this.AnimHandle = null;
|
|
||||||
|
|
||||||
// Open state of the item
|
|
||||||
this.IsOpen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TreeviewItem.prototype.AddItem = function(name, data, open_image, close_image)
|
|
||||||
{
|
|
||||||
var item = new WM.TreeviewItem(this.Treeview, name, data, open_image, close_image);
|
|
||||||
this.Children.push(item);
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TreeviewItem.prototype.Open = function()
|
|
||||||
{
|
|
||||||
if (this.AnimHandle == null || this.AnimHandle.Complete)
|
|
||||||
{
|
|
||||||
// Swap to the open state
|
|
||||||
this.IsOpen = true;
|
|
||||||
if (this.ImageNode != null && this.OpenImage != null)
|
|
||||||
this.ImageNode.src = this.OpenImage.src;
|
|
||||||
|
|
||||||
// Cache for closure binding
|
|
||||||
var child_node = this.ChildrenNode;
|
|
||||||
var end_height = this.StartHeight;
|
|
||||||
var treeview = this.Treeview;
|
|
||||||
|
|
||||||
// Reveal the children and animate their height to max
|
|
||||||
this.ChildrenNode.style.display = "block";
|
|
||||||
this.AnimHandle = Anim.Animate(
|
|
||||||
function (val) { DOM.Node.SetHeight(child_node, val) },
|
|
||||||
0, end_height, 0.2,
|
|
||||||
function() { treeview.UpdateScrollbar(); });
|
|
||||||
|
|
||||||
// Fade the children in
|
|
||||||
Anim.Animate(function(val) { DOM.Node.SetOpacity(child_node, val) }, 0, 1, 0.2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TreeviewItem.prototype.Close = function()
|
|
||||||
{
|
|
||||||
if (this.AnimHandle == null || this.AnimHandle.Complete)
|
|
||||||
{
|
|
||||||
// Swap to the close state
|
|
||||||
this.IsOpen = false;
|
|
||||||
if (this.ImageNode != null && this.CloseImage != null)
|
|
||||||
this.ImageNode.src = this.CloseImage.src;
|
|
||||||
|
|
||||||
// Cache for closure binding
|
|
||||||
var child_node = this.ChildrenNode;
|
|
||||||
var treeview = this.Treeview;
|
|
||||||
|
|
||||||
// Mark the height of the item for reload later
|
|
||||||
this.StartHeight = child_node.offsetHeight;
|
|
||||||
|
|
||||||
// Shrink the height of the children and hide them upon completion
|
|
||||||
this.AnimHandle = Anim.Animate(
|
|
||||||
function (val) { DOM.Node.SetHeight(child_node, val) },
|
|
||||||
this.ChildrenNode.offsetHeight, 0, 0.2,
|
|
||||||
function() { child_node.style.display = "none"; treeview.UpdateScrollbar(); });
|
|
||||||
|
|
||||||
// Fade the children out
|
|
||||||
Anim.Animate(function(val) { DOM.Node.SetOpacity(child_node, val) }, 1, 0, 0.2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TreeviewItem.prototype.Toggle = function()
|
|
||||||
{
|
|
||||||
if (this.IsOpen)
|
|
||||||
this.Close();
|
|
||||||
else
|
|
||||||
this.Open();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return TreeviewItem;
|
|
||||||
})();
|
|
|
@ -1,318 +0,0 @@
|
||||||
|
|
||||||
namespace("WM");
|
|
||||||
|
|
||||||
|
|
||||||
WM.Window = (function()
|
|
||||||
{
|
|
||||||
var template_html = multiline(function(){/* \
|
|
||||||
<div class='Window'>
|
|
||||||
<div class='WindowTitleBar'>
|
|
||||||
<div class='WindowTitleBarText notextsel' style='float:left'>Window Title Bar</div>
|
|
||||||
<div class='WindowTitleBarClose notextsel' style='float:right'>✕</div>
|
|
||||||
</div>
|
|
||||||
<div class='WindowBody'>
|
|
||||||
</div>
|
|
||||||
<div class='WindowResizeHandle notextsel'>⋰</div>
|
|
||||||
</div>
|
|
||||||
*/});
|
|
||||||
|
|
||||||
|
|
||||||
function Window(manager, title, x, y, width, height, parent_node, user_data)
|
|
||||||
{
|
|
||||||
this.Manager = manager;
|
|
||||||
this.ParentNode = parent_node || document.body;
|
|
||||||
this.userData = user_data;
|
|
||||||
this.OnMove = null;
|
|
||||||
this.OnResize = null;
|
|
||||||
this.Visible = false;
|
|
||||||
this.AnimatedShow = false;
|
|
||||||
|
|
||||||
// Clone the window template and locate key nodes within it
|
|
||||||
this.Node = DOM.Node.CreateHTML(template_html);
|
|
||||||
this.TitleBarNode = DOM.Node.FindWithClass(this.Node, "WindowTitleBar");
|
|
||||||
this.TitleBarTextNode = DOM.Node.FindWithClass(this.Node, "WindowTitleBarText");
|
|
||||||
this.TitleBarCloseNode = DOM.Node.FindWithClass(this.Node, "WindowTitleBarClose");
|
|
||||||
this.ResizeHandleNode = DOM.Node.FindWithClass(this.Node, "WindowResizeHandle");
|
|
||||||
this.BodyNode = DOM.Node.FindWithClass(this.Node, "WindowBody");
|
|
||||||
|
|
||||||
// Setup the position and dimensions of the window
|
|
||||||
this.SetPosition(x, y);
|
|
||||||
this.SetSize(width, height);
|
|
||||||
|
|
||||||
// Set the title text
|
|
||||||
this.TitleBarTextNode.innerHTML = title;
|
|
||||||
|
|
||||||
// Hook up event handlers
|
|
||||||
DOM.Event.AddHandler(this.Node, "mousedown", Bind(this, "SetTop"));
|
|
||||||
DOM.Event.AddHandler(this.TitleBarNode, "mousedown", Bind(this, "BeginMove"));
|
|
||||||
DOM.Event.AddHandler(this.ResizeHandleNode, "mousedown", Bind(this, "BeginResize"));
|
|
||||||
DOM.Event.AddHandler(this.TitleBarCloseNode, "mouseup", Bind(this, "Hide"));
|
|
||||||
|
|
||||||
// Create delegates for removable handlers
|
|
||||||
this.MoveDelegate = Bind(this, "Move");
|
|
||||||
this.EndMoveDelegate = Bind(this, "EndMove")
|
|
||||||
this.ResizeDelegate = Bind(this, "Resize");
|
|
||||||
this.EndResizeDelegate = Bind(this, "EndResize");
|
|
||||||
}
|
|
||||||
|
|
||||||
Window.prototype.SetOnMove = function(on_move)
|
|
||||||
{
|
|
||||||
this.OnMove = on_move;
|
|
||||||
}
|
|
||||||
|
|
||||||
Window.prototype.SetOnResize = function(on_resize)
|
|
||||||
{
|
|
||||||
this.OnResize = on_resize;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.Show = function()
|
|
||||||
{
|
|
||||||
if (this.Node.parentNode != this.ParentNode)
|
|
||||||
{
|
|
||||||
this.ShowNoAnim();
|
|
||||||
Anim.Animate(Bind(this, "OpenAnimation"), 0, 1, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.ShowNoAnim = function()
|
|
||||||
{
|
|
||||||
// Add to the document
|
|
||||||
this.ParentNode.appendChild(this.Node);
|
|
||||||
this.AnimatedShow = false;
|
|
||||||
this.Visible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.Hide = function(evt)
|
|
||||||
{
|
|
||||||
if (this.Node.parentNode == this.ParentNode && evt.button == 0)
|
|
||||||
{
|
|
||||||
if (this.AnimatedShow)
|
|
||||||
{
|
|
||||||
// Trigger animation that ends with removing the window from the document
|
|
||||||
Anim.Animate(
|
|
||||||
Bind(this, "CloseAnimation"),
|
|
||||||
0, 1, 0.25,
|
|
||||||
Bind(this, "HideNoAnim"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.HideNoAnim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.HideNoAnim = function()
|
|
||||||
{
|
|
||||||
if (this.Node.parentNode == this.ParentNode)
|
|
||||||
{
|
|
||||||
// Remove node
|
|
||||||
this.ParentNode.removeChild(this.Node);
|
|
||||||
this.Visible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.Close = function()
|
|
||||||
{
|
|
||||||
this.HideNoAnim();
|
|
||||||
this.Manager.RemoveWindow(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.SetTop = function()
|
|
||||||
{
|
|
||||||
this.Manager.SetTopWindow(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.SetTitle = function(title)
|
|
||||||
{
|
|
||||||
this.TitleBarTextNode.innerHTML = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Update this
|
|
||||||
Window.prototype.AddControl = function(control)
|
|
||||||
{
|
|
||||||
// Get all arguments to this function and replace the first with this window node
|
|
||||||
var args = [].slice.call(arguments);
|
|
||||||
args[0] = this.BodyNode;
|
|
||||||
|
|
||||||
// Create the control and call its Init method with the modified arguments
|
|
||||||
var instance = new control();
|
|
||||||
instance.Init.apply(instance, args);
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.AddControlNew = function(control)
|
|
||||||
{
|
|
||||||
control.ParentNode = this.BodyNode;
|
|
||||||
this.BodyNode.appendChild(control.Node);
|
|
||||||
return control;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.RemoveControl = function(control)
|
|
||||||
{
|
|
||||||
if (control.ParentNode == this.BodyNode)
|
|
||||||
{
|
|
||||||
control.ParentNode.removeChild(control.Node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.Scale = function(t)
|
|
||||||
{
|
|
||||||
// Calculate window bounds centre/extents
|
|
||||||
var ext_x = this.Size[0] / 2;
|
|
||||||
var ext_y = this.Size[1] / 2;
|
|
||||||
var mid_x = this.Position[0] + ext_x;
|
|
||||||
var mid_y = this.Position[1] + ext_y;
|
|
||||||
|
|
||||||
// Scale from the mid-point
|
|
||||||
DOM.Node.SetPosition(this.Node, [ mid_x - ext_x * t, mid_y - ext_y * t ]);
|
|
||||||
DOM.Node.SetSize(this.Node, [ this.Size[0] * t, this.Size[1] * t ]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.OpenAnimation = function(val)
|
|
||||||
{
|
|
||||||
// Power ease in
|
|
||||||
var t = 1 - Math.pow(1 - val, 8);
|
|
||||||
this.Scale(t);
|
|
||||||
DOM.Node.SetOpacity(this.Node, 1 - Math.pow(1 - val, 8));
|
|
||||||
this.AnimatedShow = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.CloseAnimation = function(val)
|
|
||||||
{
|
|
||||||
// Power ease out
|
|
||||||
var t = 1 - Math.pow(val, 4);
|
|
||||||
this.Scale(t);
|
|
||||||
DOM.Node.SetOpacity(this.Node, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.NotifyChange = function()
|
|
||||||
{
|
|
||||||
if (this.OnMove)
|
|
||||||
{
|
|
||||||
var pos = DOM.Node.GetPosition(this.Node);
|
|
||||||
this.OnMove(this, pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.BeginMove = function(evt)
|
|
||||||
{
|
|
||||||
// Calculate offset of the window from the mouse down position
|
|
||||||
var mouse_pos = DOM.Event.GetMousePosition(evt);
|
|
||||||
this.Offset = [ mouse_pos[0] - this.Position[0], mouse_pos[1] - this.Position[1] ];
|
|
||||||
|
|
||||||
// Dynamically add handlers for movement and release
|
|
||||||
DOM.Event.AddHandler(document, "mousemove", this.MoveDelegate);
|
|
||||||
DOM.Event.AddHandler(document, "mouseup", this.EndMoveDelegate);
|
|
||||||
|
|
||||||
DOM.Event.StopDefaultAction(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.Move = function(evt)
|
|
||||||
{
|
|
||||||
// Use the offset at the beginning of movement to drag the window around
|
|
||||||
var mouse_pos = DOM.Event.GetMousePosition(evt);
|
|
||||||
var offset = this.Offset;
|
|
||||||
var pos = [ mouse_pos[0] - offset[0], mouse_pos[1] - offset[1] ];
|
|
||||||
this.SetPosition(pos[0], pos[1]);
|
|
||||||
|
|
||||||
if (this.OnMove)
|
|
||||||
this.OnMove(this, pos);
|
|
||||||
|
|
||||||
DOM.Event.StopDefaultAction(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.EndMove = function(evt)
|
|
||||||
{
|
|
||||||
// Remove handlers added during mouse down
|
|
||||||
DOM.Event.RemoveHandler(document, "mousemove", this.MoveDelegate);
|
|
||||||
DOM.Event.RemoveHandler(document, "mouseup", this.EndMoveDelegate);
|
|
||||||
|
|
||||||
DOM.Event.StopDefaultAction(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.BeginResize = function(evt)
|
|
||||||
{
|
|
||||||
// Calculate offset of the window from the mouse down position
|
|
||||||
var mouse_pos = DOM.Event.GetMousePosition(evt);
|
|
||||||
this.MousePosBeforeResize = [ mouse_pos[0], mouse_pos[1] ];
|
|
||||||
this.SizeBeforeResize = this.Size;
|
|
||||||
|
|
||||||
// Dynamically add handlers for movement and release
|
|
||||||
DOM.Event.AddHandler(document, "mousemove", this.ResizeDelegate);
|
|
||||||
DOM.Event.AddHandler(document, "mouseup", this.EndResizeDelegate);
|
|
||||||
|
|
||||||
DOM.Event.StopDefaultAction(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.Resize = function(evt)
|
|
||||||
{
|
|
||||||
// Use the offset at the beginning of movement to drag the window around
|
|
||||||
var mouse_pos = DOM.Event.GetMousePosition(evt);
|
|
||||||
var offset = [ mouse_pos[0] - this.MousePosBeforeResize[0], mouse_pos[1] - this.MousePosBeforeResize[1] ];
|
|
||||||
this.SetSize(this.SizeBeforeResize[0] + offset[0], this.SizeBeforeResize[1] + offset[1]);
|
|
||||||
|
|
||||||
if (this.OnResize)
|
|
||||||
this.OnResize(this, this.Size);
|
|
||||||
|
|
||||||
DOM.Event.StopDefaultAction(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.EndResize = function(evt)
|
|
||||||
{
|
|
||||||
// Remove handlers added during mouse down
|
|
||||||
DOM.Event.RemoveHandler(document, "mousemove", this.ResizeDelegate);
|
|
||||||
DOM.Event.RemoveHandler(document, "mouseup", this.EndResizeDelegate);
|
|
||||||
|
|
||||||
DOM.Event.StopDefaultAction(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.SetPosition = function(x, y)
|
|
||||||
{
|
|
||||||
this.Position = [ x, y ];
|
|
||||||
DOM.Node.SetPosition(this.Node, this.Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.SetSize = function(w, h)
|
|
||||||
{
|
|
||||||
w = Math.max(80, w);
|
|
||||||
h = Math.max(15, h);
|
|
||||||
this.Size = [ w, h ];
|
|
||||||
DOM.Node.SetSize(this.Node, this.Size);
|
|
||||||
|
|
||||||
if (this.OnResize)
|
|
||||||
this.OnResize(this, this.Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Window.prototype.GetZIndex = function()
|
|
||||||
{
|
|
||||||
return parseInt(this.Node.style.zIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return Window;
|
|
||||||
})();
|
|
|
@ -1,65 +0,0 @@
|
||||||
|
|
||||||
namespace("WM");
|
|
||||||
|
|
||||||
|
|
||||||
WM.WindowManager = (function()
|
|
||||||
{
|
|
||||||
function WindowManager()
|
|
||||||
{
|
|
||||||
// An empty list of windows under window manager control
|
|
||||||
this.Windows = [ ];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WindowManager.prototype.AddWindow = function(title, x, y, width, height, parent_node, user_data)
|
|
||||||
{
|
|
||||||
// Create the window and add it to the list of windows
|
|
||||||
var wnd = new WM.Window(this, title, x, y, width, height, parent_node, user_data);
|
|
||||||
this.Windows.push(wnd);
|
|
||||||
|
|
||||||
// Always bring to the top on creation
|
|
||||||
wnd.SetTop();
|
|
||||||
|
|
||||||
return wnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WindowManager.prototype.RemoveWindow = function(window)
|
|
||||||
{
|
|
||||||
// Remove from managed window list
|
|
||||||
var index = this.Windows.indexOf(window);
|
|
||||||
if (index != -1)
|
|
||||||
{
|
|
||||||
this.Windows.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WindowManager.prototype.SetTopWindow = function(top_wnd)
|
|
||||||
{
|
|
||||||
// Bring the window to the top of the window list
|
|
||||||
var top_wnd_index = this.Windows.indexOf(top_wnd);
|
|
||||||
if (top_wnd_index != -1)
|
|
||||||
this.Windows.splice(top_wnd_index, 1);
|
|
||||||
this.Windows.push(top_wnd);
|
|
||||||
|
|
||||||
// Set a CSS z-index for each visible window from the bottom up
|
|
||||||
for (var i in this.Windows)
|
|
||||||
{
|
|
||||||
var wnd = this.Windows[i];
|
|
||||||
if (!wnd.Visible)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Ensure there's space between each window for the elements inside to be sorted
|
|
||||||
var z = (parseInt(i) + 1) * 10;
|
|
||||||
wnd.Node.style.zIndex = z;
|
|
||||||
|
|
||||||
// Notify window that its z-order has changed
|
|
||||||
wnd.NotifyChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return WindowManager;
|
|
||||||
|
|
||||||
})();
|
|
|
@ -1,652 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
.notextsel
|
|
||||||
{
|
|
||||||
/* Disable text selection so that it doesn't interfere with button-clicking */
|
|
||||||
user-select: none;
|
|
||||||
-moz-user-select: none; /* Firefox */
|
|
||||||
-ms-user-select: none; /* Internet Explorer */
|
|
||||||
-khtml-user-select: none; /* KHTML browsers (e.g. Konqueror) */
|
|
||||||
-webkit-user-select: none; /* Chrome, Safari, and Opera */
|
|
||||||
-webkit-touch-callout: none; /* Disable Android and iOS callouts*/
|
|
||||||
|
|
||||||
/* Stops the text cursor over the label */
|
|
||||||
cursor:default;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
/* Window Styles */
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
|
|
||||||
body
|
|
||||||
{
|
|
||||||
/* Clip contents to browser window without adding scrollbars */
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Window
|
|
||||||
{
|
|
||||||
position:absolute;
|
|
||||||
|
|
||||||
/* Clip all contents to the window border */
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
background: #555;
|
|
||||||
|
|
||||||
/*padding: 0px !important;*/
|
|
||||||
|
|
||||||
border-radius: 3px;
|
|
||||||
-moz-border-radius: 5px;
|
|
||||||
|
|
||||||
-webkit-box-shadow: 1px 1px 1px #222, 1px 1px 1px #777 inset;
|
|
||||||
box-shadow: 1px 1px 1px #222, 1px 1px 1px #777 inset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*:root
|
|
||||||
{
|
|
||||||
--SideBarSize: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.WindowBodyDebug
|
|
||||||
{
|
|
||||||
color: #BBB;
|
|
||||||
font: 9px Verdana;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.WindowSizeLeft
|
|
||||||
{
|
|
||||||
position: absolute;
|
|
||||||
left: 0px;
|
|
||||||
top: 0px;
|
|
||||||
width: var(--SideBarSize);
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.WindowSizeRight
|
|
||||||
{
|
|
||||||
position: absolute;
|
|
||||||
left: calc(100% - var(--SideBarSize));
|
|
||||||
top:0px;
|
|
||||||
width: var(--SideBarSize);
|
|
||||||
height:100%;
|
|
||||||
}
|
|
||||||
.WindowSizeTop
|
|
||||||
{
|
|
||||||
position: absolute;
|
|
||||||
left: 0px;
|
|
||||||
top: 0px;
|
|
||||||
width: 100%;
|
|
||||||
height: var(--SideBarSize);
|
|
||||||
}
|
|
||||||
.WindowSizeBottom
|
|
||||||
{
|
|
||||||
position: absolute;
|
|
||||||
left: 0px;
|
|
||||||
top: calc(100% - var(--SideBarSize));
|
|
||||||
width: 100%;
|
|
||||||
height: var(--SideBarSize);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
.Window_Transparent
|
|
||||||
{
|
|
||||||
/* Set transparency changes to fade in/out */
|
|
||||||
opacity: 0.5;
|
|
||||||
transition: opacity 0.5s ease-out;
|
|
||||||
-moz-transition: opacity 0.5s ease-out;
|
|
||||||
-webkit-transition: opacity 0.5s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Window_Transparent:hover
|
|
||||||
{
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.WindowTitleBar
|
|
||||||
{
|
|
||||||
height: 17px;
|
|
||||||
cursor: move;
|
|
||||||
/*overflow: hidden;*/
|
|
||||||
|
|
||||||
border-bottom: 1px solid #303030;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.WindowTitleBarText
|
|
||||||
{
|
|
||||||
color: #BBB;
|
|
||||||
font: 9px Verdana;
|
|
||||||
/*white-space: nowrap;*/
|
|
||||||
|
|
||||||
padding: 3px;
|
|
||||||
cursor: move;
|
|
||||||
}
|
|
||||||
|
|
||||||
.WindowTitleBarClose
|
|
||||||
{
|
|
||||||
color: #999999;
|
|
||||||
font: 9px Verdana;
|
|
||||||
|
|
||||||
padding: 3px;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.WindowTitleBarClose:hover {
|
|
||||||
color: #bbb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.WindowResizeHandle
|
|
||||||
{
|
|
||||||
color: #999999;
|
|
||||||
font: 17px Verdana;
|
|
||||||
padding: 3px;
|
|
||||||
cursor: se-resize;
|
|
||||||
position: absolute;
|
|
||||||
bottom: -7px;
|
|
||||||
right: -3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.WindowBody {
|
|
||||||
position: absolute;
|
|
||||||
/* overflow: hidden; */
|
|
||||||
display: block;
|
|
||||||
padding: 10px;
|
|
||||||
border-top: 1px solid #606060;
|
|
||||||
top: 18px;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
/* Container Styles */
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.Container
|
|
||||||
{
|
|
||||||
/* Position relative to the parent window */
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
/* Clip contents */
|
|
||||||
/*overflow: hidden;*/
|
|
||||||
|
|
||||||
background:#2C2C2C;
|
|
||||||
|
|
||||||
border: 1px black solid;
|
|
||||||
|
|
||||||
/* Two inset box shadows to simulate depressing */
|
|
||||||
-webkit-box-shadow: -1px -1px 1px #222 inset, 1px 1px 1px #222 inset;
|
|
||||||
box-shadow: -1px -1px 1px #222 inset, 1px 1px 1px #222 inset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*.Panel
|
|
||||||
{*/
|
|
||||||
/* Position relative to the parent window */
|
|
||||||
/*position: absolute;*/
|
|
||||||
|
|
||||||
/* Clip contents */
|
|
||||||
/*overflow: hidden;
|
|
||||||
|
|
||||||
background:#2C2C2C;
|
|
||||||
|
|
||||||
border: 1px black solid;*/
|
|
||||||
|
|
||||||
/* Two inset box shadows to simulate depressing */
|
|
||||||
/*-webkit-box-shadow: -1px -1px 1px #222 inset, 1px 1px 1px #222 inset;
|
|
||||||
box-shadow: -1px -1px 1px #222 inset, 1px 1px 1px #222 inset;*/
|
|
||||||
/*}*/
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
/* Ruler Styles */
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*.Ruler
|
|
||||||
{
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
border: dashed 1px;
|
|
||||||
|
|
||||||
opacity: 0.35;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
/* Treeview Styles */
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.Treeview
|
|
||||||
{
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
background:#2C2C2C;
|
|
||||||
border: 1px solid black;
|
|
||||||
overflow:hidden;
|
|
||||||
|
|
||||||
/* Two inset box shadows to simulate depressing */
|
|
||||||
-webkit-box-shadow: -1px -1px 1px #222 inset, 1px 1px 1px #222 inset;
|
|
||||||
box-shadow: -1px -1px 1px #222 inset, 1px 1px 1px #222 inset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TreeviewItem
|
|
||||||
{
|
|
||||||
margin:1px;
|
|
||||||
padding:2px;
|
|
||||||
border:solid 1px #2C2C2C;
|
|
||||||
background-color:#2C2C2C;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TreeviewItemImage
|
|
||||||
{
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TreeviewItemText
|
|
||||||
{
|
|
||||||
float: left;
|
|
||||||
margin-left:4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TreeviewItemChildren
|
|
||||||
{
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TreeviewItemSelected
|
|
||||||
{
|
|
||||||
background-color:#444;
|
|
||||||
border-color:#FFF;
|
|
||||||
|
|
||||||
-webkit-transition: background-color 0.2s ease-in-out;
|
|
||||||
-moz-transition: background-color 0.2s ease-in-out;
|
|
||||||
-webkit-transition: border-color 0.2s ease-in-out;
|
|
||||||
-moz-transition: border-color 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Used to populate treeviews that want highlight on hover behaviour */
|
|
||||||
.TreeviewItemHover
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
.TreeviewItemHover:hover
|
|
||||||
{
|
|
||||||
background-color:#111;
|
|
||||||
border-color:#444;
|
|
||||||
|
|
||||||
-webkit-transition: background-color 0.2s ease-in-out;
|
|
||||||
-moz-transition: background-color 0.2s ease-in-out;
|
|
||||||
-webkit-transition: border-color 0.2s ease-in-out;
|
|
||||||
-moz-transition: border-color 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TreeviewScrollbarInset
|
|
||||||
{
|
|
||||||
float: right;
|
|
||||||
|
|
||||||
position:relative;
|
|
||||||
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
/* CRAZINESS PART A: Trying to get the inset and scrollbar to have 100% height match its container */
|
|
||||||
margin: -8px -8px 0 0;
|
|
||||||
padding: 0 1px 14px 1px;
|
|
||||||
|
|
||||||
width:20px;
|
|
||||||
background:#2C2C2C;
|
|
||||||
border: 1px solid black;
|
|
||||||
|
|
||||||
/* Two inset box shadows to simulate depressing */
|
|
||||||
-webkit-box-shadow: -1px -1px 1px #222 inset, 1px 1px 1px #222 inset;
|
|
||||||
box-shadow: -1px -1px 1px #222 inset, 1px 1px 1px #222 inset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TreeviewScrollbar
|
|
||||||
{
|
|
||||||
position:relative;
|
|
||||||
|
|
||||||
background:#2C2C2C;
|
|
||||||
border: 1px solid black;
|
|
||||||
|
|
||||||
/* CRAZINESS PART B: Trying to get the inset and scrollbar to have 100% height match its container */
|
|
||||||
padding: 0 0 10px 0;
|
|
||||||
margin: 1px 0 0 0;
|
|
||||||
|
|
||||||
width: 18px;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
border-radius:6px;
|
|
||||||
border-color:#000;
|
|
||||||
border-width:1px;
|
|
||||||
border-style:solid;
|
|
||||||
|
|
||||||
/* The gradient for the button background */
|
|
||||||
background-color:#666;
|
|
||||||
background: -webkit-gradient(linear, left top, left bottom, from(#666), to(#383838));
|
|
||||||
background: -moz-linear-gradient(top, #666, #383838);
|
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#666666', endColorstr='#383838');
|
|
||||||
|
|
||||||
/* A box shadow and inset box highlight */
|
|
||||||
-webkit-box-shadow: 1px 1px 1px #222, 1px 1px 1px #777 inset;
|
|
||||||
box-shadow: 1px 1px 1px #222, 1px 1px 1px #777 inset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TreeviewScrollbarHeld
|
|
||||||
{
|
|
||||||
/* Reset the gradient to a full-colour background */
|
|
||||||
background:#383838;
|
|
||||||
|
|
||||||
/* Two inset box shadows to simulate depressing */
|
|
||||||
-webkit-box-shadow: -1px -1px 1px #222 inset, 1px 1px 1px #222 inset;
|
|
||||||
box-shadow: -1px -1px 1px #222 inset, 1px 1px 1px #222 inset;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
/* Edit Box Styles */
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.EditBoxContainer
|
|
||||||
{
|
|
||||||
position: absolute;
|
|
||||||
padding:2px 10px 2px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.EditBoxLabel
|
|
||||||
{
|
|
||||||
float:left;
|
|
||||||
padding: 3px 4px 4px 4px;
|
|
||||||
font: 9px Verdana;
|
|
||||||
}
|
|
||||||
|
|
||||||
.EditBox
|
|
||||||
{
|
|
||||||
float:left;
|
|
||||||
|
|
||||||
background:#666;
|
|
||||||
border: 1px solid;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 3px 4px 3px 4px;
|
|
||||||
height: 20px;
|
|
||||||
|
|
||||||
box-shadow: 1px 1px 1px #222 inset;
|
|
||||||
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.EditBox:focus
|
|
||||||
{
|
|
||||||
background:#FFF;
|
|
||||||
outline:0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
/* Label Styles */
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.Label
|
|
||||||
{
|
|
||||||
/* Position relative to the parent window */
|
|
||||||
position:absolute;
|
|
||||||
|
|
||||||
color: #BBB;
|
|
||||||
font: 9px Verdana;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
/* Combo Box Styles */
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.ComboBox
|
|
||||||
{
|
|
||||||
position:absolute;
|
|
||||||
|
|
||||||
/* TEMP! */
|
|
||||||
width:90px;
|
|
||||||
|
|
||||||
/* Height is fixed to match the font */
|
|
||||||
height:14px;
|
|
||||||
|
|
||||||
/* Align the text within the combo box */
|
|
||||||
padding: 1px 0 0 5px;
|
|
||||||
|
|
||||||
/* Solid, rounded border */
|
|
||||||
border: 1px solid #111;
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
/* http://www.colorzilla.com/gradient-editor/#e3e3e3+0,c6c6c6+22,b7b7b7+33,afafaf+50,a7a7a7+67,797979+82,414141+100;Custom */
|
|
||||||
background: #e3e3e3;
|
|
||||||
background: -moz-linear-gradient(top, #e3e3e3 0%, #c6c6c6 22%, #b7b7b7 33%, #afafaf 50%, #a7a7a7 67%, #797979 82%, #414141 100%);
|
|
||||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#e3e3e3), color-stop(22%,#c6c6c6), color-stop(33%,#b7b7b7), color-stop(50%,#afafaf), color-stop(67%,#a7a7a7), color-stop(82%,#797979), color-stop(100%,#414141));
|
|
||||||
background: -webkit-linear-gradient(top, #e3e3e3 0%,#c6c6c6 22%,#b7b7b7 33%,#afafaf 50%,#a7a7a7 67%,#797979 82%,#414141 100%);
|
|
||||||
background: -o-linear-gradient(top, #e3e3e3 0%,#c6c6c6 22%,#b7b7b7 33%,#afafaf 50%,#a7a7a7 67%,#797979 82%,#414141 100%);
|
|
||||||
background: -ms-linear-gradient(top, #e3e3e3 0%,#c6c6c6 22%,#b7b7b7 33%,#afafaf 50%,#a7a7a7 67%,#797979 82%,#414141 100%);
|
|
||||||
background: linear-gradient(top, #e3e3e3 0%,#c6c6c6 22%,#b7b7b7 33%,#afafaf 50%,#a7a7a7 67%,#797979 82%,#414141 100%);
|
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#e3e3e3', endColorstr='#414141',GradientType=0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
.ComboBoxPressed
|
|
||||||
{
|
|
||||||
/* The reverse of the default background, simulating depression */
|
|
||||||
background: #414141;
|
|
||||||
background: -moz-linear-gradient(top, #414141 0%, #797979 18%, #a7a7a7 33%, #afafaf 50%, #b7b7b7 67%, #c6c6c6 78%, #e3e3e3 100%);
|
|
||||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#414141), color-stop(18%,#797979), color-stop(33%,#a7a7a7), color-stop(50%,#afafaf), color-stop(67%,#b7b7b7), color-stop(78%,#c6c6c6), color-stop(100%,#e3e3e3));
|
|
||||||
background: -webkit-linear-gradient(top, #414141 0%,#797979 18%,#a7a7a7 33%,#afafaf 50%,#b7b7b7 67%,#c6c6c6 78%,#e3e3e3 100%);
|
|
||||||
background: -o-linear-gradient(top, #414141 0%,#797979 18%,#a7a7a7 33%,#afafaf 50%,#b7b7b7 67%,#c6c6c6 78%,#e3e3e3 100%);
|
|
||||||
background: -ms-linear-gradient(top, #414141 0%,#797979 18%,#a7a7a7 33%,#afafaf 50%,#b7b7b7 67%,#c6c6c6 78%,#e3e3e3 100%);
|
|
||||||
background: linear-gradient(top, #414141 0%,#797979 18%,#a7a7a7 33%,#afafaf 50%,#b7b7b7 67%,#c6c6c6 78%,#e3e3e3 100%);
|
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#414141', endColorstr='#e3e3e3',GradientType=0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
.ComboBoxText
|
|
||||||
{
|
|
||||||
/* Text info */
|
|
||||||
color: #000;
|
|
||||||
font: 9px Verdana;
|
|
||||||
|
|
||||||
float:left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ComboBoxIcon
|
|
||||||
{
|
|
||||||
/* Push the image to the far right */
|
|
||||||
float:right;
|
|
||||||
|
|
||||||
/* Align the image with the combo box */
|
|
||||||
padding: 2px 5px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ComboBoxPopup
|
|
||||||
{
|
|
||||||
position: fixed;
|
|
||||||
|
|
||||||
background: #CCC;
|
|
||||||
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
padding: 1px 0 1px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ComboBoxPopupItem
|
|
||||||
{
|
|
||||||
/* Text info */
|
|
||||||
color: #000;
|
|
||||||
font: 9px Verdana;
|
|
||||||
|
|
||||||
padding: 1px 1px 1px 5px;
|
|
||||||
|
|
||||||
border-bottom: 1px solid #AAA;
|
|
||||||
border-top: 1px solid #FFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ComboBoxPopupItemText
|
|
||||||
{
|
|
||||||
float:left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ComboBoxPopupItemIcon
|
|
||||||
{
|
|
||||||
/* Push the image to the far right */
|
|
||||||
float:right;
|
|
||||||
|
|
||||||
/* Align the image with the combo box */
|
|
||||||
padding: 2px 5px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ComboBoxPopupItem:first-child
|
|
||||||
{
|
|
||||||
border-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ComboBoxPopupItem:last-child
|
|
||||||
{
|
|
||||||
border-bottom: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ComboBoxPopupItem:hover
|
|
||||||
{
|
|
||||||
color:#FFF;
|
|
||||||
background: #2036E1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
/* Grid Styles */
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
|
|
||||||
|
|
||||||
.Grid {
|
|
||||||
overflow: auto;
|
|
||||||
background: #333;
|
|
||||||
height: 100%;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GridBody
|
|
||||||
{
|
|
||||||
overflow-x: auto;
|
|
||||||
overflow-y: auto;
|
|
||||||
height: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GridRow
|
|
||||||
{
|
|
||||||
display: inline-block;
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
background:rgb(48, 48, 48);
|
|
||||||
|
|
||||||
color: #BBB;
|
|
||||||
font: 9px Verdana;
|
|
||||||
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GridRow.GridGroup
|
|
||||||
{
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GridRow:nth-child(odd)
|
|
||||||
{
|
|
||||||
background:#333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GridRowCell
|
|
||||||
{
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
.GridRowCell.GridGroup
|
|
||||||
{
|
|
||||||
color: #BBB;
|
|
||||||
|
|
||||||
/* Override default from name */
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
padding: 1px 1px 1px 2px;
|
|
||||||
border: 1px solid;
|
|
||||||
border-radius: 2px;
|
|
||||||
|
|
||||||
border-top-color:#555;
|
|
||||||
border-left-color:#555;
|
|
||||||
border-bottom-color:#111;
|
|
||||||
border-right-color:#111;
|
|
||||||
|
|
||||||
background: #222;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GridRowBody
|
|
||||||
{
|
|
||||||
/* Clip all contents for show/hide group*/
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
/* Crazy CSS rules: controls for properties don't clip if this isn't set on this parent */
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
/* Button Styles */
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.Button
|
|
||||||
{
|
|
||||||
/* Position relative to the parent window */
|
|
||||||
position:absolute;
|
|
||||||
|
|
||||||
border-radius:4px;
|
|
||||||
|
|
||||||
/* Padding at the top includes 2px for the text drop-shadow */
|
|
||||||
padding: 2px 5px 3px 5px;
|
|
||||||
|
|
||||||
color: #BBB;
|
|
||||||
font: 9px Verdana;
|
|
||||||
text-shadow: 1px 1px 1px black;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
background-color:#555;
|
|
||||||
|
|
||||||
/* A box shadow and inset box highlight */
|
|
||||||
-webkit-box-shadow: 1px 1px 1px #222, 1px 1px 1px #777 inset;
|
|
||||||
box-shadow: 1px 1px 1px #222, 1px 1px 1px #777 inset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Button:hover {
|
|
||||||
background-color: #616161;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Button.ButtonHeld
|
|
||||||
{
|
|
||||||
/* Reset the gradient to a full-colour background */
|
|
||||||
background:#383838;
|
|
||||||
|
|
||||||
/* Two inset box shadows to simulate depressing */
|
|
||||||
-webkit-box-shadow: -1px -1px 1px #222 inset, 1px 1px 1px #222 inset;
|
|
||||||
box-shadow: -1px -1px 1px #222 inset, 1px 1px 1px #222 inset;
|
|
||||||
}
|
|
69
profiler/vis/index.html
vendored
69
profiler/vis/index.html
vendored
|
@ -1,69 +0,0 @@
|
||||||
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
|
|
||||||
<title>Remotery Viewer</title>
|
|
||||||
|
|
||||||
<!-- Style Sheets -->
|
|
||||||
<link rel="stylesheet" type="text/css" href="extern/BrowserLib/WindowManager/Styles/WindowManager.css" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="Styles/Remotery.css" />
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<!-- Utilities -->
|
|
||||||
<script type="text/javascript" src="extern/BrowserLib/Core/Code/Core.js"></script>
|
|
||||||
<script type="text/javascript" src="extern/BrowserLib/Core/Code/DOM.js"></script>
|
|
||||||
<script type="text/javascript" src="extern/BrowserLib/Core/Code/Bind.js"></script>
|
|
||||||
<script type="text/javascript" src="extern/BrowserLib/Core/Code/Animation.js"></script>
|
|
||||||
<script type="text/javascript" src="extern/BrowserLib/Core/Code/Convert.js"></script>
|
|
||||||
<script type="text/javascript" src="extern/BrowserLib/Core/Code/LocalStore.js"></script>
|
|
||||||
<script type="text/javascript" src="extern/BrowserLib/Core/Code/Mouse.js"></script>
|
|
||||||
<script type="text/javascript" src="extern/BrowserLib/Core/Code/Keyboard.js"></script>
|
|
||||||
|
|
||||||
<!-- User Interface Window Manager -->
|
|
||||||
<script type="text/javascript" src="extern/BrowserLib/WindowManager/Code/WindowManager.js"></script>
|
|
||||||
<script type="text/javascript" src="extern/BrowserLib/WindowManager/Code/Window.js"></script>
|
|
||||||
<script type="text/javascript" src="extern/BrowserLib/WindowManager/Code/Container.js"></script>
|
|
||||||
<script type="text/javascript" src="extern/BrowserLib/WindowManager/Code/EditBox.js"></script>
|
|
||||||
<script type="text/javascript" src="extern/BrowserLib/WindowManager/Code/Grid.js"></script>
|
|
||||||
<script type="text/javascript" src="extern/BrowserLib/WindowManager/Code/Label.js"></script>
|
|
||||||
<script type="text/javascript" src="extern/BrowserLib/WindowManager/Code/Button.js"></script>
|
|
||||||
|
|
||||||
<!-- Main Application -->
|
|
||||||
<script type="text/javascript" src="Code/WebGL.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/WebGLFont.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/NameMap.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/GLCanvas.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/DataViewReader.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/Console.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/WebSocketConnection.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/MouseInteraction.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/TitleWindow.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/SampleGlobals.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/GridWindow.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/PixelTimeRange.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/TimelineRow.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/TimelineMarkers.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/TimelineWindow.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/ThreadFrame.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/TraceDrop.js"></script>
|
|
||||||
<script type="text/javascript" src="Code/Remotery.js"></script>
|
|
||||||
|
|
||||||
<!-- Shaders -->
|
|
||||||
<script type="text/javascript" src="Code/Shaders/Shared.glsl"></script>
|
|
||||||
<script type="text/javascript" src="Code/Shaders/Grid.glsl"></script>
|
|
||||||
<script type="text/javascript" src="Code/Shaders/Timeline.glsl"></script>
|
|
||||||
<script type="text/javascript" src="Code/Shaders/Window.glsl"></script>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
const remotery = new Remotery();
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,56 +0,0 @@
|
||||||
package dev.euph.engine.datastructs;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class Archetype<T> {
|
|
||||||
//region Fields
|
|
||||||
private final Set<Class<? extends T>> componentClasses;
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Constructor
|
|
||||||
@SafeVarargs
|
|
||||||
public Archetype(Class<? extends T>... componentClasses){
|
|
||||||
this.componentClasses = new HashSet<>();
|
|
||||||
this.componentClasses.addAll(Arrays.asList(componentClasses));
|
|
||||||
}
|
|
||||||
public Archetype(List<Class<? extends T>> componentClasses){
|
|
||||||
this.componentClasses = new HashSet<>();
|
|
||||||
this.componentClasses.addAll(componentClasses);
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
|
|
||||||
public boolean contains(Class<? extends T> componentClass) {
|
|
||||||
for(Class<?extends T> _componentClass : componentClasses){
|
|
||||||
if(componentClass.isAssignableFrom(_componentClass)){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//region Overrides Comparison
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return componentClasses.hashCode();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if(this == obj)return true;
|
|
||||||
if(obj == null || obj.getClass() != this.getClass()) return false;
|
|
||||||
|
|
||||||
Archetype other = (Archetype) obj;
|
|
||||||
return other.hashCode() == this.hashCode();
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Getter
|
|
||||||
public Set<Class<? extends T>> getComponentClasses() {
|
|
||||||
return componentClasses;
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
package dev.euph.engine.datastructs.octree;
|
|
||||||
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class Octree<T> {
|
|
||||||
private final OctreeNode<T> root;
|
|
||||||
|
|
||||||
public Octree(int maxCapacity, Vector3f center, float halfSize) {
|
|
||||||
root = new OctreeNode<T>(maxCapacity, center, halfSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void insert(Vector3f position, T data) {
|
|
||||||
root.insert(position, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<T> query(Vector3f position, float radius) {
|
|
||||||
return root.query(position, radius);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,153 +0,0 @@
|
||||||
package dev.euph.engine.datastructs.octree;
|
|
||||||
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static java.lang.Math.max;
|
|
||||||
import static java.lang.String.join;
|
|
||||||
|
|
||||||
public class OctreeNode<T> {
|
|
||||||
private final int maxCapacity;
|
|
||||||
private final Vector3f center;
|
|
||||||
private final float halfSize;
|
|
||||||
private final List<Vector3f> positions;
|
|
||||||
private final Map<Vector3f, T> dataMap;
|
|
||||||
private OctreeNode<T>[] children;
|
|
||||||
|
|
||||||
public OctreeNode(int maxCapacity, Vector3f center, float halfSize) {
|
|
||||||
this.maxCapacity = maxCapacity;
|
|
||||||
this.center = center;
|
|
||||||
this.halfSize = halfSize;
|
|
||||||
this.positions = new ArrayList<>();
|
|
||||||
this.dataMap = new HashMap<>();
|
|
||||||
this.children = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void insert(Vector3f position, T data) {
|
|
||||||
// Check if the item fits within this node's region
|
|
||||||
if (isOutOfBounds(position)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the node is not subdivided, add the item to its data list and map
|
|
||||||
if (children == null) {
|
|
||||||
positions.add(position);
|
|
||||||
dataMap.put(position, data);
|
|
||||||
|
|
||||||
// Subdivide if the capacity is exceeded
|
|
||||||
if (positions.size() > maxCapacity) {
|
|
||||||
subdivide();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Otherwise, insert the item into the appropriate child node
|
|
||||||
for (OctreeNode<T> child : children) {
|
|
||||||
child.insert(position, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<T> query(Vector3f position, float radius) {
|
|
||||||
List<T> result = new ArrayList<>();
|
|
||||||
|
|
||||||
if (isOutOfBounds(position)) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add data items from this node if their positions are within the query radius
|
|
||||||
for (Vector3f itemPosition : positions) {
|
|
||||||
if (itemPosition.distance(position) <= radius) {
|
|
||||||
result.add(dataMap.get(itemPosition));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively query child nodes
|
|
||||||
if (children != null) {
|
|
||||||
for (OctreeNode<T> child : children) {
|
|
||||||
result.addAll(child.query(position, radius));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOutOfBounds(Vector3f position) {
|
|
||||||
float minX = center.x - halfSize;
|
|
||||||
float maxX = center.x + halfSize;
|
|
||||||
float minY = center.y - halfSize;
|
|
||||||
float maxY = center.y + halfSize;
|
|
||||||
float minZ = center.z - halfSize;
|
|
||||||
float maxZ = center.z + halfSize;
|
|
||||||
|
|
||||||
return (
|
|
||||||
position.x < minX ||
|
|
||||||
position.x > maxX ||
|
|
||||||
position.y < minY ||
|
|
||||||
position.y > maxY ||
|
|
||||||
position.z < minZ ||
|
|
||||||
position.z > maxZ
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void subdivide() {
|
|
||||||
OctreeNode<T>[] childNodes = new OctreeNode[8];
|
|
||||||
|
|
||||||
float quarterSize = halfSize / 2.0f;
|
|
||||||
|
|
||||||
// Calculate centers for each child node
|
|
||||||
Vector3f[] childCenters = new Vector3f[]{
|
|
||||||
new Vector3f(center.x - quarterSize, center.y - quarterSize, center.z - quarterSize),
|
|
||||||
new Vector3f(center.x + quarterSize, center.y - quarterSize, center.z - quarterSize),
|
|
||||||
new Vector3f(center.x - quarterSize, center.y + quarterSize, center.z - quarterSize),
|
|
||||||
new Vector3f(center.x + quarterSize, center.y + quarterSize, center.z - quarterSize),
|
|
||||||
new Vector3f(center.x - quarterSize, center.y - quarterSize, center.z + quarterSize),
|
|
||||||
new Vector3f(center.x + quarterSize, center.y - quarterSize, center.z + quarterSize),
|
|
||||||
new Vector3f(center.x - quarterSize, center.y + quarterSize, center.z + quarterSize),
|
|
||||||
new Vector3f(center.x + quarterSize, center.y + quarterSize, center.z + quarterSize)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create and initialize child nodes
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
childNodes[i] = new OctreeNode<>(maxCapacity, childCenters[i], quarterSize);
|
|
||||||
}
|
|
||||||
children = childNodes; // Assign the initialized children array to the class field
|
|
||||||
|
|
||||||
// Redistribute positions and data from this node to children
|
|
||||||
for (int i = positions.size() - 1; i >= 0; i--) {
|
|
||||||
Vector3f position = positions.get(i);
|
|
||||||
T data = dataMap.remove(position); // Remove data from the current node
|
|
||||||
|
|
||||||
// Find the child that contains the position and add data to it
|
|
||||||
for (OctreeNode<T> child : children) {
|
|
||||||
if (!child.isOutOfBounds(position)) {
|
|
||||||
child.insert(position, data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the position from the current node
|
|
||||||
positions.remove(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Vector3f> getPositions() {
|
|
||||||
List<Vector3f> allPositions = new ArrayList<>(positions);
|
|
||||||
|
|
||||||
if (children != null) {
|
|
||||||
for (OctreeNode<T> child : children) {
|
|
||||||
allPositions.addAll(child.getPositions());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return allPositions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OctreeNode<T>[] getChildren() {
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
package dev.euph.engine.datastructs.pipeline;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
public class Pipeline<Input, Output> {
|
|
||||||
private final Collection<PipelineStage<?, ?>> pipelineStages;
|
|
||||||
|
|
||||||
public Pipeline() {
|
|
||||||
this.pipelineStages = new ArrayList<>();
|
|
||||||
}
|
|
||||||
public Pipeline(PipelineStage<Input, Output> pipelineStage) {
|
|
||||||
pipelineStages = Collections.singletonList(pipelineStage);
|
|
||||||
}
|
|
||||||
private Pipeline(Collection<PipelineStage<?, ?>> pipelineStages) {
|
|
||||||
this.pipelineStages = new ArrayList<>(pipelineStages);
|
|
||||||
}
|
|
||||||
public <NewOutput> Pipeline<Input, NewOutput> addStage(PipelineStage<Output, NewOutput> pipelineStage){
|
|
||||||
final ArrayList<PipelineStage<?, ?>> newPipelineStages = new ArrayList<>(pipelineStages);
|
|
||||||
newPipelineStages.add(pipelineStage);
|
|
||||||
return new Pipeline<>(newPipelineStages);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param input the input of the pipeline
|
|
||||||
* @return the Output of the pipelines execution
|
|
||||||
* @throws PipelineRuntimeException if there is an error durring execution
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public Output execute(Input input) {
|
|
||||||
try {
|
|
||||||
Object output = input;
|
|
||||||
for (final PipelineStage pipelineStage : pipelineStages){
|
|
||||||
output = pipelineStage.execute(output);
|
|
||||||
}
|
|
||||||
return (Output) output;
|
|
||||||
} catch (Exception exception){
|
|
||||||
throw new PipelineRuntimeException(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
package dev.euph.engine.datastructs.pipeline;
|
|
||||||
|
|
||||||
public class PipelineRuntimeException extends RuntimeException{
|
|
||||||
public PipelineRuntimeException(String message) {
|
|
||||||
super("Pipeline Runtime Exception: " + message);
|
|
||||||
}
|
|
||||||
public PipelineRuntimeException(String message, Throwable cause) {
|
|
||||||
super("Pipeline Runtime Exception: " + message, cause);
|
|
||||||
}
|
|
||||||
public PipelineRuntimeException(Throwable cause) {
|
|
||||||
super("Pipeline Runtime Exception: " + cause);
|
|
||||||
}
|
|
||||||
protected PipelineRuntimeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
|
||||||
super("Pipeline Runtime Exception: " + message, cause, enableSuppression, writableStackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
package dev.euph.engine.datastructs.pipeline;
|
|
||||||
|
|
||||||
public interface PipelineStage<Input, Output> {
|
|
||||||
Output execute(Input input);
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package dev.euph.engine.ecs;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public abstract class Component {
|
|
||||||
|
|
||||||
//region Fields
|
|
||||||
protected Entity entity = null;
|
|
||||||
protected List<Class<? extends Component>> requiredComponents = new ArrayList<>();
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
public void OnReady(){}
|
|
||||||
public void OnUpdate(float deltaTime){}
|
|
||||||
public void OnDestroy(){}
|
|
||||||
|
|
||||||
//region Component Management
|
|
||||||
protected void requireComponent(Class<? extends Component> componentClass) {
|
|
||||||
requiredComponents.add(componentClass);
|
|
||||||
}
|
|
||||||
protected boolean hasRequiredComponents(Entity entity) {
|
|
||||||
|
|
||||||
for (Class<? extends Component> requiredClass : requiredComponents) {
|
|
||||||
if (entity.getComponent(requiredClass) == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Getter
|
|
||||||
public Entity getEntity() {
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
package dev.euph.engine.ecs;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class Entity {
|
|
||||||
//region Fields
|
|
||||||
private boolean destroyPending = false;
|
|
||||||
private boolean componentsChanged = false;
|
|
||||||
private String name;
|
|
||||||
private final List<Component> components;
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Constructor
|
|
||||||
public Entity(String name){
|
|
||||||
this.name = name;
|
|
||||||
components = new ArrayList<>();
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Component Management
|
|
||||||
public <T extends Component> T addComponent(T component) {
|
|
||||||
|
|
||||||
if (!component.hasRequiredComponents(this)) {
|
|
||||||
throw new IllegalArgumentException("Cannot add Component. Missing required Components.");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.components.add(component);
|
|
||||||
this.componentsChanged = true;
|
|
||||||
|
|
||||||
if(component.entity != null){
|
|
||||||
component.entity.removeComponent(component.getClass());
|
|
||||||
}
|
|
||||||
component.entity = this;
|
|
||||||
|
|
||||||
return component;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends Component> T getComponent(Class<T> componentClass){
|
|
||||||
for(Component c : components){
|
|
||||||
if(componentClass.isAssignableFrom(c.getClass())){
|
|
||||||
try {
|
|
||||||
return componentClass.cast(c);
|
|
||||||
}catch (ClassCastException e){
|
|
||||||
e.printStackTrace();
|
|
||||||
assert false : "Error: Casting component.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
public <T extends Component> void removeComponent(Class<T> componentClass){
|
|
||||||
for(int i=0; i < components.size(); i++){
|
|
||||||
if(componentClass.isAssignableFrom(components.get(i).getClass())){
|
|
||||||
components.remove(i);
|
|
||||||
this.componentsChanged = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
public void ready(){
|
|
||||||
for (Component component : components) {
|
|
||||||
component.OnReady();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void update(float deltaTime){
|
|
||||||
for (Component component : components) {
|
|
||||||
component.OnUpdate(deltaTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void destroy(){
|
|
||||||
destroyPending = true;
|
|
||||||
for (Component component : components) {
|
|
||||||
component.OnDestroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//region Getter/Setter
|
|
||||||
public boolean isDestroyPending(){
|
|
||||||
return destroyPending;
|
|
||||||
}
|
|
||||||
public List<Component> getComponents() {
|
|
||||||
return components;
|
|
||||||
}
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
protected boolean areComponentsChanged() {
|
|
||||||
return componentsChanged;
|
|
||||||
}
|
|
||||||
protected void setComponentsChanged(boolean componentsChanged) {
|
|
||||||
this.componentsChanged = componentsChanged;
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
}
|
|
|
@ -1,186 +0,0 @@
|
||||||
package dev.euph.engine.ecs;
|
|
||||||
|
|
||||||
import dev.euph.engine.datastructs.Archetype;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class Scene {
|
|
||||||
|
|
||||||
//region Fields
|
|
||||||
private boolean isRunning = false;
|
|
||||||
private final List<Entity> entities;
|
|
||||||
private final Map<Archetype<Component>, List<Entity>> entitiesByArchetype;
|
|
||||||
private final Map<Class<? extends Component>, Set<Class<? extends Component>>> subclassesByComponent;
|
|
||||||
private final Map<Class<? extends Component>, List<Component>> componentInstancesByType;
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Constructor
|
|
||||||
public Scene(){
|
|
||||||
entities = new ArrayList<>();
|
|
||||||
entitiesByArchetype = new HashMap<>();
|
|
||||||
subclassesByComponent = new HashMap<>();
|
|
||||||
componentInstancesByType = new HashMap<>();
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
public void start(){
|
|
||||||
if(isRunning)return;
|
|
||||||
isRunning = true;
|
|
||||||
|
|
||||||
for(Entity entity : entities){
|
|
||||||
entity.ready();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void update(float deltaTime) {
|
|
||||||
List<Entity> entitiesToRemove = new ArrayList<>();
|
|
||||||
|
|
||||||
for (Entity entity : entities) {
|
|
||||||
entity.update(deltaTime);
|
|
||||||
|
|
||||||
if (entity.isDestroyPending()) {
|
|
||||||
removeEntityFromArchetype(entity);
|
|
||||||
entitiesToRemove.add(entity);
|
|
||||||
|
|
||||||
// Remove the components of the entity from the componentInstancesByType map
|
|
||||||
for (Component component : entity.getComponents()) {
|
|
||||||
Class<? extends Component> componentClass = component.getClass();
|
|
||||||
List<Component> componentInstances = componentInstancesByType.get(componentClass);
|
|
||||||
if (componentInstances != null) {
|
|
||||||
componentInstances.remove(component);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (entity.areComponentsChanged()) {
|
|
||||||
removeEntityFromArchetype(entity);
|
|
||||||
|
|
||||||
List<Class<? extends Component>> components = new ArrayList<>();
|
|
||||||
for (Component component : entity.getComponents()) {
|
|
||||||
components.add(component.getClass());
|
|
||||||
}
|
|
||||||
Archetype<Component> newArchetype = new Archetype<>(components);
|
|
||||||
List<Entity> archetypeEntities = entitiesByArchetype.computeIfAbsent(newArchetype, key -> new ArrayList<>());
|
|
||||||
archetypeEntities.add(entity);
|
|
||||||
|
|
||||||
for (Component component : entity.getComponents()) {
|
|
||||||
Class<? extends Component> componentClass = component.getClass();
|
|
||||||
List<Component> componentInstances = componentInstancesByType.computeIfAbsent(componentClass, key -> new ArrayList<>());
|
|
||||||
componentInstances.add(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
entity.setComponentsChanged(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
entities.removeAll(entitiesToRemove);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addEntityToScene(Entity entity) {
|
|
||||||
if (entities.contains(entity)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
entities.add(entity);
|
|
||||||
|
|
||||||
List<Class<? extends Component>> components = new ArrayList<>();
|
|
||||||
for (Component component : entity.getComponents()) {
|
|
||||||
components.add(component.getClass());
|
|
||||||
}
|
|
||||||
Archetype<Component> newArchetype = new Archetype<>(components);
|
|
||||||
|
|
||||||
for (Class<? extends Component> componentClass : components) {
|
|
||||||
// Update subclasses mapping
|
|
||||||
updateSubclassesMapping(componentClass);
|
|
||||||
|
|
||||||
// Add component instance to the componentInstancesByType map
|
|
||||||
List<Component> componentInstances = componentInstancesByType.computeIfAbsent(componentClass, key -> new ArrayList<>());
|
|
||||||
componentInstances.add(entity.getComponent(componentClass)); // added
|
|
||||||
|
|
||||||
// Handle subclasses: If the componentClass has a superclass, add the component instance to its list too
|
|
||||||
Class<?> superClass = componentClass.getSuperclass();
|
|
||||||
while (Component.class.isAssignableFrom(superClass)) {
|
|
||||||
if (Component.class.isAssignableFrom(superClass)) {
|
|
||||||
List<Component> superClassInstances = componentInstancesByType.computeIfAbsent(superClass.asSubclass(Component.class), key -> new ArrayList<>());
|
|
||||||
superClassInstances.add(entity.getComponent(componentClass));
|
|
||||||
}
|
|
||||||
superClass = superClass.getSuperclass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Entity> archetypeEntities = entitiesByArchetype.computeIfAbsent(newArchetype, key -> new ArrayList<>());
|
|
||||||
archetypeEntities.add(entity);
|
|
||||||
|
|
||||||
if (!isRunning) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
entity.ready();
|
|
||||||
}
|
|
||||||
@SafeVarargs
|
|
||||||
public final List<Entity> getEntitiesWithComponents(boolean exactMatch, Class<? extends Component>... componentClasses) {
|
|
||||||
List<Entity> filteredEntities = new ArrayList<>();
|
|
||||||
|
|
||||||
if (exactMatch) {
|
|
||||||
Archetype<Component> requestedArchetype = new Archetype<>(componentClasses);
|
|
||||||
List<Entity> matchingEntities = entitiesByArchetype.get(requestedArchetype);
|
|
||||||
if (matchingEntities != null) {
|
|
||||||
filteredEntities.addAll(matchingEntities);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Set<Class<? extends Component>> componentSet = new HashSet<>();
|
|
||||||
for (Class<? extends Component> componentClass : componentClasses) {
|
|
||||||
componentSet.add(componentClass);
|
|
||||||
componentSet.addAll(subclassesByComponent.getOrDefault(componentClass, Collections.emptySet()));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Class<? extends Component> componentClass : componentSet) {
|
|
||||||
List<Component> componentInstances = componentInstancesByType.get(componentClass);
|
|
||||||
if (componentInstances != null) {
|
|
||||||
filteredEntities.addAll(componentInstances.stream()
|
|
||||||
.map(Component::getEntity)
|
|
||||||
.toList());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return filteredEntities;
|
|
||||||
}
|
|
||||||
public List<Component> getComponentInstances(Class<? extends Component> componentClass){
|
|
||||||
return componentInstancesByType.get(componentClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
//region ECS Utility Functions
|
|
||||||
private void removeEntityFromArchetype(Entity entity) {
|
|
||||||
for (Map.Entry<Archetype<Component>, List<Entity>> entry : entitiesByArchetype.entrySet()) {
|
|
||||||
Archetype<Component> archetype = entry.getKey();
|
|
||||||
List<Entity> archetypeEntities = entry.getValue();
|
|
||||||
if (archetypeEntities.remove(entity)) {
|
|
||||||
if (archetypeEntities.isEmpty()) {
|
|
||||||
entitiesByArchetype.remove(archetype);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void updateSubclassesMapping(Class<? extends Component> componentClass) {
|
|
||||||
Set<Class<? extends Component>> subclasses = getSubclasses(componentClass);
|
|
||||||
subclassesByComponent.put(componentClass, subclasses);
|
|
||||||
}
|
|
||||||
private Set<Class<? extends Component>> getSubclasses(Class<? extends Component> componentClass) {
|
|
||||||
Set<Class<? extends Component>> subclasses = new HashSet<>();
|
|
||||||
for (Class<? extends Component> clazz : subclassesByComponent.keySet()) {
|
|
||||||
if (componentClass.isAssignableFrom(clazz)) {
|
|
||||||
subclasses.add(clazz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return subclasses;
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Getter
|
|
||||||
public boolean isRunning() {
|
|
||||||
return isRunning;
|
|
||||||
}
|
|
||||||
public List<Entity> getEntities() {
|
|
||||||
return entities;
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
package dev.euph.engine.ecs.components;
|
|
||||||
|
|
||||||
import dev.euph.engine.ecs.Component;
|
|
||||||
import dev.euph.engine.managers.Window;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
|
|
||||||
|
|
||||||
public class Camera extends Component {
|
|
||||||
|
|
||||||
//region Fields
|
|
||||||
public float fov = 90f;
|
|
||||||
public float nearPlane = 0.1f;
|
|
||||||
public float farPlane = 500f;
|
|
||||||
public Color backgroundColor = new Color(0, 200, 255);
|
|
||||||
public Window window;
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Constructor
|
|
||||||
public Camera(Window window){
|
|
||||||
requireComponent(Transform.class);
|
|
||||||
this.window = window;
|
|
||||||
}
|
|
||||||
public Camera(Window window, float fov, float nearPlane, float farPlane, Color backgroundColor) {
|
|
||||||
requireComponent(Transform.class);
|
|
||||||
this.window = window;
|
|
||||||
this.fov = fov;
|
|
||||||
this.nearPlane = nearPlane;
|
|
||||||
this.farPlane = farPlane;
|
|
||||||
this.backgroundColor = backgroundColor;
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
|
|
||||||
public Window getWindow() {
|
|
||||||
return window;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
package dev.euph.engine.ecs.components;
|
|
||||||
|
|
||||||
import dev.euph.engine.ecs.Component;
|
|
||||||
import dev.euph.engine.render.Material;
|
|
||||||
import dev.euph.engine.resources.Mesh;
|
|
||||||
|
|
||||||
public class MeshRenderer extends Component {
|
|
||||||
|
|
||||||
//region Fields
|
|
||||||
private final Mesh mesh;
|
|
||||||
private final Material material;
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Constructor
|
|
||||||
public MeshRenderer(Mesh mesh, Material material) {
|
|
||||||
requireComponent(Transform.class);
|
|
||||||
this.mesh = mesh;
|
|
||||||
this.material = material;
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Getter
|
|
||||||
public Mesh getMesh() {
|
|
||||||
return mesh;
|
|
||||||
}
|
|
||||||
public Material getMaterial() {
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
package dev.euph.engine.ecs.components;
|
|
||||||
|
|
||||||
import dev.euph.engine.ecs.Component;
|
|
||||||
import dev.euph.engine.math.TransformationMatrix;
|
|
||||||
import org.joml.Matrix4f;
|
|
||||||
import org.joml.Quaternionf;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import static java.lang.Math.toRadians;
|
|
||||||
|
|
||||||
public class Transform extends Component {
|
|
||||||
//region Fields
|
|
||||||
private Vector3f position;
|
|
||||||
private Vector3f rotation;
|
|
||||||
private Vector3f scale;
|
|
||||||
private TransformationMatrix transformationMatrix;
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Constructor
|
|
||||||
public Transform() {
|
|
||||||
this(new Vector3f(), new Vector3f(), new Vector3f(1));
|
|
||||||
}
|
|
||||||
public Transform(Vector3f position, Vector3f rotation, Vector3f scale) {
|
|
||||||
this.position = position;
|
|
||||||
this.rotation = rotation;
|
|
||||||
this.scale = scale;
|
|
||||||
calculateTransformationMatrix();
|
|
||||||
}
|
|
||||||
public Transform(Transform transform) {
|
|
||||||
this.position = transform.position;
|
|
||||||
this.rotation = transform.rotation;
|
|
||||||
this.scale = transform.scale;
|
|
||||||
calculateTransformationMatrix();
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Utility
|
|
||||||
private void calculateTransformationMatrix() {
|
|
||||||
transformationMatrix = new TransformationMatrix(this);
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Getter/Setter
|
|
||||||
public Vector3f getPosition() {
|
|
||||||
return new Vector3f(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPosition(Vector3f position) {
|
|
||||||
this.position = position;
|
|
||||||
calculateTransformationMatrix();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3f getRotation() {
|
|
||||||
return new Vector3f(rotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRotation(Vector3f rotation) {
|
|
||||||
this.rotation = rotation;
|
|
||||||
calculateTransformationMatrix();
|
|
||||||
}
|
|
||||||
public Vector3f getScale() {
|
|
||||||
return new Vector3f(scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setScale(Vector3f scale) {
|
|
||||||
this.scale = scale;
|
|
||||||
calculateTransformationMatrix();
|
|
||||||
}
|
|
||||||
public TransformationMatrix getTransformationMatrix() {
|
|
||||||
return transformationMatrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3f getUp() {
|
|
||||||
Quaternionf q = new Quaternionf().rotateXYZ(
|
|
||||||
(float) toRadians(rotation.x),
|
|
||||||
(float) toRadians(rotation.y),
|
|
||||||
(float) toRadians(rotation.z)
|
|
||||||
);
|
|
||||||
return q.positiveY(new Vector3f());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3f getDown() {
|
|
||||||
return new Vector3f(getUp()).negate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3f getRight() {
|
|
||||||
Quaternionf q = new Quaternionf().rotateY((float) toRadians(rotation.y));
|
|
||||||
return q.positiveX(new Vector3f());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3f getLeft() {
|
|
||||||
return new Vector3f(getRight()).negate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3f getForward() {
|
|
||||||
return new Vector3f(getBack()).negate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3f getBack() {
|
|
||||||
Quaternionf q = new Quaternionf().rotateXYZ(
|
|
||||||
(float) toRadians(rotation.x),
|
|
||||||
(float) toRadians(rotation.y),
|
|
||||||
(float) toRadians(rotation.z)
|
|
||||||
);
|
|
||||||
return q.positiveZ(new Vector3f());
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package dev.euph.engine.ecs.components.lights;
|
|
||||||
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
|
|
||||||
public class DirectionalLight extends LightSource{
|
|
||||||
public DirectionalLight(Color color) {
|
|
||||||
super(color, LightType.Directional);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package dev.euph.engine.ecs.components.lights;
|
|
||||||
|
|
||||||
import dev.euph.engine.ecs.Component;
|
|
||||||
import dev.euph.engine.ecs.components.Transform;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
|
|
||||||
public abstract class LightSource extends Component {
|
|
||||||
|
|
||||||
public static class LightType {
|
|
||||||
public static final int Directional = 0;
|
|
||||||
public static final int Point = 1;
|
|
||||||
public static final int Spot = 2;
|
|
||||||
public static final int Area = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
//region Fields
|
|
||||||
private Color color;
|
|
||||||
private final int type;
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Constructor
|
|
||||||
public LightSource(Color color, int type) {
|
|
||||||
requireComponent(Transform.class);
|
|
||||||
this.color = color;
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Getter
|
|
||||||
public Color getColor() {
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setColor(Color color) {
|
|
||||||
this.color = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
package dev.euph.engine.ecs.components.lights;
|
|
||||||
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
|
|
||||||
public class PointLight extends LightSource{
|
|
||||||
private Vector3f attenuation;
|
|
||||||
public PointLight(Color color, Vector3f attenuation) {
|
|
||||||
super(color, LightType.Point);
|
|
||||||
this.attenuation = attenuation;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package dev.euph.engine.ecs.components.lights;
|
|
||||||
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
|
|
||||||
public class SpotLight extends LightSource{
|
|
||||||
//region Fields
|
|
||||||
public float coneAngle;
|
|
||||||
public float hardCutoff;
|
|
||||||
public float softCutoff;
|
|
||||||
private Vector3f attenuation;
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Constructor
|
|
||||||
public SpotLight(Color color, Vector3f attenuation, float coneAngle, float hardCutoff, float softCutoff) {
|
|
||||||
super(color, LightType.Spot);
|
|
||||||
this.attenuation = attenuation;
|
|
||||||
this.coneAngle = coneAngle;
|
|
||||||
this.hardCutoff = hardCutoff;
|
|
||||||
this.softCutoff = softCutoff;
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
package dev.euph.engine.managers;
|
|
||||||
|
|
||||||
public class Input {
|
|
||||||
/* TODO:
|
|
||||||
- Add Some Kind of Input Action map
|
|
||||||
- Input Action Maps should be replace and editable.
|
|
||||||
- Input Action should have different Modes: Button, Axis, 2Axis
|
|
||||||
- Any Combination of Inputs that fits should be able to be bound to actions.
|
|
||||||
- Inputs that dont Fit should either be splittable in smaller parts, or combinable with some struct.
|
|
||||||
- The Output of an Input Action should either be directly readable, or/-and bindable to some callback.
|
|
||||||
*/
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
package dev.euph.engine.managers;
|
|
||||||
|
|
||||||
import dev.euph.engine.render.Shader;
|
|
||||||
import dev.euph.engine.util.Path;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class ShaderManager {
|
|
||||||
|
|
||||||
private final Map<String, Shader> shaders;
|
|
||||||
|
|
||||||
public ShaderManager() {
|
|
||||||
shaders = new HashMap<>();
|
|
||||||
try {
|
|
||||||
Shader defaultShader = new Shader(
|
|
||||||
"default",
|
|
||||||
Path.VERTEX_SHADERS + "default.glsl",
|
|
||||||
Path.FRAGMENT_SHADERS + "default.glsl"
|
|
||||||
);
|
|
||||||
addShader(defaultShader);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addShader(Shader shader) {
|
|
||||||
shaders.put(shader.getName(), shader);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Shader getShader(String shaderName) {
|
|
||||||
return shaders.get(shaderName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cleanup() {
|
|
||||||
shaders.values().forEach(Shader::close);
|
|
||||||
shaders.clear();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,157 +0,0 @@
|
||||||
package dev.euph.engine.managers;
|
|
||||||
|
|
||||||
import dev.euph.engine.util.Time;
|
|
||||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
|
||||||
import org.lwjgl.glfw.GLFWVidMode;
|
|
||||||
import org.lwjgl.glfw.GLFWWindowSizeCallback;
|
|
||||||
import org.lwjgl.opengl.GL;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
|
|
||||||
import static org.lwjgl.glfw.GLFW.*;
|
|
||||||
import static org.lwjgl.opengl.GL11.glViewport;
|
|
||||||
|
|
||||||
public class Window {
|
|
||||||
private final long id;
|
|
||||||
private final int initalWidth;
|
|
||||||
private int width;
|
|
||||||
private final int initalHeight;
|
|
||||||
private int height;
|
|
||||||
private Time time;
|
|
||||||
private GLFWWindowSizeCallback windowSizeCallback;
|
|
||||||
public Window(int width, int height, String title){
|
|
||||||
//setup error callback to use for errors
|
|
||||||
GLFWErrorCallback.createPrint(System.err).set();
|
|
||||||
|
|
||||||
//initialize GLFW
|
|
||||||
if (!glfwInit()) {
|
|
||||||
throw new IllegalStateException("Unable to initialize GLFW");
|
|
||||||
}
|
|
||||||
|
|
||||||
time = new Time();
|
|
||||||
|
|
||||||
//configure the window
|
|
||||||
glfwDefaultWindowHints();
|
|
||||||
|
|
||||||
//set the window to be resizable
|
|
||||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
|
||||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
|
||||||
initalWidth = width;
|
|
||||||
this.width = width;
|
|
||||||
initalHeight = height;
|
|
||||||
this.height = height;
|
|
||||||
|
|
||||||
//create the window
|
|
||||||
id = glfwCreateWindow(width, height, title, 0, 0);
|
|
||||||
if (id == 0) {
|
|
||||||
throw new RuntimeException("Failed to create the GLFW window");
|
|
||||||
}
|
|
||||||
|
|
||||||
//set up the callback to close the window when the user presses the 'X'
|
|
||||||
glfwSetKeyCallback(id, (window, key, scancode, action, mods) -> {
|
|
||||||
if(key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) {
|
|
||||||
glfwSetWindowShouldClose(window, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
windowSizeCallback = new GLFWWindowSizeCallback() {
|
|
||||||
@Override
|
|
||||||
public void invoke(long window, int newWidth, int newHeight) {
|
|
||||||
adjustViewport(newWidth, newHeight);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
glfwSetWindowSizeCallback(id, windowSizeCallback);
|
|
||||||
|
|
||||||
//Get the resolution of the primary monitor
|
|
||||||
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
|
||||||
//Center the window
|
|
||||||
assert vidmode != null;
|
|
||||||
glfwSetWindowPos(
|
|
||||||
id,
|
|
||||||
(vidmode.width() - width) / 2,
|
|
||||||
(vidmode.height() - height) / 2
|
|
||||||
);
|
|
||||||
|
|
||||||
//set the opengl context to the current window
|
|
||||||
glfwMakeContextCurrent(id);
|
|
||||||
//enable v-sync
|
|
||||||
glfwSwapInterval(1);
|
|
||||||
//make the window visible
|
|
||||||
glfwShowWindow(id);
|
|
||||||
//bind glfw window context for lwjgl
|
|
||||||
GL.createCapabilities();
|
|
||||||
//start the delta time
|
|
||||||
time.startDeltaTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void adjustViewport(int newWidth, int newHeight) {
|
|
||||||
this.width = newWidth;
|
|
||||||
this.height = newHeight;
|
|
||||||
|
|
||||||
float aspectRatio = (float) initalWidth / initalHeight;
|
|
||||||
|
|
||||||
int newViewportWidth = newWidth;
|
|
||||||
int newViewportHeight = (int) (newWidth / aspectRatio);
|
|
||||||
|
|
||||||
if (newViewportHeight > newHeight) {
|
|
||||||
newViewportWidth = (int) (newHeight * aspectRatio);
|
|
||||||
newViewportHeight = newHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the position to center the viewport
|
|
||||||
int x = (newWidth - newViewportWidth) / 2;
|
|
||||||
int y = (newHeight - newViewportHeight) / 2;
|
|
||||||
|
|
||||||
// Set the OpenGL viewport
|
|
||||||
glViewport(x, y, newViewportWidth, newViewportHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCloseRequested() {
|
|
||||||
return glfwWindowShouldClose(id);
|
|
||||||
}
|
|
||||||
public void updateWindow() {
|
|
||||||
//update the window
|
|
||||||
glfwSwapBuffers(id);
|
|
||||||
|
|
||||||
//process inputs
|
|
||||||
glfwPollEvents();
|
|
||||||
//update the delta time
|
|
||||||
time.updateDeltaTime();
|
|
||||||
}
|
|
||||||
public void destroyWindowy() {
|
|
||||||
//run the cleanup
|
|
||||||
cleanUp();
|
|
||||||
//terminate GLFW and free the error callback
|
|
||||||
glfwTerminate();
|
|
||||||
Objects.requireNonNull(glfwSetErrorCallback(null)).free();
|
|
||||||
}
|
|
||||||
private void cleanUp() {
|
|
||||||
//free the window callbacks and destroy the window
|
|
||||||
glfwFreeCallbacks(id);
|
|
||||||
glfwDestroyWindow(id);
|
|
||||||
}
|
|
||||||
public long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getInitalWidth() {
|
|
||||||
return initalWidth;
|
|
||||||
}
|
|
||||||
public int getInitalHeight() {
|
|
||||||
return initalHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Time getTime() {
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getWidth() {
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getHeight() {
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
package dev.euph.engine.math;
|
|
||||||
|
|
||||||
import dev.euph.engine.ecs.components.Camera;
|
|
||||||
import dev.euph.engine.managers.Window;
|
|
||||||
import org.joml.Matrix4f;
|
|
||||||
import org.lwjgl.BufferUtils;
|
|
||||||
|
|
||||||
import java.nio.IntBuffer;
|
|
||||||
|
|
||||||
import static org.lwjgl.glfw.GLFW.glfwGetWindowSize;
|
|
||||||
|
|
||||||
public class ProjectionMatrix extends Matrix4f {
|
|
||||||
|
|
||||||
public ProjectionMatrix(Camera camera) {
|
|
||||||
super();
|
|
||||||
calcualteProjection(camera.getWindow(), camera.fov, camera.nearPlane, camera.farPlane);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void calcualteProjection(Window window, float fov, float nearPlane, float farPlane) {
|
|
||||||
float aspectRatio = (float) window.getInitalWidth() / (float) window.getInitalHeight();
|
|
||||||
|
|
||||||
float y_scale = (float) (1f / Math.tan(Math.toRadians(fov / 2f))) * aspectRatio;
|
|
||||||
float x_scale = y_scale / aspectRatio;
|
|
||||||
float frustum_length = farPlane - nearPlane;
|
|
||||||
|
|
||||||
m00(x_scale);
|
|
||||||
m11(y_scale);
|
|
||||||
m22(-((farPlane + nearPlane) / frustum_length));
|
|
||||||
m23(-1);
|
|
||||||
m32(-((2 * nearPlane * farPlane) / frustum_length));
|
|
||||||
m33(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
package dev.euph.engine.math;
|
|
||||||
|
|
||||||
import dev.euph.engine.ecs.components.Transform;
|
|
||||||
import org.joml.Matrix4f;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
public class TransformationMatrix extends Matrix4f {
|
|
||||||
|
|
||||||
public TransformationMatrix(Vector3f translation, Vector3f rotation, Vector3f scale) {
|
|
||||||
super();
|
|
||||||
super.identity();
|
|
||||||
super.translate(translation);
|
|
||||||
super.rotate((float) Math.toRadians(rotation.x % 360), new Vector3f(1, 0, 0));
|
|
||||||
super.rotate((float) Math.toRadians(rotation.y % 360), new Vector3f(0, 1, 0));
|
|
||||||
super.rotate((float) Math.toRadians(rotation.z % 360), new Vector3f(0, 0, 1));
|
|
||||||
super.scale(scale);
|
|
||||||
}
|
|
||||||
public TransformationMatrix(Transform transform) {
|
|
||||||
super();
|
|
||||||
super.identity();
|
|
||||||
super.translate(transform.getPosition());
|
|
||||||
Vector3f rotation = transform.getRotation();
|
|
||||||
super.rotate((float) Math.toRadians(rotation.x % 360), new Vector3f(1, 0, 0));
|
|
||||||
super.rotate((float) Math.toRadians(rotation.y % 360), new Vector3f(0, 1, 0));
|
|
||||||
super.rotate((float) Math.toRadians(rotation.z % 360), new Vector3f(0, 0, 1));
|
|
||||||
super.scale(transform.getScale());
|
|
||||||
}
|
|
||||||
public TransformationMatrix() {
|
|
||||||
super();
|
|
||||||
super.identity();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package dev.euph.engine.math;
|
|
||||||
|
|
||||||
import dev.euph.engine.ecs.components.Camera;
|
|
||||||
import dev.euph.engine.ecs.components.Transform;
|
|
||||||
import org.joml.Math;
|
|
||||||
import org.joml.Matrix4f;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
public class ViewMatrix extends Matrix4f {
|
|
||||||
|
|
||||||
public ViewMatrix(Camera camera) {
|
|
||||||
super();
|
|
||||||
Transform cameraTransform = camera.getEntity().getComponent(Transform.class);
|
|
||||||
super.identity();
|
|
||||||
super.rotate(Math.toRadians(cameraTransform.getRotation().x), new Vector3f(1, 0, 0));
|
|
||||||
super.rotate(Math.toRadians(cameraTransform.getRotation().y), new Vector3f(0, 1, 0));
|
|
||||||
super.rotate(Math.toRadians(cameraTransform.getRotation().z), new Vector3f(0, 0, 1));
|
|
||||||
Vector3f pos = new Vector3f(cameraTransform.getPosition());
|
|
||||||
super.translate(pos.negate());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
package dev.euph.engine.render;
|
|
||||||
|
|
||||||
import dev.euph.engine.ecs.Entity;
|
|
||||||
import dev.euph.engine.ecs.components.Camera;
|
|
||||||
import dev.euph.engine.ecs.components.MeshRenderer;
|
|
||||||
import dev.euph.engine.ecs.components.Transform;
|
|
||||||
import dev.euph.engine.managers.ShaderManager;
|
|
||||||
import dev.euph.engine.math.ProjectionMatrix;
|
|
||||||
import dev.euph.engine.math.TransformationMatrix;
|
|
||||||
import dev.euph.engine.math.ViewMatrix;
|
|
||||||
import dev.euph.engine.resources.Mesh;
|
|
||||||
import dev.euph.engine.resources.TexturedMesh;
|
|
||||||
import dev.euph.engine.ecs.Scene;
|
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL30.*;
|
|
||||||
public class ForwardRenderer implements IRenderPipeline {
|
|
||||||
//region Fields
|
|
||||||
private Scene scene;
|
|
||||||
private final ShaderManager shaderManager;
|
|
||||||
public Camera activeCamera;
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Constructor
|
|
||||||
public ForwardRenderer(Scene scene, ShaderManager shaderManager) {
|
|
||||||
this.scene = scene;
|
|
||||||
this.shaderManager = shaderManager;
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Rendering
|
|
||||||
public void init(){
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
}
|
|
||||||
public void render() {
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
if (activeCamera == null) {
|
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
glClearColor(
|
|
||||||
activeCamera.backgroundColor.getRed(),
|
|
||||||
activeCamera.backgroundColor.getGreen(),
|
|
||||||
activeCamera.backgroundColor.getBlue(),
|
|
||||||
activeCamera.backgroundColor.getAlpha()
|
|
||||||
);
|
|
||||||
|
|
||||||
Shader shader = shaderManager.getShader("default");
|
|
||||||
if (shader == null) return;
|
|
||||||
|
|
||||||
shader.start();
|
|
||||||
ViewMatrix viewMatrix = new ViewMatrix(activeCamera);
|
|
||||||
shader.loadMatrix4(shader.getUniformLocation("viewMatrix"), viewMatrix);
|
|
||||||
ProjectionMatrix projectionMatrix = new ProjectionMatrix(activeCamera);
|
|
||||||
shader.loadMatrix4(shader.getUniformLocation("projectionMatrix"), projectionMatrix);
|
|
||||||
|
|
||||||
shader.stop();
|
|
||||||
|
|
||||||
for (Entity entity : scene.getEntitiesWithComponents(false, MeshRenderer.class)) {
|
|
||||||
renderEntity(entity, entity.getComponent(MeshRenderer.class));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void renderEntity(Entity entity, MeshRenderer meshRenderer) {
|
|
||||||
Mesh mesh = meshRenderer.getMesh();
|
|
||||||
Material material = meshRenderer.getMaterial();
|
|
||||||
Shader shader = material.getShader();
|
|
||||||
|
|
||||||
if (shader == null || mesh == null) return;
|
|
||||||
|
|
||||||
shader.start();
|
|
||||||
shader.bindAttributes();
|
|
||||||
|
|
||||||
TransformationMatrix transformationMatrix = entity.getComponent(Transform.class).getTransformationMatrix();
|
|
||||||
shader.loadMatrix4(shader.getUniformLocation("transformationMatrix"), transformationMatrix);
|
|
||||||
|
|
||||||
material.useMaterial();
|
|
||||||
|
|
||||||
if (mesh instanceof TexturedMesh texturedMesh) {
|
|
||||||
glBindVertexArray(texturedMesh.getVaoId());
|
|
||||||
} else {
|
|
||||||
glBindVertexArray(mesh.getVaoId());
|
|
||||||
}
|
|
||||||
|
|
||||||
shader.enableAttributes();
|
|
||||||
|
|
||||||
glDrawElements(GL_TRIANGLES, mesh.getVertexCount(), GL_UNSIGNED_INT, 0);
|
|
||||||
|
|
||||||
shader.disableAttributes();
|
|
||||||
glBindVertexArray(0);
|
|
||||||
|
|
||||||
shader.stop();
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region Getter/Setter
|
|
||||||
public Scene getScene() {
|
|
||||||
return scene;
|
|
||||||
}
|
|
||||||
public void setScene(Scene scene) {
|
|
||||||
this.scene = scene;
|
|
||||||
}
|
|
||||||
public Camera getActiveCamera() {
|
|
||||||
return activeCamera;
|
|
||||||
}
|
|
||||||
public void setActiveCamera(Camera activeCamera) {
|
|
||||||
this.activeCamera = activeCamera;
|
|
||||||
}
|
|
||||||
//endregion
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
package dev.euph.engine.render;
|
|
||||||
|
|
||||||
public interface IRenderPipeline {
|
|
||||||
void render();
|
|
||||||
void init();
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
package dev.euph.engine.render;
|
|
||||||
|
|
||||||
import dev.euph.engine.resources.Texture;
|
|
||||||
import org.lwjgl.opengl.GL30;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
|
|
||||||
|
|
||||||
public class Material {
|
|
||||||
|
|
||||||
private final Shader shader;
|
|
||||||
private Texture albedoTexture;
|
|
||||||
private Color color;
|
|
||||||
|
|
||||||
public Material(Shader shader) {
|
|
||||||
this.shader = shader;
|
|
||||||
this.albedoTexture = null;
|
|
||||||
this.color = Color.WHITE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAlbedoTexture(Texture albedoTexture) {
|
|
||||||
this.albedoTexture = albedoTexture;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setColor(Color color) {
|
|
||||||
this.color = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void useMaterial() {
|
|
||||||
shader.start();
|
|
||||||
|
|
||||||
// Bind albedoTexture if it is set
|
|
||||||
int useAlbedoTextureLocation = shader.getUniformLocation("useAlbedoTexture");
|
|
||||||
if (albedoTexture != null) {
|
|
||||||
int albedoTextureLocation = shader.getUniformLocation("albedoTexture");
|
|
||||||
shader.loadInt(albedoTextureLocation, 0); // Use texture unit 0
|
|
||||||
GL30.glActiveTexture(GL30.GL_TEXTURE0);
|
|
||||||
GL30.glBindTexture(GL_TEXTURE_2D, albedoTexture.getId());
|
|
||||||
|
|
||||||
shader.loadBoolean(useAlbedoTextureLocation, true);
|
|
||||||
} else {
|
|
||||||
shader.loadBoolean(useAlbedoTextureLocation, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
int colorLocation = shader.getUniformLocation("color");
|
|
||||||
shader.loadVector4(colorLocation, new org.joml.Vector4f(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, 1f));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void cleanup() {
|
|
||||||
shader.close();
|
|
||||||
if (albedoTexture != null) {
|
|
||||||
GL30.glDeleteTextures(albedoTexture.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Shader getShader() {
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue