preliminary web worker support #4

Merged
merged 3 commits into from Nov 20, 2015
Jump to file or symbol
Failed to load files and symbols.
+491 −47
Diff settings

Always

Just for now

Next

preliminary web worker support

Emulator run in a web worker while A/V output stuff is run in the main
thread.
  • Loading branch information...
taisel committed Nov 19, 2015
commit 4c43ae0ba715d2938953b23733d97311442748c9
View
@@ -19,9 +19,7 @@ function GameBoyAdvanceEmulator() {
"dynamicSpeed":false //Whether to actively change the target speed for best user experience.
};
this.audioFound = 0; //Do we have audio output sink found yet?
- this.loaded = false; //Did we initialize IodineGBA?
- this.faultFound = false; //Did we run into a fatal error?
- this.paused = true; //Are we paused?
+ this.emulatorStatus = 0x10; //{paused, saves loaded, fault found, loaded}
this.offscreenWidth = 240; //Width of the GBA screen.
this.offscreenHeight = 160; //Height of the GBA screen.
this.BIOS = []; //Initialize BIOS as not existing.
@@ -56,32 +54,31 @@ GameBoyAdvanceEmulator.prototype.generateCoreExposed = function () {
}
}
GameBoyAdvanceEmulator.prototype.play = function () {
- if (this.paused) {
- this.paused = false;
- if (!this.loaded && this.BIOS && this.ROM) {
+ if ((this.emulatorStatus | 0) >= 0x10) {
+ this.emulatorStatus = this.emulatorStatus & 0xF;
+ if ((this.emulatorStatus & 0x1) == 0 && this.BIOS && this.ROM) {
this.initializeCore();
- this.loaded = true;
+ this.emulatorStatus = this.emulatorStatus | 0x1;
this.importSave();
}
this.invalidateMetrics();
this.setBufferSpace();
}
}
GameBoyAdvanceEmulator.prototype.pause = function () {
- if (!this.paused) {
+ if ((this.emulatorStatus | 0) < 0x10) {
this.exportSave();
- this.paused = true;
+ this.emulatorStatus = this.emulatorStatus | 0x10;
}
}
GameBoyAdvanceEmulator.prototype.stop = function () {
- this.faultFound = false;
- this.loaded = false;
+ this.emulatorStatus = this.emulatorStatus & 0x1C;
this.audioUpdateState = 1;
this.pause();
}
GameBoyAdvanceEmulator.prototype.restart = function () {
- if (this.loaded) {
- this.faultFound = false;
+ if ((this.emulatorStatus & 0x1) == 0x1) {
+ this.emulatorStatus = this.emulatorStatus & 0x1D;
this.exportSave();
this.initializeCore();
this.importSave();
@@ -93,25 +90,23 @@ GameBoyAdvanceEmulator.prototype.restart = function () {
GameBoyAdvanceEmulator.prototype.timerCallback = function (lastTimestamp) {
//Callback passes us a reference timestamp:
this.lastTimestamp = +lastTimestamp;
- if (!this.paused) {
- if (!this.faultFound && this.loaded) { //Any error pending or no ROM loaded is a show-stopper!
- this.iterationStartSequence(); //Run start of iteration stuff.
- this.IOCore.enter(this.CPUCyclesTotal | 0); //Step through the emulation core loop.
- this.iterationEndSequence(); //Run end of iteration stuff.
- }
- else {
- this.pause(); //Some pending error is preventing execution, so pause.
- }
+ if ((this.emulatorStatus | 0) == 0x5) { //Any error pending or no ROM loaded is a show-stopper!
+ this.iterationStartSequence(); //Run start of iteration stuff.
+ this.IOCore.enter(this.CPUCyclesTotal | 0); //Step through the emulation core loop.
+ this.iterationEndSequence(); //Run end of iteration stuff.
+ }
+ else {
+ this.pause(); //Some pending error is preventing execution, so pause.
}
}
GameBoyAdvanceEmulator.prototype.iterationStartSequence = function () {
this.calculateSpeedPercentage(); //Calculate the emulator realtime run speed heuristics.
- this.faultFound = true; //If the end routine doesn't unset this, then we are marked as having crashed.
+ this.emulatorStatus = this.emulatorStatus | 0x2; //If the end routine doesn't unset this, then we are marked as having crashed.
this.audioUnderrunAdjustment(); //If audio is enabled, look to see how much we should overclock by to maintain the audio buffer.
this.audioPushNewState(); //Check to see if we need to update the audio core for any output changes.
}
GameBoyAdvanceEmulator.prototype.iterationEndSequence = function () {
- this.faultFound = false; //If core did not throw while running, unset the fatal error flag.
+ this.emulatorStatus = this.emulatorStatus & 0x1D; //If core did not throw while running, unset the fatal error flag.
this.clockCyclesSinceStart = ((this.clockCyclesSinceStart | 0) + (this.CPUCyclesTotal | 0)) | 0; //Accumulate tracking.
}
GameBoyAdvanceEmulator.prototype.attachROM = function (ROM) {
@@ -123,7 +118,7 @@ GameBoyAdvanceEmulator.prototype.attachBIOS = function (BIOS) {
this.BIOS = BIOS;
}
GameBoyAdvanceEmulator.prototype.getGameName = function () {
- if (!this.faultFound && this.loaded) {
+ if ((this.emulatorStatus & 0x3) == 0x1) {
return this.IOCore.cartridge.name;
}
else {
@@ -149,23 +144,31 @@ GameBoyAdvanceEmulator.prototype.importSave = function () {
if (this.saveImportHandler) {
var name = this.getGameName();
if (name != "") {
- var save = this.saveImportHandler(name);
- var saveType = this.saveImportHandler("TYPE_" + name);
- if (save && saveType && !this.faultFound && this.loaded) {
- var length = save.length | 0;
- var convertedSave = getUint8Array(length | 0);
- if ((length | 0) > 0) {
- for (var index = 0; (index | 0) < (length | 0); index = ((index | 0) + 1) | 0) {
- convertedSave[index | 0] = save[index | 0] & 0xFF;
+ var parentObj = this;
+ this.emulatorStatus = this.emulatorStatus & 0x1B;
+ this.saveImportHandler(name, function (save) {
+ parentObj.emulatorStatus = parentObj.emulatorStatus & 0x1B;
+ parentObj.saveImportHandler("TYPE_" + name, function (saveType) {
+ if (save && saveType && (parentObj.emulatorStatus & 0x3) == 0x1) {
+ var length = save.length | 0;
+ var convertedSave = getUint8Array(length | 0);
+ if ((length | 0) > 0) {
+ for (var index = 0; (index | 0) < (length | 0); index = ((index | 0) + 1) | 0) {
+ convertedSave[index | 0] = save[index | 0] & 0xFF;
+ }
+ parentObj.IOCore.saves.importSave(convertedSave, saveType | 0);
+ parentObj.emulatorStatus = parentObj.emulatorStatus | 0x4;
+ }
}
- this.IOCore.saves.importSave(convertedSave, saveType | 0);
- }
- }
+ }, function (){parentObj.emulatorStatus = parentObj.emulatorStatus | 0x4;});
+ }, function (){parentObj.emulatorStatus = parentObj.emulatorStatus | 0x4;});
+ return;
}
}
+ this.emulatorStatus = this.emulatorStatus | 0x4;
}
GameBoyAdvanceEmulator.prototype.exportSave = function () {
- if (this.saveExportHandler && !this.faultFound && this.loaded) {
+ if (this.saveExportHandler && (this.emulatorStatus & 0x3) == 0x1) {
var save = this.IOCore.saves.exportSave();
var saveType = this.IOCore.saves.exportSaveType();
if (save != null && saveType != null) {
@@ -254,13 +257,13 @@ GameBoyAdvanceEmulator.prototype.initializeCore = function () {
}
GameBoyAdvanceEmulator.prototype.keyDown = function (keyPressed) {
keyPressed = keyPressed | 0;
- if (!this.paused && (keyPressed | 0) >= 0 && (keyPressed | 0) <= 9) {
+ if ((this.emulatorStatus | 0) < 0x10 && (keyPressed | 0) >= 0 && (keyPressed | 0) <= 9) {
this.IOCore.joypad.keyPress(keyPressed | 0);
}
}
GameBoyAdvanceEmulator.prototype.keyUp = function (keyReleased) {
keyReleased = keyReleased | 0;
- if (!this.paused && (keyReleased | 0) >= 0 && (keyReleased | 0) <= 9) {
+ if ((this.emulatorStatus | 0) < 0x10 && (keyReleased | 0) >= 0 && (keyReleased | 0) <= 9) {
this.IOCore.joypad.keyRelease(keyReleased | 0);
}
}
View
@@ -57,6 +57,7 @@
<script src="user_scripts/IodineGBAGraphicsGlueCode.js"></script>
<script src="user_scripts/IodineGBAAudioGlueCode.js"></script>
<script src="user_scripts/IodineGBACoreGlueCode.js"></script>
+ <script src="user_scripts/IodineGBAWorkerGlueCode.js"></script>
<script src="user_scripts/base64.js"></script>
<link rel="stylesheet" href="user_css/main.css">
</head>
@@ -46,7 +46,7 @@ var IodineGUI = {
};
window.onload = function () {
//Initialize Iodine:
- IodineGUI.Iodine = new GameBoyAdvanceEmulator();
+ registerIodineHandler();
//Initialize the timer:
registerTimerHandler();
//Initialize the graphics:
@@ -60,6 +60,27 @@ window.onload = function () {
//Register GUI settings.
registerGUISettings();
}
+function registerIodineHandler() {
+ try {
+ //Try starting Iodine in a webworker:
+ IodineGUI.Iodine = new IodineGBAWorkerShim();
+ addEvent("onbeforeunload", window, registerBeforeUnloadHandler);
+ }
+ catch (e) {
+ //Otherwise just run on-thread:
+ IodineGUI.Iodine = new GameBoyAdvanceEmulator();
+ }
+}
+function registerBeforeUnloadHandler(e) {
+ IodineGUI.Iodine.pause();
+ this.style.display = "none";
+ document.getElementById("play").style.display = "inline";
+ if (e.preventDefault) {
+ e.preventDefault();
+ }
+ removeEvent("onbeforeunload", window, registerBeforeUnloadHandler);
+ return "IodineGBA needs to process your save data, leaving now may result in not saving current data.";
+}
function registerTimerHandler() {
var rate = 4;
IodineGUI.Iodine.setIntervalRate(rate | 0);
@@ -77,7 +98,7 @@ function registerTimerHandler() {
}, rate | 0);
}
function registerBlitterHandler() {
- IodineGUI.Blitter = new GlueCodeGfx();
+ IodineGUI.Blitter = new GlueCodeGfx(240, 160);
IodineGUI.Blitter.attachCanvas(document.getElementById("emulator_target"));
IodineGUI.Iodine.attachGraphicsFrameHandler(function (buffer) {IodineGUI.Blitter.copyBuffer(buffer);});
}
@@ -8,11 +8,11 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-function GlueCodeGfx() {
+function GlueCodeGfx(width, height) {
this.didRAF = false; //Set when rAF has been used.
this.graphicsFound = 0; //Do we have graphics output sink found yet?
- this.offscreenWidth = 240; //Width of the GBA screen.
- this.offscreenHeight = 160; //Height of the GBA screen.
+ this.offscreenWidth = width; //Width of the GBA screen.
+ this.offscreenHeight = height; //Height of the GBA screen.
this.doSmoothing = true;
//Cache some frame buffer lengths:
var offscreenRGBCount = this.offscreenWidth * this.offscreenHeight * 3;
@@ -8,18 +8,19 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-function ImportSaveCallback(name) {
+function ImportSaveCallback(name, callbackFunc, callbackFuncNoSave) {
try {
var save = findValue("SAVE_" + name);
if (save != null) {
writeRedTemporaryText("Loaded save.");
- return base64ToArray(save);
+ callbackFunc(base64ToArray(save));
+ return;
}
}
catch (error) {
writeRedTemporaryText("Could not read save: " + error.message);
}
- return null;
+ callbackFuncNoSave();
}
function ExportSave() {
IodineGUI.Iodine.exportSave();
Oops, something went wrong.