Dorenal

Need some help to get started the good way

Recommended Posts

Hello everyone :)

I have a school project, which is called "corewar" to do. This is about coding a virtual machine in C, and running basic programs into its memory.

I want to go further and create a "visualizator", which allow anyone to see what's inside the memory during each cpu cyle.

Well, might sound very abstract, so here are som few pic to illustrate how it looks:

  • A gif
  • A video
  • And my first implementation (see attached video)

I have tried to use p5js but it's too slow. So I'm trying pixi, but I don't know how to get started, good practices, etc...

I read the code reference (the doc), which is pretty handy, but it didn't give me any advises of how to structure my code, the right way to use classes (as PIXI.Graphics for example), etc...

Right now, my code looks like this:

function setup() {
	const app = new PIXI.Application({ width: CANVAS_WIDTH, height: CANVAS_HEIGHT });
	console.log("Setting PIXI dimension to ", CANVAS_WIDTH, "x", CANVAS_HEIGHT);
	const bytes = initializeBytes(app); // An array containing all our bytes
	const processes = []; // An array containing all our processes
	
	document.getElementById('ram-app').appendChild(app.view);
	bytes.forEach(b => b.draw());
}

class Byte {
	/**
	 * rect: PIXI.Rectangle instance
	 * renderer: PIXI.Graphics instance
	 */
	constructor(rect, renderer) {
		this.rect = rect;
		this.renderer = renderer;
		this.color = 0xFFFFFF;
	}

	draw() {
		const { x, y, width, height } = this.rect;
		this.renderer
			.clear()
			.lineStyle(1, 0x000000, 1, 0)
			.beginFill(this.color)
			.drawRoundedRect(x, y, width, height, 3)
			.endFill();
	}
}

I have 4096 bytes (so 4096 instances of PIXI.Rectangle and PIXI.Graphics), and it's already really slow. I mean, I only have 20 FPS like this.

I'm pretty sure I'm missing some obvious points, so any help to improve this code, and make it run @60FPS will be really apprecated :)

 

Thanks a lot!

Share this post


Link to post
Share on other sites

Every element can be a sprite with certain "tint" field (red, green, whatever), that way it'll be better. I cant just say "use sprites in all cases" , as I said. For example,. in pixi-v4 sprites are batched but in v5 Graphics are batched too so its cool! However Graphics allows to put many rectangles in it and cache their vertex buffers, you can hold one Graphics per row and re-calculate only when the row is changed, that'll be more efficient than just sprites.

I suggest to try Graphics-per-row approach or make 4000 sprites with same texture but different tint. Maybe you can make two sprites per square , one outer and one inner. 

In any case, Text should be BitmapText because every text element is basically +1 dynamic texture and if you have 4000 of them that'll be huge problem.

I wish you look with all of that, post more, post demos, I'll help when you stuck on strange problems.

Share this post


Link to post
Share on other sites

Hello Ivan :)

First and foremost, thanks a lot for your guidance. Pixi looks really tought for a beginner like me which has never used any graphical lib before (I mean, other than HTML/CSS)

So, if I understand well, your advise is to create only one instance of PIXI.Graphics per row, and update all the row if needed, am I right ?

I have tried it, and it looks really more effective. Currently, my code look like this:

/**
 * Global defines
 */
const BYTE_COUNT = 4096;

const BYTE_PER_LINE = 64;
const BYTE_WIDTH = 20;

const CANVAS_WIDTH = BYTE_WIDTH * BYTE_PER_LINE;
const CANVAS_HEIGHT = parseInt(BYTE_COUNT / BYTE_PER_LINE) * BYTE_WIDTH;

/**
 * App
 */
function setup() {
	const app = new PIXI.Application({ width: CANVAS_WIDTH, height: CANVAS_HEIGHT });
	document.getElementById('ram-app').appendChild(app.view);

	console.log("Setting PIXI dimension to ", CANVAS_WIDTH, "x", CANVAS_HEIGHT);
	const bytes = initializeBytes();
	const rows = initializeRows(bytes);
	console.log(rows);
	
	console.time('drawAll');
	rows.forEach(row => {
		app.stage.addChild(row.renderer);
		row.render();
	});
	console.timeEnd('drawAll');
}

function initializeBytes() {
	const bytes = [];

	for (let i = 0; i < BYTE_COUNT; i++) {
		const x = i * BYTE_WIDTH % (BYTE_PER_LINE * BYTE_WIDTH);
		const y = parseInt(i / BYTE_PER_LINE) * BYTE_WIDTH;
		const rect = new PIXI.Rectangle(x, y, BYTE_WIDTH, BYTE_WIDTH);
		bytes.push(rect);
	}

	return bytes;
}

function initializeRows(bytes) {
	const rows = [];

	for (let i = 0; i < BYTE_COUNT / BYTE_PER_LINE; i++) {
		const start = i * BYTE_PER_LINE;
		const end = start + BYTE_PER_LINE;
		const row = new Row(start, end, bytes)
		rows.push(row);
	}

	return rows;
}

class Row {
	constructor(start, end, bytes) {
		this.start = start;
		this.end = end;
		this.bytes = bytes;
		this.renderer = new PIXI.Graphics();
	}

	render() {
		this.renderer.clear();
		for (let i = this.start; i < this.end; i++) {
			const rect = this.bytes[i];
			const fillColor = 0x3c40c6; // Player color

			this.renderer
				.lineStyle(1, 0xffffff)
				.beginFill(fillColor)
				.drawShape(rect)
				.endFill();
		}
	}
}

/**
 * Running everything
 */
document.addEventListener('DOMContentLoaded', () => setup());

Everything is pretty fast, drawing all the 4096 rectangle took only 20ms. So it's much better than before, and so much better compared to p5 which take 450ms (20x more).

What I don't get is that I don't have 60FPS at this point, I got only 54~57. And this is weird because all I do is drawing rectangle ONE time.

Do you think I'm missing something ?

See and download the full code here.

 

Next, I was thinking about using sprites instead of graphics, because it could be really fun and more visual. I could use texture like grass, water, lava and sand to materialize each player. But let's do things in order.

Share this post


Link to post
Share on other sites

OK, we can make that geometry lesser. Try two drawrects instead of lineStyle+drawRect: first make black rectangle, then red one over it with smaller size. Because right now every frame is like 8 vertices + square inside is 4 , its 12 total. 2 squares are only 8 frames. I dont know whether it helps, its just the biggest number here is number of vertices and we can try to reduce it :)

That'll lead to overdraw, x2 for each pixel, so, like i told before, its all about balance.

Share this post


Link to post
Share on other sites

Actually, there's special shader for rects-only graphics, https://github.com/pixijs/pixi-tilemap/ . I suggest you to update your pixijs to latest v4, and add JS from this plugin. Put all your 22x22 images in one texture, make textures out of it (new PIXI.Texture(baseTexture, rect) ) and then fill tilemaps, one tilemap per row like for graphics, or maybe for more rows. I think for your case this thing will be so fast that it'll be fine to use one huge tilemap and refill it every time something in memory changes. 

Its very fast because it doesnt spawn extra JS objects, it just fills the buffers with rectangles you supply. Dont drop your current app, just make an alternative to test if tilemap is faster for you.

tilemap.addFrame(redTexture, x, y);

Also you can decrease memory consumption here: https://github.com/pixijs/pixi-tilemap/blob/master/src/Constant.ts#L3, like "PIXI.tilemap.Constant.maxTexture=1". That thing puts several texture in its own temporary texture because of special conditions from RpgMaker MV (that's why that plugin was made!).

Share this post


Link to post
Share on other sites
45 minutes ago, ivan.popelyshev said:

OK, we can make that geometry lesser. Try two drawrects instead of lineStyle+drawRect: first make black rectangle, then red one over it with smaller size. Because right now every frame is like 8 vertices + square inside is 4 , its 12 total. 2 squares are only 8 frames. I dont know whether it helps, its just the biggest number here is number of vertices and we can try to reduce it :)

That'll lead to overdraw, x2 for each pixel, so, like i told before, its all about balance.

Well, the rect borders are not needed, so I can remove it. Later, I'll probably need to put borders but only around certains rect.

But I still don't have any clue about why the framerate is so low, I mean I didn't use any loop or redraw everything at each frame, so why ?

Share this post


Link to post
Share on other sites

because pixi does redraw it every frame. Surprise! https://github.com/pixijs/pixi.js/wiki/v5-Custom-Application-GameLoop

You can make your own Application class, or somehow override the original by adding extra flag inside"render()" method so it doesnt run every frame.

I suggest to make your own. Application is mashup made for demos, to show people that pixi is easy. In reality, all projects need their own implementations of this class.

Here it is for v4: https://github.com/pixijs/pixi.js/blob/v4.x/src/core/Application.js

It can be made much easier if you remove all those options.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.