Jump to content

  • Log In with Google      Sign In   
  • Create Account


Member Since 26 May 2004
Offline Last Active Aug 25 2014 09:18 AM

Topics I've Started

testing my game on really old hardware (mostly for fun)

18 March 2014 - 05:58 PM

I have a simple portal/sector rendered FPS maze that's around 'DOOM' or Quake 1 geometric complexity.  Of course, I'm using OpenGL and VBOs.  Shaders are optional in my engine.  (Fallback to fixed-function vertex lighting when needed/specified)


I also have an old PC with a k6-3 processor and a Voodoo 3 graphics card.  It dual boots windows 98 (for those 3dfx glide games I don't want to emulate) and lubuntu 10.04.


I decided to compile it on the voodoo machine for no reason other than curiousity.  I figured that glquake runs really good on this machine, so a homemade game that looks worse than gl DOOM should run good too.


After a little source tweaking, it built.  Run... segfault.  Fired up gdb:  glCreateBuffer (and glCreateBufferARB) are missing... Right... voodoo 3.  Ok, old-style vertex arrays look just like VBOs, just glVertexPointer is a real pointer instead of a screwy offset.  Very minor tweak to make this work.  (All my VBO stuff is abstracted, so I literally change it in one place...)


Next crash:  glActiveTexture was NULL.  What?  Oh, don't panic.  My card only supports glActiveTextureARB.  Ok, simple change, use ARB when core isn't available.  (I already do the same on my netbook; for whatever reason the Atom I have supports glCreateBufferARB but not glCreateBuffer...)


A little bit of tweaking and... well its a slideshow.


Turn on some optimizations.  (On my desktop PC I get away with "-g" because I'm not close to CPU bound.  Turn on -O3 and --fastmath) and wow, like 15-20 fps.


Next up:  I know I need to put in state sorting.  Instead of rendering, I'll put items into a queue, then sort by texture, then render.  In fact, right now even if I draw two things that are the same texture right after each other, I'll still re-bind the second item even though I'm not changing anything.  Bad, bad.


Why am I doing this?  Mostly fun.  But its also useful.  If a vertex-lit, FPS looks like DOOM, it better run at the speed of gl DOOM on a computer that can play gl DOOM, or I've done something horribly wrong.


That, and I'm exploring the possibility of a gracefully degrading engine.  When I run by terrain test app on my desktop, the triangles subdivide tiny to just being a few pixels wide.  Lots of procedural detail.  When I run it on my netbook, I divide even less.  You can see the polygons, but the core flying mechanic is the same.  I run it on voodoo.  The polygons are huge, but its still the same rough terrain and same core mechanics (though its much slower than I want; 5-10 fps for terrain, I'll get the numbers up there.)


Anyway its a lot of fun.  Oh, and this is in the lounge, because, well, who in the opengl forum would care about making it work on a voodoo card!

cryptic gcc warning: function ponter typedef w/ struct pointer argument

06 March 2014 - 09:59 PM

I have a struct with a function pointer in it.  This compiles fine:

typedef struct gx_quadpatch_s
    gx_vbuffer_t* vb;
    struct gx_quadpatch_s* children[4];
    struct gx_quadpatch_s* parent;


    void (*detailer)(struct gx_quadpatch_s* dest, struct gx_quadpatch_s* source, int a_start, int a_end, int b_start, int b_end);

} gx_quadpatch_t ;

I wanted a pretty typedef for the function type:


typedef     void (*gx_quadpatch_detailer_f)(     struct gx_quadpatch_s* dest, struct gx_quadpatch_s* source, int a_start, int a_end, int b_start, int b_end);

typedef struct gx_quadpatch_s
    gx_vbuffer_t* vb;
    struct gx_quadpatch_s* children[4];
    struct gx_quadpatch_s* parent;


    gx_quadpatch_detailer_f detailer;

} gx_quadpatch_t ;

GCC gives this warning:

In file included from gx_quadpatch.c:18:0:
../graphics/gx_quadpatch.h:5:80: warning: 'struct gx_quadpatch_s' declared inside parameter list [enabled by default]
 typedef  void (*gx_quadpatch_detailer_f)(  struct gx_quadpatch_s* dest, struct gx_quadpatch_s* source, int a_start, int a_end, int b_start, int b_end);
../graphics/gx_quadpatch.h:5:80: warning: its scope is only this definition or declaration, which is probably not what you want [enabled by default]

This suggests I need to declare the struct quadpatch_s definition before the gx_quadpatch_detailer_f.  Circular dependency.  Not sure how to break it.


Usually if I have an interdependency between structs, I can do this:

typedef struct foo_s
    struct bar_s* my_bar;

} foo_t;

typedef struct bar_s
   struct foo_s* my_foo;
} bar_t;

After that, I can refer them at foo_t and bar_t.  In its allowed for a struct to defined as containing a pointer to another struct that hasn't yet because all struct pointers are the same size.  Can I not do the same thing with function argument lists?


Is there something I can put above the gx_quadpatch_detailer_f typedef to resolve this? 

stack overflows: why not turn the stack upside down?

13 February 2014 - 05:41 PM

We all know about stack overflows.  You overwrite your buffer on the stack, and you are now trashing another buffer, or even worse, the return address.  Then when 'ret' (I'm using x86 lingo here), runs, the cpu jumps to a bad place.  This is because on most platforms the stack grows downward.  When you exceed your stack frame's bouundary, you start tracking the previous stack frame.


This downward growth is mostly hisorical.  On older computers, before virtual memory, your stack started at the top of RAM, and grew downwards.  The heap started somewhere else and grew up.  You ran out of memory when the two pointer met.  With virtual memory, you can put the stack 'anywhere' and even grow it on a page fault in some cases.  And I think it's time to turn the stack around:



Suppose f() calls g() and that calls h():

classic stack:  'R' is where the return address goes:
low addr  [empty-----------------R][--f--]   high addr
low addr  [empty------------[--g-R][--f--]   high addr
low addr  [empty-----[--h-R][--g-R][--f--]   high addr

How H overflows:

low addr  [empty-----[--h--*R*****][--f--]   high addr

we crash, or worse, run an explot from h inserting an return address to who-knows-where

upside-down stack:
low addr  [--f--][-empty------------------]   high addr
low addr  [--f--][R-g--][empty-----------]   high addr
low addr  [--f--][R-g--][R-h--][empty----]   high addr

Now H overflows:
low addr  [--f--][R-g--][R-h***********--]

We've overflowed into empty space.  Return address and previous stack frames are safe

This wouldn't be too hard to do.  Most stack access during a function is done pointer arithmetic:

mov eax, [esp-4]

mov eax, [esp+4]


I know there are some hardware platforms that already have stacks that grow upwards.  Redefining the ABI for x86 would break using standard libraries, but for inside your own application, this might enhance security.  I suppose its even possible to use an 'upwards' stack for your application, and then when you call a 3rd party library, switch the stack pointer to a seperate area where you have a standard downwards stack defined.   I imagine this would involve a lot of hacking around in gcc or llvm to make it work.  In an open OS like linux, maybe you could recompile the whole system to use upwards stacks. 


Just a thought.  Downvote if it stinks! 








is pascal a dead language?

04 February 2014 - 04:28 PM

I'm posting this in the lounge instead of programming because this is likely to be very subjective.


I don't remember exactly how, but a couple of weeks ago I stumbled upon something written in pascal, and before I knew it I was downloading freepascal and playing around with it.  I guess I'm coming into it to late; I never played with pascal when it was popular.  I went from qbasic straight to C, and then java, and various other 'curly brace' languages.


Does anyone use freepascal or delphi?   Breaking away from curly-brace land, and finding myself in begin/end reminds me a lot of my qbasic days (if/end if, etc), but it's new, like when I was learning programming back then, but I also get advanced things I didn't use back then, like pointers, objects, etc.  It's a nice combo of new and nostalgia.


Playing around with this was a fun diversion, but I'm starting to think that attempting a larger project in it would be worthwhile. 


  • has anyone written a game in pascal and had a great time?
  • has anyone written a game in pascal and had a nightmare getting it finished?
  • would the ratio of good times/bad times be any different in C++ than in pascal?


C99: strict aliasing rule vs compatible pointers

06 April 2013 - 02:28 PM

I'm cleaning up some of my code by compiling with gcc -Wall and -Wstrict-aliasing=1 to look for unclean things I've done.



This is a fairly common pattern in my C code:


typedef struct generic_list_s
	int type;
	struct generic_list_s* next;
} generic_list_t;

typedef struct
	generic_list_t gen;
	char* str;
} string_t;

typedef struct 
	generic_list_t gen;
	int x;
} int_t;


The general idea is I have some generic structure that I reuse as the 1st element of larger, more specific structures.  The C standard allows you to cast a pointer back and forth between a struct, and it's 1st member.  A pointer to an int_t or a string_t in the above examples is also a pointer to a generic_list_t.


GCC however likes to complain.  If I compile a snippet like this:

	string_t* node0;
	int_t* node1;
	string_t* node2;
	node0 = mk_string("Hello");
	node1 = mk_int(123);
	node2 = mk_string("World");

	/* Below seems correct
	  node1 is a pointer to an int_t
	  int_t starts with generic_list_t, so
	  a pointer to int_t is also a pointer to int_t
	  ( says )
	node0->gen.next = (generic_list_t*) node1;
	node1->gen.next = (generic_list_t*) node2;

with -Wstring-aliasing =1, I get this warning:


warning: dereferencing type-punned pointer might break strict-aliasing rules


This makes sense, in that node1 is a pointer to an int_t, and node0->gen.next is technically a pointer to a generic_list_t.  I have two different types of pointers pointing to the same thing.  I can make it go away by doing this:


node0->gen.next = &(node1->gen);
node1->gen.next = &(node2->gen);


Which is more correct, although a pointer to the first element of a struct or a pointer to the struct are supposed to be compatible with each other.


The real problem comes with functions like:


void print_thing(generic_list_t* n)
	if (n->type == TYPE_STRING)
		string_t* s = (string_t*) n;/* warning: dereferencing type-punned pointer might break strict-aliasing rules*/
		printf("%s\n", s->str);

	if (n->type == TYPE_INT)
		int_t* i = (int_t*) n;  /* warning: dereferencing type-punned pointer might break strict-aliasing rules*/
		printf("%d\n", i->x);


 Hoe do I clean up these warnings?