Creating your own engine is bad -- ORLY?

26.02.2010 18:37 in programming, rant

People often say: "never start creating your own engine. If you do so, you will never finish a game".

This may be true. But those people won't tell you what by creating your game, you won't finish it as well.

You can of course create simple game, let's say Tetris, for example. You probably can even write nice, well-organized, object-oriented-and-other-stuff code. OK, few hacks here and there, it's nothing. What next? You can code Snake. Or Arkanoid. How many simple games can you make? But ater how many of them your learning curve will go flat? If you've completed Tetris, Pacman, Snake, Arkanoid and Space Invaders, what can you actually learn from making Bomberman?

You can go deeper and create game with more content. But can you do it by yourself? Most programmers have very inferior graphics skills. You will need a team to do this. But again, most programmers don't have team-management skills. In the end, you'll finish with spending 80% time on team-related issues, and you'll still need to do some artwork by yourself. And that's optimistic scenario. I give a 5% chances of success. In the other 95% your team will just broke and you'll be left with nothing.

That's not a failure. After all, you are programmer, not game-director & team-manager & artist-level designer-musican & everything else. You need to write code.

So we have following charts so far:

Chart
creating simple gamesteamwork

But what if you start coding your own engine? Well, you won't need anybody else (profit in non-commercial "bussiness"). You won't have any specific needs nor deadlines. You can throw out 10KLOC just because it's poorly written -- no need to worry about compability issues, something you'll never afford during "real thing". You can experiment and implement every single new graphic technique you'll read about. You can optimize every bit of code down to assembly level. You won't need hacks to do things "fast". And you learn. And don't stop. So the chart for engine programming looks like this:

Chart

Yes, it's a spiral. Because you constantly start over and over, but every time with new skills.

You will, of course, never finish such engine. But why should you? After few years of such programming you'll have enough skills to do every single programming job you can imagine (assuming you have Not Invented Here disease and code everything from memory managers and containers to high-level networking stuff). You'll know a lot of APIs and ideas. You'll read tons of documentations and publications. What could "simple games programmer" write in his resume? I've done Pacman, Tetris and Arkanoid. Also Space Invaders, twice! I've never gone into deep details but I can code simple game for food. And someone who has tried teamwork? I am good candidate. I have never finished anything, well, to be honest, I have never gone further than concepts and DD.

And even if you're not going to be a professional (game) programmer, you can always have a decent hobby. Some people fix cars, some take care of plants. You can always polish your engine.
:)

OpenGL 3.2 pack #1

25.01.2010 00:34 in OpenGL

Finally I've fully switched my engine code to OpenGL 3.2 (core profile). I have some experiences that I would like to share.

Performance

Well... damn, it's fast! Although GPU itself can't be accelerated much, the CPU/driver part is much faster. Most of all, count of API calls dropped significantly. Few examples:

  • setting up material: instead of 20-30 uniforms and about 5 texture changes I can now upload 1 uniform buffer and 1 texture array (diffuse/normal/specular/...)
  • drawing a mesh: was: few enable/disables, few glXxxPointers, 1 glDrawElements. Now: 1 bind of vertex array object, 1 glDrawElements.
  • updating buffers: previously bind, update, unbind. Now (thanks to EXT_direct_state_access [spec]) just NamedBufferData(buffer_id, ...). Numbers of calls to setup textures, framebuffers and other stuff could also be reduced with DSA. Drawback: no ATI support at the moment.

This may seem like "just a little optimization". But it's not -- especially if you are CPU-bound. On my main development machine with powerful GPU and rather weak CPU the difference was huge. Even up to 10ms! That's 60 FPS -> 200 FPS. On average, there is 10-40% boost.

Uniform Buffer Objects [spec]

I've been using bindable uniforms for some time. However there was a problem: specification gave no standard layout for data and even no methods to determine the layout. In the end, I've been using float4 for everything and packed data manually. That was quite cumbersome, so I've switched to new OpenGL 3.1 UBO (uniform buffer objects). There are 2 big differences between UBO and bindable uniforms [spec].

First of all, you have 3 different layouts in UBO:

  • std140 -- probably most useful. Basically you align float3/float4/structs/arrays/matrices to 16 bytes, float2 to 8 and floats to 4. That is quite OK if you sort your data from biggest to smallest. You would do so in CPU code, right?
  • shared -- data using this layout can be shared across programs, but not GPU vendors. Well, I don't think that messing with structs are worth it, it will probably be the same as std140.
  • packed -- this is an optimised layout, stripping unused variables, rearranging order and so on. But you can't share such buffer across other programs. And if you can't share it, why bother to create unused variables? :) That's mystery, and rather useless feature for me.

And the other difference is quite minor: with uniform buffers you directly bind buffers to uniforms, with UBO you bind them like textures. So you bind buffers to "slots", and bind those slots to uniforms. OpenGL makers seem to like it a lot.

Setting buffer data & bugs (?)

In my particles code I have found a very annoying bug called "random mess shows up on screen". What was wrong? Finally I've made this piece of code:

GLuint id;
glGenBuffers(1, &id);
glBindBuffer(GL_ARRAY_BUFFER, id);
glBufferData(GL_ARRAY_BUFFER, size_of_data, data, GL_STREAM_COPY);
glGetBufferSubData(GL_ARRAY_BUFFER, 0, size_of_data, data2);
if (data != data2) panic();

Of course initially there was BufferSubData instead of generating buffer. The result was a mess. No GL errors raised, but data was quite random in non-random manner -- everytime I've run the app the data was the same. What was wrong? I have absolutely no idea. I've managed with this bug by using MapBuffer instead of SubBufferData and it worked like a charm. But at least I've learned about...

...Transform Feedback [spec]

Equivalent of DirectX's Stream Out. Basically this allows to stream some data from vertex shader into a buffer. So you can for example:

  • debug your vertex shader. You can hook between vertex and fragment shader and see what is VS' output
  • save vertex results for future usedata, for example to do skinning only once per frame (in case you have shadows/reflections etc)
  • do some GPGPU calculations without OpenCL -- this way you can store structures more easily than doing your job in fragment shader

You can also disable rasterization of generated vertices (pure streaming into buffer).

Timer Query [spec]

This is very useful for profiling your OpenGL application. Because GPU & CPU are doing their jobs asynchronically, something like this is bad:

var t0 = get_current_timestamp();
glRenderFancyThings();
var t1 = get_current_timestamp();
Log("Time elapsed: %f", t1 - t0);

Another bad example. This time we wait for GPU to complete, so the result makes no sense (compared to real world usage):

var t0 = get_current_timestamp();
glRenderFancyThings();
glFinish();
var t1 = get_current_timestamp();
Log("Time elapsed: %f", t1 - t0);

However we can use Timer Query to measure every GL command and grab result after few frames, when the commands have finished.

Bonus: Fermi GPU Architecture

NVIDIA_Fermi_Compute_Architecture_Whitepaper.pdf. Interesting, I wonder about its performance in OpenGL/DirectX.

OpenGL extensions on NVidia & ATI

16.01.2010 19:54 in OpenGL, extensions

I've made a simple diff of supported OpenGL extensions on NVidia & ATI cards. Lists were done in GPU caps viewer -- 3.2 compability (default) profile. Vendor-specific exts are marked with color background.

The good news: bindable uniforms.

The bad news: no direct state access on ATI. I hope this is going to be implemented soon!

Thx Krajek for ATI extensions. :)

See it here:

Read more...

gitcharts: LOC charts generator for git

13.01.2010 04:17 in git, tools

I've made small C# utility to generate LOC charts for git repository. It's quite fancy and the other reason was to learn using github. The code is definitely not the art of computer programming and to be honest quite slow (anyway, git on Windows is slow) but anyway MUCH faster than any other svnstat-like tools: it took 6 minutes to generate stats for 80KLOC with about 2000 commits. I don't remember how long did it take with svnstat but it was hours. And the project was much smaller that days.

gitcharts on github * direct download (C# solution) * example:

gitcharts

First screenshot of our FPS game

04.01.2010 02:14 in 3D graphics, apps, FPSGame

fps_small.jpg
click to visit gamedev.pl

Initially I didn't want to publish any screens from this FPS game, but sometimes you need a little motivation to keep things running. Especially when after few months the team still doesn't have game ready.

This is pretty old screen, beginning of October 2009. Its basically dynamic-only lighting (but no deferred lighting at that time) with SSAO. HUD was added as separate layer (we were testing a lot of different HUDs at that time). Map was created in Valve Hammer and finetuned in Blender.

Objective C 2.0 properties and storage keywords

11.12.2009 21:11 in ObjC

@interface Foo : NSObject
{
  NSString *_foo;
}
@property NSString *foo;
@end

@implementation Foo : NSObject
@dynamic foo;
- (void) setFoo:(NSString*)value
{
  [_foo release];
  _foo = value;
  [_foo retain];
  [self doSomethingFancyWithNewFoo];
}
- (NSString*) foo
{
       return _foo;
}
@end

This code is quite cool. However, a problem arises. Here we go.

warning: 'assign' attribute (default) not appropriate for non-gc object property 'foo'
warning: no 'assign', 'retain', or 'copy' attribute is specified - 'assign' is assumed

OK. We have 3 options:

  • @property (assign) NSString *foo
  • @property (retain) NSString *foo
  • @property (copy) NSString *foo

I assume you know what those values would mean in @synthesize. But do they have any meaning with @dynamic properties?

Answer is: no. They have no meaning at all. I changed NSString to my own class doing a lot of NSLogging and usage of assign/retain/copy is completely meaningless.

I suppose the same goes for (non)atomic keywords. So why do we have to write down those keywords anyway? I have no idea. Should it be a hint for class' user? But dynamic property doesn't need to store its data in variable. I'm aware that class' clients know nothing about implementation (is is synthesized or dynamic). But I think that there should be dynamic storage keyword. So that we could write:

@property (dynamic[, readwrite]) NSString *foo;

If you are Obj-C expert and know better explanation, let me know. :)

Going more Web 2.0

08.12.2009 01:19 in blog

As you see, I've integrated my blog with Twitter. It has a rather nice API (but SOAP is better anyway). As the current layout has no space for fancy stuff like twitter statuses, I've added a Dabroz Labs (rings a bell? *really*?) -- more extras on the way (one of those extras would be an option to disable them).

And if we are going to be more Web 2.0, what should be next? Profiles and photos? Direct messaging and a lot of colorful flash applications? Well, I'd rather steal everything from Facebook and call it... maybe Grono.net? Oh s**t, the name's already occupied. :(