Commit 54300f9f authored by reassembler's avatar reassembler
Browse files

Emscripten Build

parent 0c92abed
......@@ -86,6 +86,7 @@ epr-10330.57
#################
mingw/
emscripten/
#################
......
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>CannonBall: The Enhanced OutRun Engine</title>
<style>
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
textarea.emscripten { font-family: monospace; width: 80%; }
div.emscripten { text-align: center; }
div.emscripten_border { border: 1px solid black; }
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
canvas.emscripten { border: 0px none; }
</style>
</head>
<body>
<hr/>
<div class="emscripten" id="status">Downloading...</div>
<div class="emscripten">
<progress value="0" max="100" id="progress" hidden=1></progress>
</div>
<div class="emscripten_border">
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
</div>
<hr/>
<!-- Additional Controls -->
<input type="button" id="start" value="Start CannonBall!" onclick="cannonball_main()" />
Current fps:
<input type="text" id="fps" size="5"/>
Target fps:
<input type="text" id="framerate" size="20"/>
<input type="button" id="framerate_button" value="+" onclick="inc_fps = true" />
Sound:
<input type="checkbox" id="sound" onclick="audio_enabled = !audio_enabled; set_audio(audio_enabled)">
<hr/>
<input type="file" onchange="snes_readfile(this)" />
<hr/>
<input type="button" value="Fullscreen" onclick="cannonball_set_screen_mode(0)">
<textarea class="emscripten" id="output" rows="8"></textarea>
<!-- Converted CannonBall C++ Code -->
<script type="text/javascript" src="cannonball.html.js" > </script>
<!-- Front-End Javascript Code -->
<script type="text/javascript">
// Setup references to C++ Routines
cpp_init = Module._emscripten_init
cpp_tick = Module._emscripten_tick
cpp_audio = Module.cwrap('emscripten_audio', 'number', ['number', 'number']) // funcname, return, params
cpp_setfps = Module._emscripten_setfps
cpp_framesize = Module._emscripten_set_framesize
// Default to 30fps
inc_fps = false
fps = 0
fps_text = document.getElementById("fps")
fps_text.value = "0"
framerate = 30
framerate_text = document.getElementById("framerate")
framerate_text.value = "30 fps"
framerate_inc = document.getElementById("framerate_button")
framerate_inc.disabled = true
// Sound Buffer
audio_enabled = false;
audio_freq = 44100;
audio_chan = 2;
buffer_size = 512; // Must be a multiple of 2
// Initialize Web Audio
var AudioContext = window.AudioContext || window.webkitAudioContext;
if (AudioContext)
{
audio_supported = true;
// Memory to share with C++
ptr_buffer_l = Module._malloc(buffer_size * Float32Array.BYTES_PER_ELEMENT) // 4 bytes per entry
ptr_buffer_r = Module._malloc(buffer_size * Float32Array.BYTES_PER_ELEMENT) // 4 bytes per entry
// get a bytes-wise view on the audio buffer
audio_l = new Float32Array(Module.HEAPU8.buffer, ptr_buffer_l, buffer_size);
audio_r = new Float32Array(Module.HEAPU8.buffer, ptr_buffer_r, buffer_size);
audio_context = new AudioContext();
audio_node = audio_context.createScriptProcessor(buffer_size, audio_chan, audio_chan);
// Setup Callback
audio_process = function(e)
{
var left = e.outputBuffer.getChannelData(0);
var right = e.outputBuffer.getChannelData(1);
fillBuffer(left, right);
};
set_audio(audio_enabled);
}
else
{
audio_supported = false;
alert('Web Audio API is not supported in this browser');
}
// Toggle Audio On/Off
function set_audio(state)
{
// Audio Not Supported: Just Disable!
if (!audio_supported)
{
cpp_framesize(0);
return;
}
// Enable
if (state)
{
audio_node.onaudioprocess = audio_process;
audio_node.connect(audio_context.destination);
cpp_framesize(buffer_size); // Start CPP sound code
}
// Disable
else
{
audio_node.onaudioprocess = null;
audio_node.disconnect(0);
cpp_framesize(0); // Stop CPP sound code
}
}
// Callback Function To Fill Audio Buffer
function fillBuffer(left, right)
{
// Pass audio buffer to C++ (byteOffset passes index at which array starts in underlying buffer)
cpp_audio(audio_l.byteOffset, audio_r.byteOffset);
// Fill the buffer
left.set(audio_l);
right.set(audio_r);
}
// See: https://groups.google.com/forum/#!topic/emscripten-discuss/oeEg6WrZ7rg
// http://comments.gmane.org/gmane.comp.compilers.emscripten/302
function cannonball_mainloop()
{
start_time = new Date().getTime()
// Increment FPS
if (inc_fps)
set_framerate(1)
cpp_tick();
// Update Framerate counter
if (++frames >= framerate)
{
this_time=new Date().getTime()
fps_text.value=((frames)*1000/(this_time-last_time)).toFixed(3)
last_time=this_time
frames = 0
}
// Lock to desired FPS rate
frame_time = new Date().getTime() - start_time
if (frame_time < (1000/framerate))
delay = (1000/framerate) - frame_time
else
delay = 0
setTimeout("cannonball_mainloop()", delay);
}
function cannonball_init()
{
// Enable Buttons
framerate_inc.disabled = false
fps_text=document.getElementById("fps")
frameskipped=0
}
function set_sound(n)
{
var MIN = 256;
var MAX = 4096;
if (n > 0)
{
buffer_size *= 2;
}
else if (n < 0)
{
buffer_size /= 2;
}
/*if (buffer_size > MAX)
buffer_size = 0;
else if (buffer_size < MIN)
buffer_size = MAX;*/
sound_text.value = buffer_size;
cpp_framesize(buffer_size);
update_sound = 0;
}
function set_framerate(n)
{
fps += n
if (fps > 2)
fps = 0
// Call Native CannonBall Function
cpp_setfps(fps)
if (fps == 0)
{
framerate_text.value= "30 fps"
framerate = 30
}
else if (fps == 1)
{
framerate_text.value= "Arcade 30/60 fps"
framerate = 60
}
else if (fps == 2)
{
framerate_text.value= "Enhanced 60 fps"
framerate = 60
}
frames = 0
inc_fps = false
}
function snes_readfile(controller)
{
if (window.File && window.FileReader && window.FileList && window.Blob) {
} else {
alert('The File APIs are not fully supported in this browser.');
}
var f=controller.files[0]
cout_print(f.name)
var reader= new FileReader()
reader.onprogress = function(e){
if (e.lengthComputable){
cout_print(Math.round((e.loaded / e.total) * 100)+"%")
}
else
count_print(e.loaded+"bytes")
document.getElementById("start").disabled=false
}
reader.onload = function(e) {
cout_print("_.smc loaded")
Module.FS_createDataFile("/", "_.smc", new Uint8Array(this.result) , true, true)
}
reader.readAsArrayBuffer(f)
}
function cannonball_main()
{
document.getElementById("start").disabled=true
cannonball_init()
cpp_init()
cpp_framesize(buffer_size);
frames = 0;
last_time = new Date().getTime()
cannonball_mainloop();
}
function cannonball_set_screen_mode(mode)
{
// Full-Screen Mode
if (mode == 0)
{
var hide_cursor = true
var notify_sdl_resize = false
Module.requestFullScreen(hide_cursor, notify_sdl_resize)
}
}
</script>
</body>
\ No newline at end of file
......@@ -25,20 +25,20 @@ else()
include(${DCMAKE})
endif(TARGET)
set(BOOST_INCLUDEDIR ${lib_base}/boost_1_51_0)
find_package(Boost REQUIRED)
#set(BOOST_INCLUDEDIR ${lib_base}/boost_1_51_0)
#find_package(Boost REQUIRED)
# Include
include_directories(
"${main_cpp_base}"
"${BOOST_INCLUDEDIR}"
#"${BOOST_INCLUDEDIR}"
)
# Create compile time setup
file(WRITE ${main_cpp_base}/setup.hpp
"// This file is AUTO GENERATED by CMake.
#pragma once
#include \"SDL_video.h\"
#include <SDL.h>
const static char* FILENAME_CONFIG = \"${xml_directory}config.xml\";
const static char* FILENAME_SCORES = \"${xml_directory}hiscores.xml\";
const static char* FILENAME_SCORES_JAP = \"${xml_directory}hiscores_jap.xml\";
......@@ -224,15 +224,15 @@ set(SRCS
)
# Add Icon For Windows Builds
if(WIN32)
set(SRCS ${SRCS} ../res/cannonball.rc)
endif(WIN32)
#if(WIN32)
# set(SRCS ${SRCS} ../res/cannonball.rc)
#endif(WIN32)
if(OPENGL)
add_definitions(-DWITH_OPENGL)
endif()
add_executable(cannonball ${SRCS})
add_executable(cannonball.bc ${SRCS})
# Copy Configuration file to current build
configure_file(../res/config.xml ./config.xml
......
# Emscripten Javascript Build
add_definitions(-DEMSCRIPTEN)
# Use OpenGL for rendering.
set(OPENGL 0)
# Supress useless compiler warnings
set(CMAKE_CXX_FLAGS "-Qunused-arguments")
# XML Config not used by Emscripten
set(xml_directory ./)
# SDL Software Rendering Flags (ignored if OpenGL used)
set(sdl_flags "SDL_SWSURFACE | SDL_DOUBLEBUF")
\ No newline at end of file
Snes9X - good reference!
- Emscripten
- Sound is ok
http://tjwei.github.io/xnes/
Amiga UAE
- Native Javascript
- Sound is iffy
https://github.com/naTmeg/ScriptedAmigaEmulator
JNES
- Native Javascript
- Sound is delayed
- Src: https://github.com/bfirsh/jsnes/
- Uses dynamic audio library: - https://github.com/bfirsh/dynamicaudio.js
http://fir.sh/projects/jsnes/
Useful presentation, high-level overview:
http://de.slideshare.net/andreweissflog3/quovadis2013-cpp-ontheweb
Another good practical guide:
http://forums.tigsource.com/index.php?topic=33268.0
Main Site:
https://github.com/kripken/emscripten
WebGL Experiences:
http://www.sea-of-memes.com/LetsCode80/LetsCode80.html
1/ Followed instructions here, including Visual Studio 2010 setup:
https://github.com/kripken/emscripten/wiki/Using-Emscripten-on-Windows
2/ Then continued with the examples from here:
https://github.com/kripken/emscripten/wiki/Tutorial
Needed to ammend paths to contain forward-slashes rather than back-slashes in the .emscripten config file
3/ Build for Visual Studio 2010 with modified cmake file
"cmake -G "Visual Studio 10" -DTARGET=emscripten ../cmake"
4/ Goto configuration manager and change the cannonball project to build with Emscripten.
Uncheck the option to copy the Win32 options.
Add Boost to the Configuration Properties -> Toolchain Directories -> Library Directories
C:\coding\lib\boost_1_51_0
- Remove boost stuff
- Fix the 3 million Clang compilation warnings and errors
Build with CMake:
https://github.com/kripken/emscripten/blob/master/cmake/Platform/Emscripten.cmake
To use this toolchain file with CMake, invoke CMake with the following command line parameters
cmake -DEMSCRIPTEN=1 -DCMAKE_TOOLCHAIN_FILE=/emscripten/cmake/Platform/Emscripten.cmake -DCMAKE_MODULE_PATH=/emscripten/cmake -DCMAKE_BUILD_TYPE=Debug -G "Unix Makefiles" -DTARGET=emscripten ../cmake
Mingw:
1/ Run CMake as follows:
cmake -DEMSCRIPTEN=1 -DCMAKE_TOOLCHAIN_FILE=c:/utils/emscripten/emscripten/cmake/Platform/Emscripten.cmake -DCMAKE_MODULE_PATH=c:/utils/emscripten/emscripten/cmake -DCMAKE_BUILD_TYPE=Debug -G "Unix Makefiles" -DTARGET=emscripten ../cmake
2/ Then Run Make:
mingw32-make
3/ Rename file to .bc
4/ Then convert to javascript
emcc cannonball.bc -o cannonball.js
---------------------------------------------------------------------------------------------------
WEB AUDIO API
---------------------------------------------------------------------------------------------------
http://www.w3.org/TR/webaudio/
https://developer.mozilla.org/en-US/docs/Web_Audio_API
http://www.html5rocks.com/en/tutorials/webaudio/intro/
Create an AudioBuffer?
AudioContext.createBuffer()
https://developer.mozilla.org/en-US/docs/Web/API/AudioBuffer
***http://developer.apple.com/library/safari/#documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/PlayingandSynthesizingSounds/PlayingandSynthesizingSounds.html
Good speccy sound reference:
https://github.com/gasman/jsspeccy2/blob/master/core/sound.js
Use
1/
ScriptProcessorNode createScriptProcessor(unsigned long bufferSize, // 8192
optional unsigned long numberOfInputChannels = 2,
optional unsigned long numberOfOutputChannels = 2);
2/ The buffer size value controls how frequently onaudioprocess is called
Setup onaudioprocess to fill the buffer as necessary
Create the buffer. Extend the buffer in size
---------------------------------------------------------------------------------------------------
ARRAYS
---------------------------------------------------------------------------------------------------
// See: https://groups.google.com/forum/#!topic/emscripten-discuss/oeEg6WrZ7rg
// http://comments.gmane.org/gmane.comp.compilers.emscripten/302
---------------------------------------------------------------------------------------------------
TODO
---------------------------------------------------------------------------------------------------
- Load Roms from zip
- Add more html based options (widescreen on/off)
- Fix Sound support
- Add mouse controls for phone
- Add Surrounding graphics
---------------------------------------------------------------------------------------------------
DONE
---------------------------------------------------------------------------------------------------
1/ Implement the replacement main loop defined here:
https://github.com/kripken/emscripten/wiki/Emscripten-browser-environment
2/ Implement the replacement file system defined here:
https://github.com/kripken/emscripten/wiki/Filesystem-Guide
\ No newline at end of file
@echo off
set WORKING_DIR="C:\coding\cannonball\emscripten"
set FIREFOX_DIR="C:\Program Files (x86)\FirefoxNightly"
if "%1"=="cmake" goto cmake
if "%1"=="build" goto build
if "%1"=="convertold" goto convertold
if "%1"=="convertslow" goto convertslow
if "%1"=="convert" goto convert
if "%1"=="run" goto run
if "%1"=="clean" goto clean
:cmake
if not exist %WORKING_DIR% mkdir %WORKING_DIR%
cd %WORKING_DIR%
cmake -DEMSCRIPTEN=1 -DCMAKE_TOOLCHAIN_FILE=c:/utils/emscripten/emscripten/cmake/Platform/Emscripten.cmake -DCMAKE_MODULE_PATH=c:/utils/emscripten/emscripten/cmake -DCMAKE_BUILD_TYPE=Debug -G "Unix Makefiles" -DTARGET=emscripten ../cmake
cd..
goto end
:build
cd %WORKING_DIR%
mingw32-make
cd..
goto end
:convertold
cd %WORKING_DIR%
echo Converting .bc to .js (OPTIMIZED)
call emcc -O2 --llvm-lto 1 -closure 1 -s DOUBLE_MODE=0 cannonball.bc -o cannonball.html --preload-file C:\coding\cannonball\roms@roms
cd..
goto end
:convertslow
cd %WORKING_DIR%
echo Converting .bc to .js (Not Optimized)
call emcc -s EXPORTED_FUNCTIONS="['_emscripten_init', '_emscripten_tick', '_emscripten_setfps', '_emscripten_audio']" ^
cannonball.bc -o cannonball.html --preload-file C:\coding\cannonball\roms@roms
cd..
goto end
:convert
cd %WORKING_DIR%
echo Converting with new html... (OPTIMIZED)
rem call emcc -O2 --llvm-lto 1 -closure 1 -s DOUBLE_MODE=0 ^
call emcc -O2 -s DOUBLE_MODE=0 -closure 1 ^
call emcc -s EXPORTED_FUNCTIONS="['_emscripten_init', '_emscripten_tick', '_emscripten_setfps', '_emscripten_audio', '_emscripten_set_framesize']" ^
cannonball.bc -o cannonball.html --preload-file C:\coding\cannonball\roms@roms
echo Extracting Javascript from HTML
cd..
python extract_script.py %WORKING_DIR%/cannonball.html > %WORKING_DIR%/cannonball.html.js
del %WORKING_DIR%\cannonball.html
copy cannonball.html %WORKING_DIR%
goto end
:run
cd %WORKING_DIR%
%FIREFOX_DIR%\firefox %WORKING_DIR%\cannonball.html
cd..
goto end
:clean
if not exist %WORKING_DIR% echo Directory: %WORKING_DIR% not found!
if exist %WORKING_DIR% rm -rf emscripten
goto end
:end
\ No newline at end of file
import re
import sys
with open(sys.argv[1], "r") as f:
buf=f.read()
while 1:
m=re.search(r"<script [^>]*>|<script>", buf, re.DOTALL|re.MULTILINE)
if m==None:
break
buf=buf[m.end(0):]
m=re.search(r"</script [^>]*>|</script>", buf, re.DOTALL|re.MULTILINE)
if m==None:
break
print buf[:m.start(0)]
print
buf=buf[m.end(0):]
\ No newline at end of file
emcc [options] file...
Most normal gcc/g++ options will work, for example:
--help Display this information
--version Display compiler version information
Options that are modified or new in emcc include:
-O0 No optimizations (default)
-O1 Simple optimizations, including asm.js, LLVM -O1
optimizations, and no runtime assertions
or C++ exception catching (to re-enable
C++ exception catching, use
-s DISABLE_EXCEPTION_CATCHING=0 ).
(For details on the affects of different
opt levels, see apply_opt_level() in
tools/shared.py and also src/settings.js.)
Note: Optimizations are only done when
compiling to JavaScript, not to intermediate
bitcode, *unless* you build with
EMCC_OPTIMIZE_NORMALLY=1 (not recommended
unless you know what you are doing!)
-O2 As -O1, plus the relooper (loop recreation),
LLVM -O2 optimizations, and
-s ALIASING_FUNCTION_POINTERS=1
-O3 As -O2, plus dangerous optimizations that may
break the generated code! This adds
-s FORCE_ALIGNED_MEMORY=1
-s DOUBLE_MODE=0
-s PRECISE_I64_MATH=0
--closure 1
--llvm-lto 1
This is not recommended at all. A better idea
is to try each of these separately on top of
-O2 to see what works. See the wiki and
src/settings.js (for the -s options) for more
information.
-s OPTION=VALUE JavaScript code generation option passed
into the emscripten compiler. For the
available options, see src/settings.js
Note that for options that are lists, you
need quotation marks in most shells, for
example
-s RUNTIME_LINKED_LIBS="['liblib.so']"
or
-s "RUNTIME_LINKED_LIBS=['liblib.so']"
(without the external "s in either of those,
you would get an error)
You can also specify a file from which the
value would be read, for example,
-s DEAD_FUNCTIONS=@/path/to/file