@@ -119,16 +119,22 @@ | |||
const _ = require('lodash'); | |||
const axios = require('axios'); | |||
import { dom_example } from './examples/dom_example' | |||
import { pong_example } from './examples/pong_example' | |||
import { call_cpp_example } from './examples/call_cpp_example' | |||
import { perf_example } from './examples/perf_example' | |||
var context = require.context("./examples", true, /\.js$/); | |||
var examples = []; | |||
var dom_example_index = -1; | |||
context.keys().forEach(function (key) { | |||
if (key.indexOf('dom.js') != -1) { | |||
dom_example_index = examples.length; | |||
console.log("FOUND: " + dom_example_index); | |||
} | |||
examples.push(context(key)['example']) | |||
}); | |||
let cpp_code = dom_example.cpp | |||
let js_code = dom_example.js | |||
let wasm_code = dom_example.wasm | |||
let html_code = dom_example.html | |||
let compiler_flags = dom_example.flags | |||
let cpp_code = examples[dom_example_index].cpp_code, | |||
js_code = examples[dom_example_index].js_code, | |||
wasm_code = examples[dom_example_index].wasm_code, | |||
html_code = examples[dom_example_index].html_code, | |||
compiler_flags = examples[dom_example_index].flags; | |||
function findIframeByName(name) { | |||
return _.find(window.frames, frame => frame.name === name); | |||
@@ -188,12 +194,7 @@ | |||
examples: { | |||
type: Array, | |||
default: function () { | |||
return [ | |||
{ title: 'DOM example', cpp_code: dom_example.cpp, 'js_code': dom_example.js, 'wasm_code': dom_example.wasm, 'html_code': dom_example.html, flags: dom_example.flags }, | |||
{ title: 'Pong WASM example', cpp_code: pong_example.cpp, 'js_code': pong_example.js, 'wasm_code': pong_example.wasm, 'html_code': pong_example.html, flags: pong_example.flags }, | |||
{ title: 'Call C++ example', cpp_code: call_cpp_example.cpp, 'js_code': call_cpp_example.js, 'wasm_code': call_cpp_example.wasm, 'html_code': call_cpp_example.html, flags: call_cpp_example.flags }, | |||
{ title: 'JS vs Compiled JS perf', cpp_code: perf_example.cpp, 'js_code': perf_example.js, 'wasm_code': perf_example.wasm, 'html_code': perf_example.html, flags: perf_example.flags }, | |||
] | |||
return examples | |||
} | |||
}, | |||
share_link: { | |||
@@ -385,12 +386,9 @@ | |||
}, | |||
}, | |||
watch: { | |||
html_code(new_val) { | |||
}, | |||
cpp_code(new_val) { | |||
}, | |||
js_code(new_val) { | |||
}, | |||
html_code(new_val) {}, | |||
cpp_code(new_val) {}, | |||
js_code(new_val) {}, | |||
}, | |||
created: function() { | |||
var hashchange_fun = function() { |
@@ -1,70 +0,0 @@ | |||
const cpp_code = ` | |||
#include <cheerp/client.h> | |||
#include <cheerp/clientlib.h> | |||
using namespace client; | |||
// The class can of course have any name | |||
// The [[cheerp::jsexport]] attribute tells Cheerp to make | |||
// the class available to JavaScript code | |||
class [[cheerp::jsexport]] JsBridge | |||
{ | |||
private: | |||
// The class is allowed to have member variables | |||
// but they should all be trivially destructible | |||
int callCount; | |||
public: | |||
JsBridge():callCount(0) | |||
{ | |||
} | |||
int addAndReturn(int a) | |||
{ | |||
console.log("Called C++ code"); | |||
callCount+=a; | |||
return callCount; | |||
} | |||
}; | |||
// An entry point, even if empty, is still required | |||
void webMain() | |||
{ | |||
}`.trim(); | |||
const html_code = ` | |||
<!DOCTYPE html> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="utf-8"> | |||
<title>Cheerp test</title> | |||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> | |||
<script> | |||
var jsBridge=null; | |||
function callCPPCode() | |||
{ | |||
if(!jsBridge) | |||
jsBridge=new JsBridge(); | |||
var ret=jsBridge.addAndReturn(3); | |||
$("#clickText").text("C++ code returned "+ret); | |||
return false; | |||
} | |||
</script> | |||
</head> | |||
<body> | |||
<a id="clickText" href="javascript:void(0)" onclick="callCPPCode()">Click to call C++ code</a> | |||
</body> | |||
</html>`.trim(); | |||
const js_code = ``.trim(); | |||
const flags = ` | |||
-cheerp-pretty-code | |||
-cheerp-no-type-optimizer | |||
-cheerp-no-native-math | |||
-cheerp-no-math-imul | |||
-cheerp-no-math-fround | |||
-O3 | |||
-target cheerp`.trim() | |||
const wasm_code = ''; | |||
export const call_cpp_example = { cpp: cpp_code, js: js_code, wasm: wasm_code, html: html_code, flags: flags } |
@@ -1,58 +0,0 @@ | |||
const cpp_code = ` | |||
#include <cheerp/client.h> | |||
#include <cheerp/clientlib.h> | |||
// We need to extend the client namespace to declare our | |||
// custom JavaScript function | |||
namespace client | |||
{ | |||
// The name should be the same as the JavaScript one | |||
// The parameters needs to be a const client::String reference | |||
// so that implicit conversion from const char* is supported | |||
void changeTitle(const String& str); | |||
} | |||
using namespace client; | |||
void webMain() | |||
{ | |||
Element* titleElement=document.getElementById("pagetitle"); | |||
String* oldText=titleElement->get_textContent(); | |||
changeTitle("Literal C++ string"); | |||
}`.trim(); | |||
const html_code = ` | |||
<!DOCTYPE html> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="utf-8"> | |||
<title>Cheerp test</title> | |||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"><\/script> | |||
<script> | |||
// Use jQuery to make a (trivial) change to the page | |||
function changeTitle(str) | |||
{ | |||
$("#pagetitle").text(str); | |||
} | |||
<\/script> | |||
</head> | |||
<body> | |||
<h1 id="pagetitle">Boring static text</h1> | |||
<!-- MARKER: Include javascript here. --> | |||
</body> | |||
</html>`.trim(); | |||
const js_code = ``.trim(); | |||
const flags = ` | |||
-cheerp-pretty-code | |||
-cheerp-no-type-optimizer | |||
-cheerp-no-native-math | |||
-cheerp-no-math-imul | |||
-cheerp-no-math-fround | |||
-O3 | |||
-target cheerp`.trim() | |||
const wasm_code = ''; | |||
export const dom_example = { cpp: cpp_code, js: js_code, wasm: wasm_code, html: html_code, flags: flags } |
@@ -1,18 +0,0 @@ | |||
const cpp_code = ``.trim(); | |||
const html_code = ``.trim(); | |||
const js_code = ``.trim(); | |||
const flags = ` | |||
-cheerp-pretty-code | |||
-cheerp-no-type-optimizer | |||
-cheerp-no-native-math | |||
-cheerp-no-math-imul | |||
-cheerp-no-math-fround | |||
-O3 | |||
-target cheerp`.trim() | |||
const wasm_code = ''; | |||
export const dom_example = { cpp: cpp_code, js: js_code, wasm: wasm_code, html: html_code, flags: flags } |
@@ -1,82 +0,0 @@ | |||
const cpp_code = ` | |||
#include <cmath> | |||
#include <cheerp/client.h> | |||
#include <cheerp/clientlib.h> | |||
class [[cheerp::jsexport]] JsBridge | |||
{ | |||
public: | |||
JsBridge() = default; | |||
size_t test() | |||
{ | |||
volatile size_t counter = 0; | |||
for (int i = 0; i < 50000; ++i) { | |||
for (int j = 0; j < 50000; ++j) { | |||
counter++; | |||
} | |||
} | |||
return sqrt(counter); | |||
} | |||
}; | |||
void webMain() {} | |||
`.trim(); | |||
const html_code = ` | |||
<html> | |||
<head> | |||
<script> | |||
function test() { | |||
var counter = 0 | |||
for (var i = 0; i < 50000; ++i) { | |||
for (var j = 0; j < 50000; ++j) { | |||
counter++; | |||
} | |||
} | |||
return Math.sqrt(counter); | |||
} | |||
function benchmark() { | |||
var jsBridge = new JsBridge(); | |||
{ | |||
document.write("Running native JS..<br>"); | |||
var start = new Date().getTime(); | |||
var ret = test(); | |||
var time = new Date().getTime() - start; | |||
document.write("Result: " + ret + "<br>"); | |||
document.write("Execution time: " + (time / 1000.0) + " secs<br>"); | |||
} | |||
document.write("<br>"); | |||
{ | |||
document.write("Running compiled JS..<br>"); | |||
var start = new Date().getTime(); | |||
var ret = jsBridge.test(); | |||
var time = new Date().getTime() - start; | |||
document.write("Result: " + ret + "<br>"); | |||
document.write("Execution time: " + (time / 1000.0) + " secs<br>"); | |||
} | |||
} | |||
</script> | |||
</head> | |||
<body> | |||
Benchmark started... <br/><br/> | |||
<script> | |||
benchmark(); | |||
</script> | |||
<br/> | |||
Benchmark finished... <br/> | |||
</body> | |||
</html>`.trim(); | |||
const js_code = ``.trim(); | |||
const flags = ` | |||
-cheerp-pretty-code | |||
-cheerp-no-type-optimizer | |||
-cheerp-no-native-math | |||
-cheerp-no-math-imul | |||
-cheerp-no-math-fround | |||
-O3 | |||
-target cheerp`.trim() | |||
const wasm_code = ''; | |||
export const perf_example = { cpp: cpp_code, js: js_code, wasm: wasm_code, html: html_code, flags: flags } |
@@ -1,243 +0,0 @@ | |||
const cpp_code = ` | |||
#include <cheerp/clientlib.h> | |||
#include <cheerp/client.h> | |||
#include <math.h> | |||
static constexpr int width = 400; | |||
static constexpr int height = 320; | |||
// Forward declaration for the main loop of the game, compiled to WebAssembly | |||
void mainLoop(); | |||
// All the graphics code should stay on the JS side. It is possible to tag whole classes with the [[cheerp::genericjs]] tag. | |||
// All members and methods of this class will be compiled to standard JavaScript. | |||
class [[cheerp::genericjs]] Graphics | |||
{ | |||
private: | |||
// When compiling to standard JavaScript it is possible to use DOM objects like any other C++ object. | |||
static client::HTMLCanvasElement* canvas; | |||
static client::CanvasRenderingContext2D* canvasCtx; | |||
static int width; | |||
static int height; | |||
// This method is the handler for requestAnimationFrame. The browser will call this | |||
// in sync with its graphics loop, usually at 60 fps. | |||
static void rafHandler() | |||
{ | |||
mainLoop(); | |||
client::requestAnimationFrame(cheerp::Callback(rafHandler)); | |||
} | |||
public: | |||
// Define this method later on, we need to declare the Platform class first | |||
static void keyDownHandler(client::KeyboardEvent* e); | |||
static void keyUpHandler(client::KeyboardEvent* e); | |||
static void initializeCanvas(int w, int h) | |||
{ | |||
width = w; | |||
height = h; | |||
canvas = (client::HTMLCanvasElement*)client::document.getElementById("pongcanvas"); | |||
canvas->set_width(w); | |||
canvas->set_height(h); | |||
client::document.get_body()->appendChild(canvas); | |||
canvasCtx = (client::CanvasRenderingContext2D*)canvas->getContext("2d"); | |||
client::requestAnimationFrame(cheerp::Callback(rafHandler)); | |||
// Listen for keydown events | |||
client::document.addEventListener("keydown", cheerp::Callback(keyDownHandler)); | |||
client::document.addEventListener("keyup", cheerp::Callback(keyUpHandler)); | |||
} | |||
static void drawRect(int x, int y, int w, int h, int rgb) | |||
{ | |||
int r = rgb&0xff; | |||
int g = (rgb>>8)&0xff; | |||
int b = (rgb>>16)&0xff; | |||
canvasCtx->set_fillStyle(client::String("").concat("rgb(", r, ",", g, ",", b, ")")); | |||
canvasCtx->fillRect(x, y, w, h); | |||
} | |||
static void drawCircle(int x, int y, int radius, int rgb) | |||
{ | |||
int r = rgb&0xff; | |||
int g = (rgb>>8)&0xff; | |||
int b = (rgb>>16)&0xff; | |||
canvasCtx->set_fillStyle(client::String("").concat("rgb(", r, ",", g, ",", b, ")")); | |||
canvasCtx->beginPath(); | |||
canvasCtx->arc(x,y,radius,0,2*M_PI); | |||
canvasCtx->fill(); | |||
} | |||
static void debugOutput(const char* str) | |||
{ | |||
canvasCtx->set_font("24px sans-serif"); | |||
canvasCtx->set_fillStyle("rgb(255,255,255)"); | |||
canvasCtx->fillText(str, 0, height - 24); | |||
} | |||
}; | |||
// This whole class will be compiled to Wasm code by default since we are using the -cheerp-mode=wasm | |||
// command line option. This is a game entity so it's better to get as much performance as we can. | |||
class Platform | |||
{ | |||
private: | |||
bool moveLeft; | |||
bool moveRight; | |||
int x; | |||
int y; | |||
int width; | |||
int height; | |||
public: | |||
Platform(int x, int y, int width, int height):moveLeft(false),moveRight(false),x(x),y(y),width(width),height(height) | |||
{ | |||
} | |||
int getX() const | |||
{ | |||
return x; | |||
} | |||
int getY() const | |||
{ | |||
return y; | |||
} | |||
int getWidth() const | |||
{ | |||
return width; | |||
} | |||
int getHeight() const | |||
{ | |||
return width; | |||
} | |||
void render() const | |||
{ | |||
Graphics::drawRect(x, y, width, height, 0xffffff); | |||
} | |||
void setMoveLeft(bool val) | |||
{ | |||
moveLeft = val; | |||
} | |||
void setMoveRight(bool val) | |||
{ | |||
moveRight = val; | |||
} | |||
void update() | |||
{ | |||
if (moveLeft) { | |||
x -= 5; | |||
} | |||
if (moveRight) { | |||
x += 5; | |||
} | |||
} | |||
}; | |||
class Ball | |||
{ | |||
private: | |||
int x; | |||
int y; | |||
int vx; | |||
int vy; | |||
public: | |||
Ball(int x, int y, int vx, int vy):x(x),y(y),vx(vx),vy(vy) | |||
{ | |||
} | |||
void update() | |||
{ | |||
x += vx; | |||
y += vy; | |||
} | |||
// Returns true if the ball gets out of the field | |||
bool collide(const Platform& platform, int maxX, int maxY) | |||
{ | |||
// If we collided with the bottom side, we lost | |||
if(y >= maxY) | |||
return true; | |||
// Check left and right side collisions | |||
if(x <= 0 || x >= maxX) | |||
vx = -vx; | |||
// Check top side collision | |||
if(y <= 0) | |||
vy = -vy; | |||
// Check collision with the top side of the plaform | |||
if(platform.getX() < x && (platform.getX() + platform.getWidth()) > x && | |||
platform.getY() < y && (platform.getY() + platform.getHeight()) > y) | |||
{ | |||
vy = -vy; | |||
} | |||
return false; | |||
} | |||
void render() | |||
{ | |||
Graphics::drawCircle(x, y, 5, 0xffffff); | |||
} | |||
}; | |||
// Define global instances for game entities. A more serious game | |||
// would manage these objects dynamically | |||
Platform platform(width / 2 - 15, height - 20, 30, 7); | |||
Ball ball(width / 2, height / 2, 2, -2); | |||
void Graphics::keyDownHandler(client::KeyboardEvent* e) | |||
{ | |||
if(e->get_keyCode() == 37) | |||
platform.setMoveLeft(true); | |||
else if(e->get_keyCode() == 39) | |||
platform.setMoveRight(true); | |||
} | |||
void Graphics::keyUpHandler(client::KeyboardEvent* e) | |||
{ | |||
if(e->get_keyCode() == 37) | |||
platform.setMoveLeft(false); | |||
else if(e->get_keyCode() == 39) | |||
platform.setMoveRight(false); | |||
} | |||
void mainLoop() | |||
{ | |||
// Reset the background to black | |||
Graphics::drawRect(0, 0, width, height, 0x000000); | |||
// Draw the platform | |||
platform.render(); | |||
platform.update(); | |||
// Update the ball state | |||
ball.update(); | |||
// Check for collisions | |||
bool hasLost = ball.collide(platform, width, height); | |||
if(hasLost) | |||
Graphics::debugOutput("You lost!"); | |||
// Render the ball | |||
ball.render(); | |||
} | |||
// This function is the entry point of the program. Since we will be compiling this with the -cheerp-mode=wasm option, it will | |||
// be compiled to WebAssembly by default. | |||
void webMain() | |||
{ | |||
Graphics::initializeCanvas(width, height); | |||
}`.trim(); | |||
const html_code = ` | |||
<!DOCTYPE html> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="utf-8"> | |||
<title>Cheerp test</title> | |||
</head> | |||
<body> | |||
<canvas id="pongcanvas"></canvas> | |||
<!-- MARKER: Include javascript here. --> | |||
</body> | |||
</html> | |||
`.trim(); | |||
const js_code = ``.trim() | |||
const wasm_code = ``.trim(); | |||
const flags = ` | |||
-cheerp-pretty-code | |||
-cheerp-no-type-optimizer | |||
-cheerp-no-native-math | |||
-cheerp-no-math-imul | |||
-cheerp-no-math-fround | |||
-target cheerp | |||
-cheerp-mode=wasm | |||
-O2 | |||
`.trim() | |||
export const pong_example = { cpp: cpp_code, js: js_code, wasm: wasm_code, html: html_code, flags: flags } |