WASM: porting jbox2d benchmark to WebAssembly

This commit is contained in:
Alexey Andreev 2016-09-29 20:05:32 +03:00
parent f890680e90
commit cc0c68e809
16 changed files with 425 additions and 62 deletions

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import org.teavm.interop.Import;
import org.teavm.jso.JSBody; import org.teavm.jso.JSBody;
public class TFloat extends TNumber implements TComparable<TFloat> { public class TFloat extends TNumber implements TComparable<TFloat> {
@ -89,12 +90,19 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
} }
@JSBody(params = "v", script = "return isNaN(v);") @JSBody(params = "v", script = "return isNaN(v);")
@Import(module = "runtime", name = "isNaN")
public static native boolean isNaN(float v); public static native boolean isNaN(float v);
@JSBody(params = "v", script = "return !isFinite(v);") public static boolean isInfinite(float v) {
public static native boolean isInfinite(float v); return !isFinite(v);
}
@JSBody(params = "v", script = "return isFinite(v);")
@Import(module = "runtime", name = "isFinite")
private static native boolean isFinite(float v);
@JSBody(params = {}, script = "return NaN;") @JSBody(params = {}, script = "return NaN;")
@Import(module = "runtime", name = "getNaN")
private static native float getNaN(); private static native float getNaN();
public static float parseFloat(TString string) throws TNumberFormatException { public static float parseFloat(TString string) throws TNumberFormatException {

View File

@ -16,11 +16,8 @@
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import org.teavm.backend.javascript.spi.GeneratedBy; import org.teavm.backend.javascript.spi.GeneratedBy;
import org.teavm.interop.Import;
/**
*
* @author Alexey Andreev
*/
public final class TMath extends TObject { public final class TMath extends TObject {
public static final double E = 2.71828182845904523536; public static final double E = 2.71828182845904523536;
public static final double PI = 3.14159265358979323846; public static final double PI = 3.14159265358979323846;
@ -29,21 +26,27 @@ public final class TMath extends TObject {
} }
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "math", name = "sin")
public static native double sin(double a); public static native double sin(double a);
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "math", name = "cos")
public static native double cos(double a); public static native double cos(double a);
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "math", name = "tan")
public static native double tan(double a); public static native double tan(double a);
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "math", name = "asin")
public static native double asin(double a); public static native double asin(double a);
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "math", name = "acos")
public static native double acos(double a); public static native double acos(double a);
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "math", name = "atan")
public static native double atan(double a); public static native double atan(double a);
public static double toRadians(double angdeg) { public static double toRadians(double angdeg) {
@ -55,9 +58,11 @@ public final class TMath extends TObject {
} }
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "math", name = "exp")
public static native double exp(double a); public static native double exp(double a);
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "math", name = "log")
public static native double log(double a); public static native double log(double a);
public static double log10(double a) { public static double log10(double a) {
@ -65,6 +70,7 @@ public final class TMath extends TObject {
} }
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "math", name = "sqrt")
public static native double sqrt(double a); public static native double sqrt(double a);
public static double cbrt(double a) { public static double cbrt(double a) {
@ -77,12 +83,15 @@ public final class TMath extends TObject {
} }
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "math", name = "ceil")
public static native double ceil(double a); public static native double ceil(double a);
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "math", name = "floor")
public static native double floor(double a); public static native double floor(double a);
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "math", name = "pow")
public static native double pow(double x, double y); public static native double pow(double x, double y);
public static double rint(double a) { public static double rint(double a) {
@ -90,6 +99,7 @@ public final class TMath extends TObject {
} }
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "math", name = "atan2")
public static native double atan2(double y, double x); public static native double atan2(double y, double x);
public static int round(float a) { public static int round(float a) {
@ -101,6 +111,7 @@ public final class TMath extends TObject {
} }
@GeneratedBy(MathNativeGenerator.class) @GeneratedBy(MathNativeGenerator.class)
@Import(module = "math", name = "random")
public static native double random(); public static native double random();
public static int min(int a, int b) { public static int min(int a, int b) {

View File

@ -18,7 +18,6 @@ package org.teavm.backend.wasm.generate;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Deque; import java.util.Deque;
import java.util.HashMap; import java.util.HashMap;
@ -106,7 +105,6 @@ import org.teavm.backend.wasm.model.expression.WasmStoreInt64;
import org.teavm.backend.wasm.model.expression.WasmSwitch; import org.teavm.backend.wasm.model.expression.WasmSwitch;
import org.teavm.backend.wasm.model.expression.WasmUnreachable; import org.teavm.backend.wasm.model.expression.WasmUnreachable;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.model.CallLocation;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.TextLocation; import org.teavm.model.TextLocation;
@ -123,7 +121,6 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private WasmGenerationContext context; private WasmGenerationContext context;
private WasmClassGenerator classGenerator; private WasmClassGenerator classGenerator;
private WasmFunction function; private WasmFunction function;
private MethodReference method;
private int firstVariable; private int firstVariable;
private IdentifiedStatement currentContinueTarget; private IdentifiedStatement currentContinueTarget;
private IdentifiedStatement currentBreakTarget; private IdentifiedStatement currentBreakTarget;
@ -135,11 +132,10 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
WasmExpression result; WasmExpression result;
WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator, WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator,
WasmFunction function, MethodReference method, int firstVariable) { WasmFunction function, int firstVariable) {
this.context = context; this.context = context;
this.classGenerator = classGenerator; this.classGenerator = classGenerator;
this.function = function; this.function = function;
this.method = method;
this.firstVariable = firstVariable; this.firstVariable = firstVariable;
int typeCount = WasmType.values().length; int typeCount = WasmType.values().length;
for (int i = 0; i < typeCount; ++i) { for (int i = 0; i < typeCount; ++i) {
@ -862,15 +858,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
int vtableOffset = classGenerator.getClassSize(RuntimeClass.class.getName()); int vtableOffset = classGenerator.getClassSize(RuntimeClass.class.getName());
VirtualTableEntry vtableEntry = context.getVirtualTableProvider().lookup(expr.getMethod()); VirtualTableEntry vtableEntry = context.getVirtualTableProvider().lookup(expr.getMethod());
if (vtableEntry == null) { if (vtableEntry == null) {
result = new WasmInt32Constant(0); result = new WasmUnreachable();
TextLocation insnLocation = null;
if (expr.getLocation() != null) {
insnLocation = new TextLocation(expr.getLocation().getFileName(),
expr.getLocation().getLine());
}
CallLocation location = new CallLocation(method, insnLocation);
context.getDiagnostics().error(location, "Can't generate WebAssembly to call {{m0}} method",
expr.getMethod());
return; return;
} }
WasmExpression methodIndex = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, WasmExpression methodIndex = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
@ -1160,7 +1148,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
if (expr.getType() instanceof ValueType.Object) { if (expr.getType() instanceof ValueType.Object) {
ValueType.Object cls = (ValueType.Object) expr.getType(); ValueType.Object cls = (ValueType.Object) expr.getType();
List<TagRegistry.Range> ranges = context.getTagRegistry().getRanges(cls.getClassName()); List<TagRegistry.Range> ranges = context.getTagRegistry().getRanges(cls.getClassName());
Collections.sort(ranges, Comparator.comparingInt(range -> range.lower)); ranges.sort(Comparator.comparingInt(range -> range.lower));
WasmBlock block = new WasmBlock(false); WasmBlock block = new WasmBlock(false);
block.setLocation(expr.getLocation()); block.setLocation(expr.getLocation());
@ -1187,11 +1175,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
WasmIntBinaryOperation.GT_SIGNED, new WasmGetLocal(tagVar), WasmIntBinaryOperation.GT_SIGNED, new WasmGetLocal(tagVar),
new WasmInt32Constant(ranges.get(i - 1).upper)); new WasmInt32Constant(ranges.get(i - 1).upper));
WasmConditional conditional = new WasmConditional(upperThanExcluded); WasmConditional conditional = new WasmConditional(upperThanExcluded);
WasmExpression lowerThanExluded = new WasmIntBinary(WasmIntType.INT32, WasmExpression lowerThanExcluded = new WasmIntBinary(WasmIntType.INT32,
WasmIntBinaryOperation.LT_SIGNED, new WasmGetLocal(tagVar), WasmIntBinaryOperation.LT_SIGNED, new WasmGetLocal(tagVar),
new WasmInt32Constant(ranges.get(i).lower)); new WasmInt32Constant(ranges.get(i).lower));
WasmBranch branch = new WasmBranch(lowerThanExluded, block); WasmBranch branch = new WasmBranch(lowerThanExcluded, block);
branch.setResult(new WasmInt32Constant(0)); branch.setResult(new WasmInt32Constant(0));
conditional.getThenBlock().getBody().add(branch); conditional.getThenBlock().getBody().add(branch);

View File

@ -21,6 +21,8 @@ import org.teavm.ast.decompilation.Decompiler;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
import org.teavm.interop.Export;
import org.teavm.model.AnnotationReader;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderSource; import org.teavm.model.ClassHolderSource;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
@ -64,11 +66,16 @@ public class WasmGenerator {
function.setResult(WasmGeneratorUtil.mapType(methodReference.getReturnType())); function.setResult(WasmGeneratorUtil.mapType(methodReference.getReturnType()));
} }
WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, function, methodReference, WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, function,
firstVariable); firstVariable);
methodAst.getBody().acceptVisitor(visitor); methodAst.getBody().acceptVisitor(visitor);
function.getBody().add(visitor.result); function.getBody().add(visitor.result);
AnnotationReader exportAnnot = method.getAnnotations().get(Export.class.getName());
if (exportAnnot != null) {
function.setExportName(exportAnnot.getValue("name").getString());
}
return function; return function;
} }

View File

@ -155,7 +155,7 @@ public class WasmBinaryRenderer {
section.writeLEB(module.getMemorySize()); section.writeLEB(module.getMemorySize());
section.writeLEB(module.getMemorySize()); section.writeLEB(module.getMemorySize());
section.writeByte(0); section.writeByte(1);
writeSection("memory", section.getData()); writeSection("memory", section.getData());
} }

View File

@ -398,10 +398,10 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
append("i32.load"); append("i32.load");
break; break;
} }
append(" align=" + expression.getAlignment());
if (expression.getOffset() > 0) { if (expression.getOffset() > 0) {
append(" offset=" + expression.getOffset()); append(" offset=" + expression.getOffset());
} }
append(" align=" + expression.getAlignment());
line(expression.getIndex()); line(expression.getIndex());
close(); close();
} }
@ -432,30 +432,32 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
append("i64.load"); append("i64.load");
break; break;
} }
append(" align=" + expression.getAlignment());
if (expression.getOffset() > 0) { if (expression.getOffset() > 0) {
append(" offset=" + expression.getOffset()); append(" offset=" + expression.getOffset());
} }
append(" align=" + expression.getAlignment());
line(expression.getIndex()); line(expression.getIndex());
close(); close();
} }
@Override @Override
public void visit(WasmLoadFloat32 expression) { public void visit(WasmLoadFloat32 expression) {
open().append("f32.load align=" + expression.getAlignment()); open().append("f32.load");
if (expression.getOffset() > 0) { if (expression.getOffset() > 0) {
append(" offset=" + expression.getOffset()); append(" offset=" + expression.getOffset());
} }
append(" align=" + expression.getAlignment());
line(expression.getIndex()); line(expression.getIndex());
close(); close();
} }
@Override @Override
public void visit(WasmLoadFloat64 expression) { public void visit(WasmLoadFloat64 expression) {
open().append("f64.load align=" + expression.getAlignment()); open().append("f64.load");
if (expression.getOffset() > 0) { if (expression.getOffset() > 0) {
append(" offset=" + expression.getOffset()); append(" offset=" + expression.getOffset());
} }
append(" align=" + expression.getAlignment());
line(expression.getIndex()); line(expression.getIndex());
close(); close();
} }
@ -476,10 +478,10 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
append("i32.store"); append("i32.store");
break; break;
} }
append(" align=" + expression.getAlignment());
if (expression.getOffset() > 0) { if (expression.getOffset() > 0) {
append(" offset=" + expression.getOffset()); append(" offset=" + expression.getOffset());
} }
append(" align=" + expression.getAlignment());
line(expression.getIndex()); line(expression.getIndex());
line(expression.getValue()); line(expression.getValue());
close(); close();
@ -505,10 +507,10 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
append("i64.store"); append("i64.store");
break; break;
} }
append(" align=" + expression.getAlignment());
if (expression.getOffset() > 0) { if (expression.getOffset() > 0) {
append(" offset=" + expression.getOffset()); append(" offset=" + expression.getOffset());
} }
append(" align=" + expression.getAlignment());
line(expression.getIndex()); line(expression.getIndex());
line(expression.getValue()); line(expression.getValue());
close(); close();
@ -516,10 +518,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
@Override @Override
public void visit(WasmStoreFloat32 expression) { public void visit(WasmStoreFloat32 expression) {
open().append("f32.store align=" + expression.getAlignment()); open().append("f32.store");
if (expression.getOffset() > 0) { if (expression.getOffset() > 0) {
append(" offset=" + expression.getOffset()); append(" offset=" + expression.getOffset());
} }
append(" align=" + expression.getAlignment());
line(expression.getIndex()); line(expression.getIndex());
line(expression.getValue()); line(expression.getValue());
close(); close();
@ -527,10 +530,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
@Override @Override
public void visit(WasmStoreFloat64 expression) { public void visit(WasmStoreFloat64 expression) {
open().append("f64.store align=" + expression.getAlignment()); open().append("f64.store");
if (expression.getOffset() > 0) { if (expression.getOffset() > 0) {
append(" offset=" + expression.getOffset()); append(" offset=" + expression.getOffset());
} }
append(" align=" + expression.getAlignment());
line(expression.getIndex()); line(expression.getIndex());
line(expression.getValue()); line(expression.getValue());
close(); close();

View File

@ -38,12 +38,17 @@ public class VirtualTableProvider {
this.classSource = classSource; this.classSource = classSource;
interfaceMapping = new InterfaceToClassMapping(classSource); interfaceMapping = new InterfaceToClassMapping(classSource);
Set<String> classNames = new HashSet<>(classSource.getClassNames());
for (MethodReference virtualMethod : virtualMethods) { for (MethodReference virtualMethod : virtualMethods) {
String cls = interfaceMapping.mapClass(virtualMethod.getClassName()); String cls = interfaceMapping.mapClass(virtualMethod.getClassName());
if (cls == null) {
cls = virtualMethod.getClassName();
}
classNames.add(cls);
virtualMethodMap.computeIfAbsent(cls, c -> new HashSet<>()).add(virtualMethod.getDescriptor()); virtualMethodMap.computeIfAbsent(cls, c -> new HashSet<>()).add(virtualMethod.getDescriptor());
} }
for (String className : classSource.getClassNames()) { for (String className : classNames) {
fillClass(className); fillClass(className);
} }
} }

View File

@ -153,29 +153,6 @@ public class TypeInferer {
} }
} }
VariableType convert(ArrayElementType type) {
switch (type) {
case BYTE:
return VariableType.BYTE_ARRAY;
case CHAR:
return VariableType.CHAR_ARRAY;
case SHORT:
return VariableType.SHORT_ARRAY;
case INT:
return VariableType.INT_ARRAY;
case LONG:
return VariableType.LONG_ARRAY;
case FLOAT:
return VariableType.FLOAT_ARRAY;
case DOUBLE:
return VariableType.DOUBLE_ARRAY;
case OBJECT:
return VariableType.OBJECT_ARRAY;
default:
throw new AssertionError();
}
}
VariableType convert(NumericOperandType type) { VariableType convert(NumericOperandType type) {
switch (type) { switch (type) {
case INT: case INT:
@ -396,7 +373,15 @@ public class TypeInferer {
@Override @Override
public void binary(BinaryOperation op, VariableReader receiver, VariableReader first, VariableReader second, public void binary(BinaryOperation op, VariableReader receiver, VariableReader first, VariableReader second,
NumericOperandType type) { NumericOperandType type) {
types[receiver.getIndex()] = convert(type); switch (op) {
case COMPARE:
types[receiver.getIndex()] = VariableType.INT;
break;
default:
types[receiver.getIndex()] = convert(type);
break;
}
} }
@Override @Override

View File

@ -16,6 +16,7 @@
package org.teavm.runtime; package org.teavm.runtime;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.interop.Export;
import org.teavm.interop.StaticInit; import org.teavm.interop.StaticInit;
import org.teavm.interop.Structure; import org.teavm.interop.Structure;
import org.teavm.interop.Unmanaged; import org.teavm.interop.Unmanaged;
@ -30,6 +31,7 @@ public final class ExceptionHandling {
private static Throwable thrownException; private static Throwable thrownException;
@Export(name = "sys$catchException")
public static Throwable catchException() { public static Throwable catchException() {
Throwable exception = thrownException; Throwable exception = thrownException;
thrownException = null; thrownException = null;

View File

@ -0,0 +1,27 @@
/*
* Copyright 2016 Alexey Andreev.
*
* 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.
*/
package org.teavm.interop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Export {
String name();
}

View File

@ -45,6 +45,12 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-interop</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>org.jbox2d</groupId> <groupId>org.jbox2d</groupId>
<artifactId>jbox2d-library</artifactId> <artifactId>jbox2d-library</artifactId>
@ -144,6 +150,19 @@
<optimizationLevel>FULL</optimizationLevel> <optimizationLevel>FULL</optimizationLevel>
</configuration> </configuration>
</execution> </execution>
<execution>
<id>wasm-client</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<targetDirectory>${project.build.directory}/generated/wasm/teavm-wasm</targetDirectory>
<mainClass>org.teavm.samples.benchmark.teavm.WasmBenchmarkStarter</mainClass>
<debugInformationGenerated>true</debugInformationGenerated>
<targetType>WEBASSEMBLY</targetType>
<optimizationLevel>FULL</optimizationLevel>
</configuration>
</execution>
</executions> </executions>
</plugin> </plugin>
<plugin> <plugin>

View File

@ -30,10 +30,6 @@ import org.teavm.jso.dom.html.HTMLDocument;
import org.teavm.jso.dom.html.HTMLElement; import org.teavm.jso.dom.html.HTMLElement;
import org.teavm.samples.benchmark.shared.Scene; import org.teavm.samples.benchmark.shared.Scene;
/**
*
* @author Alexey Andreev
*/
public final class BenchmarkStarter { public final class BenchmarkStarter {
private static HTMLDocument document = Window.current().getDocument(); private static HTMLDocument document = Window.current().getDocument();
private static HTMLCanvasElement canvas = (HTMLCanvasElement) document.getElementById("benchmark-canvas"); private static HTMLCanvasElement canvas = (HTMLCanvasElement) document.getElementById("benchmark-canvas");

View File

@ -0,0 +1,105 @@
/*
* Copyright 2016 Alexey Andreev.
*
* 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.
*/
package org.teavm.samples.benchmark.teavm;
import org.jbox2d.collision.shapes.CircleShape;
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.collision.shapes.ShapeType;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.Fixture;
import org.teavm.interop.Export;
import org.teavm.interop.Import;
import org.teavm.samples.benchmark.shared.Scene;
public final class WasmBenchmarkStarter {
private static Scene scene = new Scene();
private static int currentSecond;
private static long startMillisecond;
private static int timeSpentCalculating;
private WasmBenchmarkStarter() {
}
public static void main(String[] args) {
startMillisecond = System.currentTimeMillis();
tick();
}
@Export(name = "tick")
public static void tick() {
double start = performanceTime();
System.out.println("About to calculate frame");
scene.calculate();
System.out.println("Frame calculated successfully");
double end = performanceTime();
int second = (int) ((System.currentTimeMillis() - startMillisecond) / 1000);
if (second > currentSecond) {
reportPerformance(second, timeSpentCalculating);
timeSpentCalculating = 0;
currentSecond = second;
}
timeSpentCalculating += (int) (end - start);
render();
repeatAfter(scene.timeUntilNextStep());
}
private static void render() {
WasmCanvas.save();
setupCanvas();
for (Body body = scene.getWorld().getBodyList(); body != null; body = body.getNext()) {
Vec2 center = body.getPosition();
WasmCanvas.save();
WasmCanvas.translate(center.x, center.y);
WasmCanvas.rotate(body.getAngle());
for (Fixture fixture = body.getFixtureList(); fixture != null; fixture = fixture.getNext()) {
Shape shape = fixture.getShape();
if (shape.getType() == ShapeType.CIRCLE) {
CircleShape circle = (CircleShape) shape;
WasmCanvas.beginPath();
WasmCanvas.arc(circle.m_p.x, circle.m_p.y, circle.getRadius(), 0, Math.PI * 2, true);
WasmCanvas.closePath();
WasmCanvas.stroke();
} else if (shape.getType() == ShapeType.POLYGON) {
PolygonShape poly = (PolygonShape) shape;
Vec2[] vertices = poly.getVertices();
WasmCanvas.beginPath();
WasmCanvas.moveTo(vertices[0].x, vertices[0].y);
for (int i = 1; i < poly.getVertexCount(); ++i) {
WasmCanvas.lineTo(vertices[i].x, vertices[i].y);
}
WasmCanvas.closePath();
WasmCanvas.stroke();
}
}
WasmCanvas.restore();
}
WasmCanvas.restore();
}
@Import(module = "benchmark", name = "setupCanvas")
private static native void setupCanvas();
@Import(module = "benchmark", name = "performanceTime")
private static native double performanceTime();
@Import(module = "benchmark", name = "reportPerformance")
private static native void reportPerformance(int second, int timeSpentCalculating);
@Import(module = "benchmark", name = "repeatAfter")
private static native void repeatAfter(int seconds);
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2016 Alexey Andreev.
*
* 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.
*/
package org.teavm.samples.benchmark.teavm;
import org.teavm.interop.Import;
import org.teavm.interop.StaticInit;
@StaticInit
public final class WasmCanvas {
private WasmCanvas() {
}
@Import(module = "canvas", name = "save")
public static native void save();
@Import(module = "canvas", name = "restore")
public static native void restore();
@Import(module = "canvas", name = "translate")
public static native void translate(double x, double y);
@Import(module = "canvas", name = "rotate")
public static native void rotate(double angle);
@Import(module = "canvas", name = "beginPath")
public static native void beginPath();
@Import(module = "canvas", name = "closePath")
public static native void closePath();
@Import(module = "canvas", name = "stroke")
public static native void stroke();
@Import(module = "canvas", name = "arc")
public static native void arc(double cx, double cy, double radius, double startAngle, double endAngle,
boolean counterClockwise);
@Import(module = "canvas", name = "moveTo")
public static native void moveTo(double x, double y);
@Import(module = "canvas", name = "lineTo")
public static native void lineTo(double x, double y);
}

View File

@ -0,0 +1,46 @@
<!--
~ Copyright 2016 Alexey Andreev.
~
~ 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.
-->
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>TeaVM WebAssembly jbox2d benchmark</title>
<script src="teavm-wasm.js" type="text/javascript"></script>
</head>
<body>
<h1>TeaVM performance</h1>
<div>
<canvas id="benchmark-canvas" width="600" height="600"></canvas>
</div>
<table>
<thead>
<tr>
<th>Second</th>
<th>Time spent computing, ms</th>
</tr>
</thead>
<tbody id="result-table-body">
</tbody>
</table>
<script>
var canvas = document.getElementById("benchmark-canvas");
document.body.onload = function() {
var benchmark = new Benchmark(canvas.getContext("2d"));
benchmark.runAll();
}
</script>
</body>
</html>

View File

@ -0,0 +1,104 @@
/*
* Copyright 2016 Alexey Andreev.
*
* 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.
*/
var Benchmark = function() {
function Benchmark(canvas) {
this.canvas = canvas;
this.module = null;
this.line = "";
}
Benchmark.prototype.runAll = function() {
load(this, function() { this.module.exports.main(); }.bind(this));
}
function tick(benchmark) {
var exports = benchmark.module.exports;
exports.tick();
console.log("tick");
var exception = exports.sys$catchException();
if (exception != null) {
console.log("Exception: " + exception);
}
}
function currentTimeMillis() {
return new Date().getTime();
}
function putchar(benchmark, charCode) {
if (charCode == 10) {
console.log(benchmark.line);
benchmark.line = "";
} else {
benchmark.line += String.fromCharCode(charCode);
}
}
function load(benchmark, callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "teavm-wasm/classes.wasm");
xhr.onreadystatechange = function() {
var response = xhr.response;
if (!response) {
return;
}
var importObj = {
runtime: {
currentTimeMillis: currentTimeMillis,
isNaN: isNaN,
isFinite: isFinite,
getNaN: function() { return NaN; },
putchar: function() { putchar(benchmark); }
},
benchmark: {
performanceTime: function() { return window.performance.now() || 0; },
reportPerformance: function(second, timeSpentComputing) {
console.log("Second: " + second + ", time: " + timeSpentComputing);
},
repeatAfter: function(time) {
console.log("repeatAfter");
setTimeout(tick.bind(benchmark), time);
},
setupCanvas: function() {
var canvas = benchmark.canvas;
canvas.setFillStyle("white");
context.setStrokeStyle("grey");
canvas.fillRect(0, 0, 600, 600);
canvas.translate(0, 600);
canvas.scale(1, -1);
canvas.scale(100, 100);
canvas.setLineWidth(0.01);
}
},
canvas: benchmark.canvas,
math: Math,
debug: {
traceMemoryAccess: function(callSite, address) {
if (address >= 63 * 65536) {
console.log("Memory access #" + callSite + " at " + address);
}
return address;
}
}
};
benchmark.module = Wasm.instantiateModule(new Uint8Array(response), importObj)
callback();
};
xhr.send();
}
return Benchmark;
}();