<\/a>Here’s a screenshot that won’t excite anybody but me.<\/p>\nCan you spot the exciting thing? \u00a0It’s not the cylinders (which should hopefully be proper trees by the end of the day, but are as of yet still just cylinders waiting to be shaped) — it’s actually the little green horizontal line at the bottom left corner of the screen.<\/p>\n
You’ve seen screenshots with that little green (and sometimes red or blue) line before. \u00a0It only shows up in my debugging builds, not on the builds that I release, and it helps me gauge how quickly the game is currently running.<\/p>\n
The two vertical blue bars are markers. \u00a0If the horizontal line doesn’t reach the first blue marker, then we’re running at 60fps. \u00a0If the horizontal line passes the first marker but doesn’t reach the second blue marker, then we’re running at 30fps. \u00a0Beyond that, the frame rate is really low.<\/p>\n
The exciting thing is that if I had taken this screenshot on this computer yesterday, that horizontal green line would have stretched all the way over to the first blue marker — we’re drawing in half the time that it took before, in this same situation.<\/p>\n
See, the VectorStorm library has always been phenomenally slow at drawing bitmapped text. \u00a0And when the context matrix (the box in the top right corner) is open, it really would slow things down to an extreme degree. \u00a0And unfortunately, this was difficult to solve because the real problem was caused by the VectorStorm library’s fundamental approach to drawing.<\/p>\n
I first started writing the VectorStorm library when I was made a lead programmer at work. \u00a0I was panicking at being given that level of responsibility when there were so many areas of programming which I didn’t know about; my areas of expertise were cameras, player control, and networking. \u00a0I was, in industry parlance, a “games programmer”, as opposed to a physics programmer or a graphics programmer or an engine programmer, or other “smart-person-required” role. \u00a0My knowledge outside of these fields was strictly limited.<\/p>\n
So I set myself a goal to write a simple game engine from scratch, with all the bells and whistles of the commercial engines which were written by the engine programmers at my workplace (or “the smart guys”, as I called them in my head), but which wasn’t necessarily hyper-tuned for speed the way that those professional ones were. \u00a0 After all, \u00a0I was just a games programmer, right?<\/p>\n
The real goal of writing VectorStorm was originally just to quickly learn enough not to embarrass myself at work.<\/p>\n
At about this time, I was reading about old coin-op video games, and read that “Asteroids” was effectively a two-processor machine. \u00a0One processor read the control input and assembled a list of vector lines, which it passed to the second processor, and the second processor controlled the vector display — drawing those lines. \u00a0I thought that this was a really quirky approach, and so I decided that it’d be interesting (and somewhat silly) to use that same approach in my game engine. \u00a0This intriguing set-up was why VectorStorm rendered using vector graphics. \u00a0(The “Storm” part of the name is because I started by taking a lot of code from a previous game I’d written, “GemStorm”, which was a Bejewelled rip-off that I wrote for my own amusement, and never released publicly)<\/p>\n
Anyhow, this is where the vsDisplayList came from — it’s the list of drawing commands that gets passed from one processor to another. \u00a0Inside VectorStorm, that “second processor” is actually one of the “vsRenderer” classes, which convert from the drawing instructions in the vsDisplayList into OpenGL rendering commands.<\/p>\n
I thought this was amusing and a bit Rube Goldberg-esque. \u00a0After all, the example and tutorial code you see on the web just has people calling straight into OpenGL from game code, or else from within drawing functions exposed by the game engine. \u00a0The whole concept of pushing an opcode-based set of drawing instructions into a data buffer, only to have a different piece of code pull those instructions out again and call into OpenGL just seemed somewhat farcical.<\/p>\n
(Side-note: \u00a0Only about two months ago, I was talking with a graphics programmer co-worker and learned that three of the four professional game engines I’ve worked with over the past decade have had fundamentally this same architecture; assembling drawing commands into a data buffer which gets passed across to something else which pulls them out again and actually performs the drawing. \u00a0Colour me shocked.)<\/em><\/p>\nAnyway. \u00a0When it came time to extend VectorStorm into 3D and handle larger datasets, it started becoming very expensive to push lots of data into the display list. \u00a0When you have thousands of vertices, you really don’t want to be spending computer time writing and reading them into that vsDisplayList. \u00a0Instead, you want to be able to simply tell the display list where those vertices can be found.<\/p>\n
So I implemented OpenGL VBOs (this was at about the time I was writing Lair). \u00a0 VBOs are a way to store data directly on the video card, and then refer to it by an ID number, later. \u00a0Once I’d done this, I could just have the display list say “Render the stuff I’ve already put on the card”. \u00a0Executing that command is heaps faster than pulling an array of data in\/out of the data buffer and setting up OpenGL to render from it. \u00a0So all was good, right?<\/p>\n
Well, no. \u00a0There was a big problem. \u00a0The problem was this:<\/p>\n
The problem was that vsDisplayLists don’t understand that they’re referring to external data, and so when you destroy a display list, it can’t clean up that external data.<\/p>\n
To recap: \u00a0To get the best performance, vsDisplayLists needed to stop containing the actual data to be drawn, and instead just to refer to data that’s stored elsewhere. \u00a0By doing this, it implies that you need a “somewhere else” to store that data, which can handle cleaning up after itself.<\/p>\n
Unfortunately, there is no standard “somewhere else” for that data to be stored, especially if you were loading display lists raw from disk, using vsDisplayList::Load(“MyFile”). \u00a0There was nowhere to put the data, so it just got placed directly into the vsDisplayList for lack of anywhere else to put it. \u00a0Which meant that you didn’t get the performance benefits of the VBOs.<\/p>\n
Sure, special classes could create their own VBOs and make their own display lists which used them, but that required special code for every class that was going to do it. \u00a0(As a result, virtually every single renderable thing in Lair and in MMORPG Tycoon 2 has custom code handling its rendering)<\/p>\n
See, VectorStorm uses the vsDisplayList as the fundamental drawing primitive everywhere; \u00a0everything, everything creates and returns and uses vsDisplayLists. \u00a0Which means that if something creates something to be rendered, it has nowhere to actually put the data that it generates, except for directly into the vsDisplayList.<\/p>\n
While I was making Lair, I created the vsFont class, for creating vsDisplayLists which would render bitmapped fonts. \u00a0It’s an awesome example of this problem; \u00a0since it’s just returning a vsDisplayList, it couldn’t put data onto the video card and embed references to it in the vsDisplayList, as there’d be no way to clean up that data on the card once the string wasn’t being used any more. \u00a0Instead, the vsFont just wrote the rendering data straight into the vsDisplayList. \u00a0This meant that the vsDisplayLists for rendering strings used an awful lot of memory, and so also took a long time to render.<\/p>\n
Several months back, when I was first implementing MMORPG Tycoon 2’s quest editing UI, I finally got frustrated enough to try to fix the problem; \u00a0even short strings were taking hundreds of kilobytes to store for rendering, which was far more than was sensible.<\/p>\n
And I found an improvement. \u00a0This improvement was to have each vsFont create a set of data on the video card for each glyph in the font. \u00a0Then, instead of putting all the vertex data directly into the display list, it only needed to put “Move here”, “Get ready to draw the first glyph”, “Draw it”, “Move there”, “Get ready to draw the second glyph”, “Draw it”, etc. commands. \u00a0And the vsFont itself could clean up all that glyph data on the card, when the font was no longer needed. \u00a0Suddenly, strings which used to require 300kb to render, now only required 40kb. \u00a0This was a massive memory saving! \u00a0And this is the font rendering that was used in the MS1 build.<\/p>\n
But really, that was just finding a clever hack around the fundamental problem, that for best performance, vsDisplayList needed to refer to data, not to own it. \u00a0But that for general game-use, when I had utilities creating vsDisplayLists, I needed a generic place to store the data which the vsDisplayLists were referring to, and a way to clean up that data once it was no longer needed.<\/p>\n
But now I’ve finally addressed the issue for real.<\/p>\n
I’ve created a class called the ‘vsFragment’ (I don’t know if that name will stick. \u00a0It’s not a good name. \u00a0But it’s what I’m using right now). \u00a0A ‘vsFragment’ contains a material, some rendering data (for example, references to vertices that are stored on the video card), and a display list. \u00a0When it’s destroyed, the vsFragment automatically cleans up all of that data which it had been using. \u00a0This basically means that game code can now use vsFragments the way that it used to use vsDisplayLists, and the vsFragments can now safely assume that they own the resources they had been using.<\/p>\n
I’ve added support for putting any number of vsFragments onto vsSprites or vsModels, in addition to the raw vsDisplayList they already have. \u00a0At some point in the future, I expect that raw vsDisplayList is going to go away, and vsFragments will become the only way to render. \u00a0I haven’t quite figured out the rendering structure that’ll be in use then, but there’s no real rush; \u00a0things work just fine while supporting both the new and the old system simultaneously.<\/p>\n
So with this new vsFragment class, I’ve now modified vsFont such that instead of returning a vsDisplayList, it can return a vsFragment. \u00a0This means that the vsFont can create storage for the string rendering data on the video card, and assign ownership of that data to the vsFragment. \u00a0By doing this, every string, no matter how long it is, can now be rendered by a vsDisplayList that’s just 28 bytes long, and which contains only five drawing instructions in total, since absolutely all of the rendering data has been loaded onto the video card. \u00a0I’ve modified most of the UI to now use the vsFragments, and it’s now drawing much faster than it did this morning.<\/p>\n
In MMORPG Tycoon 2, interpreting all of those text drawing commands in the display lists would often take as much time as drawing the entire rest of the world. \u00a0But now, it’s fine; \u00a0drawing text doesn’t seem to affect the frame rate at all, any longer.<\/p>\n
There’s still a lot of work ahead, of course, converting things over to using vsFragments instead of using vsDisplayLists directly, but this should yield much better performance overall, and also should solve a number of future renderer and performance issues. \u00a0And I’m not going to do it all at once; \u00a0the conversion should be a very slow, gradual process over the coming months.<\/p>\n","protected":false},"excerpt":{"rendered":"
Here’s a screenshot that won’t excite anybody but me. Can you spot the exciting thing? \u00a0It’s not the cylinders (which should hopefully be proper trees by the end of the day, but are as of yet still just cylinders waiting to be shaped) — it’s actually the little green horizontal line at the bottom left…<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":""},"categories":[33,24,25],"tags":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/po9WK-ny","_links":{"self":[{"href":"https:\/\/www.vectorstorm.com.au\/wp-json\/wp\/v2\/posts\/1460"}],"collection":[{"href":"https:\/\/www.vectorstorm.com.au\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.vectorstorm.com.au\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.vectorstorm.com.au\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.vectorstorm.com.au\/wp-json\/wp\/v2\/comments?post=1460"}],"version-history":[{"count":0,"href":"https:\/\/www.vectorstorm.com.au\/wp-json\/wp\/v2\/posts\/1460\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.vectorstorm.com.au\/wp-json\/wp\/v2\/media?parent=1460"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.vectorstorm.com.au\/wp-json\/wp\/v2\/categories?post=1460"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.vectorstorm.com.au\/wp-json\/wp\/v2\/tags?post=1460"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}