From cc0c68e809e2bc2712cf4dcdd022c43ed69d7d5b Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 29 Sep 2016 20:05:32 +0300 Subject: [PATCH] WASM: porting jbox2d benchmark to WebAssembly --- .../org/teavm/classlib/java/lang/TFloat.java | 12 +- .../org/teavm/classlib/java/lang/TMath.java | 19 +++- .../wasm/generate/WasmGenerationVisitor.java | 22 +--- .../backend/wasm/generate/WasmGenerator.java | 9 +- .../wasm/render/WasmBinaryRenderer.java | 2 +- .../wasm/render/WasmRenderingVisitor.java | 20 ++-- .../model/classes/VirtualTableProvider.java | 7 +- .../org/teavm/model/util/TypeInferer.java | 33 ++---- .../org/teavm/runtime/ExceptionHandling.java | 2 + .../main/java/org/teavm/interop/Export.java | 27 +++++ samples/benchmark/pom.xml | 19 ++++ .../benchmark/teavm/BenchmarkStarter.java | 4 - .../benchmark/teavm/WasmBenchmarkStarter.java | 105 ++++++++++++++++++ .../samples/benchmark/teavm/WasmCanvas.java | 56 ++++++++++ .../benchmark/src/main/webapp/teavm-wasm.html | 46 ++++++++ .../benchmark/src/main/webapp/teavm-wasm.js | 104 +++++++++++++++++ 16 files changed, 425 insertions(+), 62 deletions(-) create mode 100644 interop/core/src/main/java/org/teavm/interop/Export.java create mode 100644 samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/WasmBenchmarkStarter.java create mode 100644 samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/WasmCanvas.java create mode 100644 samples/benchmark/src/main/webapp/teavm-wasm.html create mode 100644 samples/benchmark/src/main/webapp/teavm-wasm.js diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java index 17330d7f9..fdbd77f58 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java @@ -15,6 +15,7 @@ */ package org.teavm.classlib.java.lang; +import org.teavm.interop.Import; import org.teavm.jso.JSBody; public class TFloat extends TNumber implements TComparable { @@ -89,12 +90,19 @@ public class TFloat extends TNumber implements TComparable { } @JSBody(params = "v", script = "return isNaN(v);") + @Import(module = "runtime", name = "isNaN") public static native boolean isNaN(float v); - @JSBody(params = "v", script = "return !isFinite(v);") - public static native boolean isInfinite(float v); + public static 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;") + @Import(module = "runtime", name = "getNaN") private static native float getNaN(); public static float parseFloat(TString string) throws TNumberFormatException { diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java index 1033a4b58..42b04aa20 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java @@ -16,11 +16,8 @@ package org.teavm.classlib.java.lang; import org.teavm.backend.javascript.spi.GeneratedBy; +import org.teavm.interop.Import; -/** - * - * @author Alexey Andreev - */ public final class TMath extends TObject { public static final double E = 2.71828182845904523536; public static final double PI = 3.14159265358979323846; @@ -29,21 +26,27 @@ public final class TMath extends TObject { } @GeneratedBy(MathNativeGenerator.class) + @Import(module = "math", name = "sin") public static native double sin(double a); @GeneratedBy(MathNativeGenerator.class) + @Import(module = "math", name = "cos") public static native double cos(double a); @GeneratedBy(MathNativeGenerator.class) + @Import(module = "math", name = "tan") public static native double tan(double a); @GeneratedBy(MathNativeGenerator.class) + @Import(module = "math", name = "asin") public static native double asin(double a); @GeneratedBy(MathNativeGenerator.class) + @Import(module = "math", name = "acos") public static native double acos(double a); @GeneratedBy(MathNativeGenerator.class) + @Import(module = "math", name = "atan") public static native double atan(double a); public static double toRadians(double angdeg) { @@ -55,9 +58,11 @@ public final class TMath extends TObject { } @GeneratedBy(MathNativeGenerator.class) + @Import(module = "math", name = "exp") public static native double exp(double a); @GeneratedBy(MathNativeGenerator.class) + @Import(module = "math", name = "log") public static native double log(double a); public static double log10(double a) { @@ -65,6 +70,7 @@ public final class TMath extends TObject { } @GeneratedBy(MathNativeGenerator.class) + @Import(module = "math", name = "sqrt") public static native double sqrt(double a); public static double cbrt(double a) { @@ -77,12 +83,15 @@ public final class TMath extends TObject { } @GeneratedBy(MathNativeGenerator.class) + @Import(module = "math", name = "ceil") public static native double ceil(double a); @GeneratedBy(MathNativeGenerator.class) + @Import(module = "math", name = "floor") public static native double floor(double a); @GeneratedBy(MathNativeGenerator.class) + @Import(module = "math", name = "pow") public static native double pow(double x, double y); public static double rint(double a) { @@ -90,6 +99,7 @@ public final class TMath extends TObject { } @GeneratedBy(MathNativeGenerator.class) + @Import(module = "math", name = "atan2") public static native double atan2(double y, double x); public static int round(float a) { @@ -101,6 +111,7 @@ public final class TMath extends TObject { } @GeneratedBy(MathNativeGenerator.class) + @Import(module = "math", name = "random") public static native double random(); public static int min(int a, int b) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index cc409d86d..12bf7720e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -18,7 +18,6 @@ package org.teavm.backend.wasm.generate; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Comparator; import java.util.Deque; 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.WasmUnreachable; import org.teavm.interop.Address; -import org.teavm.model.CallLocation; import org.teavm.model.FieldReference; import org.teavm.model.MethodReference; import org.teavm.model.TextLocation; @@ -123,7 +121,6 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { private WasmGenerationContext context; private WasmClassGenerator classGenerator; private WasmFunction function; - private MethodReference method; private int firstVariable; private IdentifiedStatement currentContinueTarget; private IdentifiedStatement currentBreakTarget; @@ -135,11 +132,10 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { WasmExpression result; WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator, - WasmFunction function, MethodReference method, int firstVariable) { + WasmFunction function, int firstVariable) { this.context = context; this.classGenerator = classGenerator; this.function = function; - this.method = method; this.firstVariable = firstVariable; int typeCount = WasmType.values().length; for (int i = 0; i < typeCount; ++i) { @@ -862,15 +858,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { int vtableOffset = classGenerator.getClassSize(RuntimeClass.class.getName()); VirtualTableEntry vtableEntry = context.getVirtualTableProvider().lookup(expr.getMethod()); if (vtableEntry == null) { - result = new WasmInt32Constant(0); - 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()); + result = new WasmUnreachable(); return; } WasmExpression methodIndex = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, @@ -1160,7 +1148,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { if (expr.getType() instanceof ValueType.Object) { ValueType.Object cls = (ValueType.Object) expr.getType(); List 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); block.setLocation(expr.getLocation()); @@ -1187,11 +1175,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { WasmIntBinaryOperation.GT_SIGNED, new WasmGetLocal(tagVar), new WasmInt32Constant(ranges.get(i - 1).upper)); WasmConditional conditional = new WasmConditional(upperThanExcluded); - WasmExpression lowerThanExluded = new WasmIntBinary(WasmIntType.INT32, + WasmExpression lowerThanExcluded = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED, new WasmGetLocal(tagVar), new WasmInt32Constant(ranges.get(i).lower)); - WasmBranch branch = new WasmBranch(lowerThanExluded, block); + WasmBranch branch = new WasmBranch(lowerThanExcluded, block); branch.setResult(new WasmInt32Constant(0)); conditional.getThenBlock().getBody().add(branch); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java index 04bbd88d0..c6eb374e0 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java @@ -21,6 +21,8 @@ import org.teavm.ast.decompilation.Decompiler; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmLocal; 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.ClassHolderSource; import org.teavm.model.ElementModifier; @@ -64,11 +66,16 @@ public class WasmGenerator { function.setResult(WasmGeneratorUtil.mapType(methodReference.getReturnType())); } - WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, function, methodReference, + WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, function, firstVariable); methodAst.getBody().acceptVisitor(visitor); function.getBody().add(visitor.result); + AnnotationReader exportAnnot = method.getAnnotations().get(Export.class.getName()); + if (exportAnnot != null) { + function.setExportName(exportAnnot.getValue("name").getString()); + } + return function; } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java index be57411ee..45246b6a7 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java @@ -155,7 +155,7 @@ public class WasmBinaryRenderer { section.writeLEB(module.getMemorySize()); section.writeLEB(module.getMemorySize()); - section.writeByte(0); + section.writeByte(1); writeSection("memory", section.getData()); } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java index 04bc47d3d..2a44e8c0e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java @@ -398,10 +398,10 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { append("i32.load"); break; } - append(" align=" + expression.getAlignment()); if (expression.getOffset() > 0) { append(" offset=" + expression.getOffset()); } + append(" align=" + expression.getAlignment()); line(expression.getIndex()); close(); } @@ -432,30 +432,32 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { append("i64.load"); break; } - append(" align=" + expression.getAlignment()); if (expression.getOffset() > 0) { append(" offset=" + expression.getOffset()); } + append(" align=" + expression.getAlignment()); line(expression.getIndex()); close(); } @Override public void visit(WasmLoadFloat32 expression) { - open().append("f32.load align=" + expression.getAlignment()); + open().append("f32.load"); if (expression.getOffset() > 0) { append(" offset=" + expression.getOffset()); } + append(" align=" + expression.getAlignment()); line(expression.getIndex()); close(); } @Override public void visit(WasmLoadFloat64 expression) { - open().append("f64.load align=" + expression.getAlignment()); + open().append("f64.load"); if (expression.getOffset() > 0) { append(" offset=" + expression.getOffset()); } + append(" align=" + expression.getAlignment()); line(expression.getIndex()); close(); } @@ -476,10 +478,10 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { append("i32.store"); break; } - append(" align=" + expression.getAlignment()); if (expression.getOffset() > 0) { append(" offset=" + expression.getOffset()); } + append(" align=" + expression.getAlignment()); line(expression.getIndex()); line(expression.getValue()); close(); @@ -505,10 +507,10 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { append("i64.store"); break; } - append(" align=" + expression.getAlignment()); if (expression.getOffset() > 0) { append(" offset=" + expression.getOffset()); } + append(" align=" + expression.getAlignment()); line(expression.getIndex()); line(expression.getValue()); close(); @@ -516,10 +518,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { @Override public void visit(WasmStoreFloat32 expression) { - open().append("f32.store align=" + expression.getAlignment()); + open().append("f32.store"); if (expression.getOffset() > 0) { append(" offset=" + expression.getOffset()); } + append(" align=" + expression.getAlignment()); line(expression.getIndex()); line(expression.getValue()); close(); @@ -527,10 +530,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { @Override public void visit(WasmStoreFloat64 expression) { - open().append("f64.store align=" + expression.getAlignment()); + open().append("f64.store"); if (expression.getOffset() > 0) { append(" offset=" + expression.getOffset()); } + append(" align=" + expression.getAlignment()); line(expression.getIndex()); line(expression.getValue()); close(); diff --git a/core/src/main/java/org/teavm/model/classes/VirtualTableProvider.java b/core/src/main/java/org/teavm/model/classes/VirtualTableProvider.java index 8ff4957bd..6babd9054 100644 --- a/core/src/main/java/org/teavm/model/classes/VirtualTableProvider.java +++ b/core/src/main/java/org/teavm/model/classes/VirtualTableProvider.java @@ -38,12 +38,17 @@ public class VirtualTableProvider { this.classSource = classSource; interfaceMapping = new InterfaceToClassMapping(classSource); + Set classNames = new HashSet<>(classSource.getClassNames()); for (MethodReference virtualMethod : virtualMethods) { String cls = interfaceMapping.mapClass(virtualMethod.getClassName()); + if (cls == null) { + cls = virtualMethod.getClassName(); + } + classNames.add(cls); virtualMethodMap.computeIfAbsent(cls, c -> new HashSet<>()).add(virtualMethod.getDescriptor()); } - for (String className : classSource.getClassNames()) { + for (String className : classNames) { fillClass(className); } } diff --git a/core/src/main/java/org/teavm/model/util/TypeInferer.java b/core/src/main/java/org/teavm/model/util/TypeInferer.java index d8a6c5a48..49ca3ced2 100644 --- a/core/src/main/java/org/teavm/model/util/TypeInferer.java +++ b/core/src/main/java/org/teavm/model/util/TypeInferer.java @@ -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) { switch (type) { case INT: @@ -396,7 +373,15 @@ public class TypeInferer { @Override public void binary(BinaryOperation op, VariableReader receiver, VariableReader first, VariableReader second, 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 diff --git a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java index 77d14fa36..2294f87a5 100644 --- a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java +++ b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java @@ -16,6 +16,7 @@ package org.teavm.runtime; import org.teavm.interop.Address; +import org.teavm.interop.Export; import org.teavm.interop.StaticInit; import org.teavm.interop.Structure; import org.teavm.interop.Unmanaged; @@ -30,6 +31,7 @@ public final class ExceptionHandling { private static Throwable thrownException; + @Export(name = "sys$catchException") public static Throwable catchException() { Throwable exception = thrownException; thrownException = null; diff --git a/interop/core/src/main/java/org/teavm/interop/Export.java b/interop/core/src/main/java/org/teavm/interop/Export.java new file mode 100644 index 000000000..51fadd89d --- /dev/null +++ b/interop/core/src/main/java/org/teavm/interop/Export.java @@ -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(); +} diff --git a/samples/benchmark/pom.xml b/samples/benchmark/pom.xml index 1cfd27ade..ff924dae5 100644 --- a/samples/benchmark/pom.xml +++ b/samples/benchmark/pom.xml @@ -45,6 +45,12 @@ ${project.version} provided + + org.teavm + teavm-interop + ${project.version} + provided + org.jbox2d jbox2d-library @@ -144,6 +150,19 @@ FULL + + wasm-client + + compile + + + ${project.build.directory}/generated/wasm/teavm-wasm + org.teavm.samples.benchmark.teavm.WasmBenchmarkStarter + true + WEBASSEMBLY + FULL + + diff --git a/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/BenchmarkStarter.java b/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/BenchmarkStarter.java index e0c67b5e7..a3e8eba16 100644 --- a/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/BenchmarkStarter.java +++ b/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/BenchmarkStarter.java @@ -30,10 +30,6 @@ import org.teavm.jso.dom.html.HTMLDocument; import org.teavm.jso.dom.html.HTMLElement; import org.teavm.samples.benchmark.shared.Scene; -/** - * - * @author Alexey Andreev - */ public final class BenchmarkStarter { private static HTMLDocument document = Window.current().getDocument(); private static HTMLCanvasElement canvas = (HTMLCanvasElement) document.getElementById("benchmark-canvas"); diff --git a/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/WasmBenchmarkStarter.java b/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/WasmBenchmarkStarter.java new file mode 100644 index 000000000..dc1764a52 --- /dev/null +++ b/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/WasmBenchmarkStarter.java @@ -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); +} diff --git a/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/WasmCanvas.java b/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/WasmCanvas.java new file mode 100644 index 000000000..07d74b711 --- /dev/null +++ b/samples/benchmark/src/main/java/org/teavm/samples/benchmark/teavm/WasmCanvas.java @@ -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); +} diff --git a/samples/benchmark/src/main/webapp/teavm-wasm.html b/samples/benchmark/src/main/webapp/teavm-wasm.html new file mode 100644 index 000000000..e516d9e00 --- /dev/null +++ b/samples/benchmark/src/main/webapp/teavm-wasm.html @@ -0,0 +1,46 @@ + + + + + + TeaVM WebAssembly jbox2d benchmark + + + +

TeaVM performance

+
+ +
+ + + + + + + + + +
SecondTime spent computing, ms
+ + + diff --git a/samples/benchmark/src/main/webapp/teavm-wasm.js b/samples/benchmark/src/main/webapp/teavm-wasm.js new file mode 100644 index 000000000..72838e620 --- /dev/null +++ b/samples/benchmark/src/main/webapp/teavm-wasm.js @@ -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; +}();