日記帳

日記です。

TestSprite.bsh

なんか恒例のやつ.

BeanShell では任意の Java オブジェクトが扱えます.gcjコンパイルしてあるので sdl4gcj*1を使えば当然 SDL も扱えるわけです.

そういうわけで SDL の testsprite.c の BeanShell 版.

#!/usr/bin/bsh-Interpreter
import sdl4gcj.*;
import sdl4gcj.video.*;
import sdl4gcj.event.*;
import sdl4gcj.input.*;

TestSprite()
{
	final int MAX_SPEED = 1;
	final Random random = new java.util.Random();

	Sprite()
	{
		// init size
		int w = super.icon.w;
		int h = super.icon.h;
		int areaW = super.screen.w - w;
		int areaH = super.screen.h - h;

		// init location
		int x = random.nextInt(areaW);
		int y = random.nextInt(areaH);

		// init speed
		int dx, dy;
		while(dx == 0 && dy == 0)
		{
			dx = random.nextInt(MAX_SPEED * 2 + 1) - MAX_SPEED;
			dy = random.nextInt(MAX_SPEED * 2 + 1) - MAX_SPEED;
		}

		// for Rect interface
		int getX(){ return x; }
		int getY(){ return y; }
		int getW(){ return w; }
		int getH(){ return h; }

		void move()
		{
			x = x + dx;
			if (x < 0 || x > areaW)
			{
				dx = -dx;
				x += dx;
			}

			y = y + dy;
			if (y < 0 || y > areaH)
			{
				dy = -dy;
				y += dy;
			}
		} 

		void draw()
		{
			super.screen.blitSurface(super.icon, x, y);
		}
		return this;
	}

	int screenWidth = 640;
	int screenHeight = 480;
	int bitsPerPixel = 8;
	int videoFlags = Screen.SDL_SWSURFACE;
	int numberOfSprite = 100;

	Surface screen = null;
	Surface icon = null;

	int bgColor = 0;
	Object[] sprites = null;
	Rect[] rects = null;
	long ticks = 0;
	int frames = 0;
	boolean useFlip = false;

	void run()
	{
		this.initScreen();
		this.initIcon();
		this.initSprites();
		this.printInfo();
		this.mainLoop();
		this.freeIcon();
	}

	double getFPS()
	{
		return ((double)frames * 1000)/ticks;
	}

	void initScreen()
	{
		screen = Screen.setVideoMode(
			screenWidth, screenHeight,
			bitsPerPixel, videoFlags);

		bgColor = screen.mapRGB(Color.BLACK); 

		if ((screen.flags & screen.SDL_DOUBLEBUF) != 0) useFlip = true;
	}

	void initIcon()
	{
		// load icon image and change pixel format
		icon = Surface.loadBMP("icon.bmp");
		icon.setColorKey(Surface.SDL_SRCCOLORKEY|Surface.SDL_RLEACCEL);
		icon.displayFormat();

		// Run a sample blit to trigger blit acceleration 
		screen.blitSurface(icon);
		screen.fillRect(bgColor);
	}

	void freeIcon()
	{
		icon.freeSurface();
		icon = null;
	}

	void initSprites()
	{
		sprites = new Object[numberOfSprite];
		rects = new Rect[numberOfSprite];
		for (int i = numberOfSprite-1;i >= 0;i--)
		{
			sprites[i] = Sprite();
			rects[i] = (Rect)sprites[i];
		}
	}

	void updateSprites()
	{
		for (sprite : sprites)
		{
			sprite.move();
			sprite.draw();
		}
	} 

	void updateScreen()
	{
		if (useFlip)
			screen.flip();
		else
			screen.updateRects(rects);
	}

	void mainLoop()
	{
		boolean done = false;
		EventManager event = new EventManager();
		frames = 0;
		System.gc();
		ticks = SDLSystem.getTicks();
		while (!done)
		{
			frames++;
			while(event.pollEvent() > 0)
			{
				switch (event.type)
				{
					case event.SDL_KEYDOWN:
					case event.SDL_QUIT:
						done = true;
						break;
					default:
						break;
				}
			}

			screen.fillRect(bgColor);
			updateSprites();
			updateScreen();
		}

		ticks = SDLSystem.getTicks() - ticks;
	} 

	void printInfo()
	{
		if (screen != null)
		{
			System.out.println("Screen is at " + screen.getPixelFormat().getBitsPerPixel() + " bits per pixel");
			var flags = screen.getFlags();
			if ((flags & Surface.SDL_HWSURFACE) == Surface.SDL_HWSURFACE)
				System.out.println("Screen is in video memory");
			else
				System.out.println("Screen is in system memory");

			if ((flags & Surface.SDL_DOUBLEBUF) == Surface.SDL_DOUBLEBUF)
				System.out.println("Screen has double-buffering enabled");
		}

		if (icon != null)
		{
			var iconFlags = icon.getFlags();
			if ((iconFlags & Surface.SDL_HWSURFACE) == Surface.SDL_HWSURFACE)
				System.out.println("Sprite is in video memory");
			else
				System.out.println("Sprite is in system memory");

			if ((iconFlags & Surface.SDL_HWACCEL) == Surface.SDL_HWACCEL)
				System.out.println("Sprite blit uses hardware acceleration");

			if ((iconFlags & Surface.SDL_RLEACCEL) == Surface.SDL_RLEACCEL)
				System.out.println("Sprite blit uses RLE acceleration");
		}
	}
	return this;
}

// main
SDLSystem.init(SDLSystem.SDL_INIT_VIDEO);
try
{
	testSprite = TestSprite();
//	testSprite.numberOfSprite = 50;
//	testSprite.videoFlags = Screen.SDL_ANYFORMAT;
//	testSprite.videoFlags = Screen.SDL_DOUBLEBUF|Screen.SDL_FULLSCREEN;
//	testSprite.bitsPerPixel = 16;
	testSprite.run();
	System.out.println(testSprite.getFPS() + " frames per second");
}
catch (e)
{
	System.out.println(e);
	e.printStackTrace();
}
finally
{
	System.out.println("SDLSystem.quit()");
	SDLSystem.quit();
}

なんかすっごいJavaそっくり.とりあえず実行してみる.

% bsh-Interpreter TestSprite.bsh 
Screen is at 32 bits per pixel
Screen is in system memory
Sprite is in system memory
Sprite blit uses RLE acceleration
42.78280978994026 frames per second

C言語版,Java版,JavaScript版と比べると以下のような感じです.

C言語 327.05
Java版(gcj版) 329.95
JavaScript版(インタープリタ) 127.51
JavaScript版(コンパイル) 162.48
BeanShell版 42.78

実行環境は以下の通り.

  • Linux 2.4.31
  • XOrg 6.8.2
  • gcj 3.3.2
  • X は 32 bpp で起動
  • SDL は 32 bpp の SDL_SWSURFACE を Window モードで作成

とりあえず結論,すっごく遅い.