TestSprite.js
Rhino では JavaScript から任意の Java オブジェクトが扱えます. gcj でコンパイルしてあるので sdl4gcj を使えば当然 SDL も扱えるわけです.新しくバインディングを書かなくても SDL が使える言語が増えるのって楽でいいですねぇ♪
ってことで SDL の testsprite.c の JavaScript 版.
importPackage(Packages.java.lang) importPackage(Packages.sdl4gcj) importPackage(Packages.sdl4gcj.video) importPackage(Packages.sdl4gcj.event) importPackage(Packages.sdl4gcj.input) var random = new java.util.Random(); function Sprite(image){ this.screen = Screen.getVideoSurface(); this.rect = Rectangle(); // init size this.image = image; this.movableW = this.screen.w - this.image.w; this.movableH = this.screen.h - this.image.h; this.rect.w = image.w; this.rect.h = image.w; // init location this.rect.x = random.nextInt(this.movableW); this.rect.y = random.nextInt(this.movableH); // init speed while(this.dx == 0 && this.dy == 0) { this.dx = random.nextInt(this.MAX_SPEED * 2 + 1) - this.MAX_SPEED; this.dy = random.nextInt(this.MAX_SPEED * 2 + 1) - this.MAX_SPEED; } } Sprite.prototype = { MAX_SPEED : 1, dx : 0, dy : 0, move : function() { this.rect.x += this.dx; if((this.rect.x < 0) || (this.rect.x >= this.movableW)) { this.dx = -this.dx; this.rect.x += this.dx; } this.rect.y += this.dy; if((this.rect.y < 0) || (this.rect.y >= this.movableH)) { this.dy = -this.dy; this.rect.y += this.dy; } }, draw : function() { this.screen.blitSurface(this.image, this.rect.x, this.rect.y); } } var testSprite = { screenWidth : 640, screenHeight : 480, bitsPerPixel : 8, videoFlags : SDLConstants.SDL_SWSURFACE, numberOfSprite : 100, screen : null, icon : null, bgColor : null, sprites : null, rects : null, ticks : 0, frames : 0, run : function() { this.initScreen(); this.initIcon(); this.initSprites(); this.printInfo(); this.mainLoop(); this.freeIcon(); }, getFPS : function() { return (this.frames * 1000)/this.ticks; }, initScreen : function() { this.screen = Screen.setVideoMode( this.screenWidth, this.screenHeight, this.bitsPerPixel, this.videoFlags); this.bgColor = this.screen.mapRGB(Color.BLACK); }, initIcon : function() { // load icon image and change pixel format this.icon = Surface.loadBMP("icon.bmp"); this.icon.setColorKey(SDLConstants.SDL_SRCCOLORKEY|SDLConstants.SDL_RLEACCEL); this.icon.displayFormat(); // Run a sample blit to trigger blit acceleration this.screen.blitSurface(this.icon); this.screen.fillRect(this.bgColor); }, freeIcon :function() { this.icon.freeSurface(); this.icon = null; }, initSprites : function() { this.sprites = new Array(this.numberOfSprite); this.rects = java.lang.reflect.Array.newInstance(Rect, this.numberOfSprite); for (var i = 0;i < this.numberOfSprite;i++) { this.sprites[i] = new Sprite(this.icon); this.rects[i] = this.sprites[i].rect; } }, updateSprites : function() { for (var i = 0;i < this.numberOfSprite;i++) { this.sprites[i].move(); this.sprites[i].draw(); } }, updateScreen : function() { if ((this.screen.flags & this.screen.SDL_DOUBLEBUF) == this.screen.SDL_DOUBLEBUF) this.screen.flip(); else this.screen.updateRects(this.rects); }, mainLoop : function() { var done = false; var event = new EventManager(); this.frames = 0; this.ticks = SDLSystem.getTicks(); while (!done) { this.frames++; while(event.pollEvent() > 0) { switch (event.type) { case SDLConstants.SDL_KEYDOWN: case SDLConstants.SDL_QUIT: done = true; break; default: break; } } this.screen.fillRect(this.bgColor); this.updateSprites(); this.updateScreen(); } this.ticks = SDLSystem.getTicks() - this.ticks; }, printInfo : function() { if (this.screen != null) { System.out.println("Screen is at " + this.screen.getPixelFormat().getBitsPerPixel() + " bits per pixel"); var flags = this.screen.getFlags(); if ((flags & SDLConstants.SDL_HWSURFACE) == SDLConstants.SDL_HWSURFACE) System.out.println("Screen is in video memory"); else System.out.println("Screen is in system memory"); if ((flags & SDLConstants.SDL_DOUBLEBUF) == SDLConstants.SDL_DOUBLEBUF) System.out.println("Screen has double-buffering enabled"); } if (this.icon != null) { var iconFlags = this.icon.getFlags(); if ((iconFlags & SDLConstants.SDL_HWSURFACE) == SDLConstants.SDL_HWSURFACE) System.out.println("Sprite is in video memory"); else System.out.println("Sprite is in system memory"); if ((iconFlags & SDLConstants.SDL_HWACCEL) == SDLConstants.SDL_HWACCEL) System.out.println("Sprite blit uses hardware acceleration"); if ((iconFlags & SDLConstants.SDL_RLEACCEL) == SDLConstants.SDL_RLEACCEL) System.out.println("Sprite blit uses RLE acceleration"); } } } SDLSystem.init(SDLSystem.SDL_INIT_VIDEO); try { testSprite.videoFlags = SDLConstants.SDL_SWSURFACE; testSprite.bitsPerPixel = 32; testSprite.run(); System.out.println(testSprite.getFPS() + " frames per second"); } catch (e) { System.out.println(e); } finally { System.out.println("SDLSystem.quit()"); SDLSystem.quit(); }
実行環境は Linux + X で gcj は 3.3.2 です.640x480 32 bpp の Window モード SDL_SWSURFACE で測定.
まずはオリジナルの C言語版.
% ./testsprite Screen is at 32 bits per pixel Screen is in system memory Sprite is in system memory Sprite blit uses RLE acceleration 591.46 frames per second
sdl4gcj の TestSprite.java の場合.
% ./TestSprite Screen is at 32 bits per pixel Screen is in system memory Sprite is in system memory Sprite blit uses RLE acceleration 589.5686083353644 frames per second
TestSprite.js を gcj でコンパイルした rhino で実行した場合.
% rhino TestSprite.js Screen is at 32 bits per pixel Screen is in system memory Sprite is in system memory Sprite blit uses RLE acceleration 134.16213416213415 frames per second SDLSystem.quit()
とりあえず遅い.
ECMAScript のNumber 型が常に倍精度浮動小数点なのが効いているのでしょうか? いやそもそも ECMAScript はとっても動的な言語なので遅くならないはずがないですけども…
しかたがない(?)ので jsc + gcj でネイティブコードにコンパイルしてみます.
% jsc -o TestSprite.class TestSprite.js % gcj -O2 -o TestSprite --main=TestSprite TestSprite.class -l-org-mozilla-classfile -l-org-mozilla-javascript TestSprite.java: In class `TestSprite': TestSprite.java: In method `TestSprite._c0(TestSprite,org.mozilla.javascript.Context,org.mozilla.javascript.Scriptable,org.mozilla.javascript.Scriptable,java.lang.Object[])': TestSprite.java:208: 警告: unreachable bytecode from 1047 to before 1052
なんか警告が出たけど実行ファイルはできたので実行してみる.
% ./TestSprite Screen is at 32 bits per pixel Screen is in system memory Sprite is in system memory Sprite blit uses RLE acceleration 169.16916916916918 frames per second SDLSystem.quit()
結構速くなってる! C 言語版や Java 版には遠く及びませんけど…
結果をまとめると以下のような感じ.
C言語版 | 591.46 |
Java版(gcj版) | 589.57 |
JavaScript版(インタープリタ) | 134.16 |
JavaScript版(コンパイル) | 169.17 |
性能は C 言語や Java に及ばないですけど rhino で開発して jsc + gcj でコンパイルとかいろいろ遊べそうでいいですねぇ.