Thursday, March 10, 2011

DECAFBAD

Makes you write code like this...
if ((ent = dynamic_cast(what)) && ent->GetGISClass() == gis_Composite) true;
At least C++ isn't judgmental.

Monday, March 07, 2011

Instancing Numbers


A quick stat on instancing performance. There are a lot of OpenGL posts with developers posting their instancing performance numbers, and others asking, so here's X-Plane.

On a 2.8 ghz Mac Pro (a few years old) with an ATI 4870 and OS X 10.6.6, we can push 87,000 meshes at just under 60 fps using instancing. The average instance call is pushing 32 instances per draw call.

Don't Go Anywhere!

I'm debugging X-Plane's autogen engine. In debug mode, with no inlining, optimizations, and a pile of safety checks, the autogen engine is not very fast. Fortunately, my main development machine has 8 cores, and the autogen engine is completely thread-crazy. The work gets spooled out to a worker pool and goes...well, about 8 times as fast.

All is good and I'm sipping my coffee when I hit a break-point. Hrm...looks like we have a NaN. Well, we divided by a sum of some elements of a vector. What's in the vector?
print ag_block.spellings_s.[0].widths[1]
Ah...8 tiles. At this point I am already dead. If you've debugged threaded apps you already know what went wrong:
  • The array access operator in vector is really a function call (particularly in debug mode - we jam bounds checks in there).
  • GDB has to let the application 'run' to run the array operator, and at that instant, the sim's thread can switch.
  • The new thread will run until it hits some kind of break-point.
  • If you have 8 threads running the same operation, you will hit the break point you expect...but from the wrong thread.
To say this makes debugging a bit confusing is an understatement.

A brute force solution is to turn off threading - in X-Plane you can simply tell the sim that your machine has one core using the command line. But that means slow load times.

Fortunately gdb has these clever commands:
set scheduler-locking on
set scheduler-locking off
When you set scheduler locking on, the thread scheduler can't jump threads. This is handy before an extended inspection session with STL classes. You can apparently put the scheduler into 'step' mode, which will switch on run but not on step, but I haven't needed that yet.

Sunday, March 06, 2011

CSM for Dummies

This quote from NVidia's GPU Programming Guide amused me:
There are many techniques available. However, the general recommendation is
that unless you know what you are doing you should just implement simple
multi-tap cascaded shadow maps.
Or put another way:
If you have no idea what the hell you're doing, try cascaded shadow maps -- what could go wrong?
Oh wait, X-Plane 10 uses CSM. Well, I guess that's for the best...

(The guide also suggests that "3 levels are sufficient to provide good shadow detail for any scene." Have they seen our scene graph?)

Monday, February 28, 2011

Order-Correct Translucency

When ATI released their order independent transparency demo, I nearly wet myself. Translucency has been the bane of X-Plane authors for years. The problem is that translucent surfaces remove hidden surfaces behind them, leading to artifacts. The thought of on-hardware OIT was tantalizing.

That is, until I found out how the tech works. My understanding is that OIT is implemented by "writing your own back-end" - that is, instead of shading into a framebuffer, you write fragments into a 'deep' framebuffer by hand, using compute-shader-style ops to create linked lists of fragments. (That is, fragments live in a general store and the framebuffer is really list heads.) In a post processing pass, you go through the 'buckets' (that is, the linked lists) and sort out what you drew.

That's a lot more back-end than I wanted...as a [spoiled, lazy] app developer I was hoping for glEnable(GL_MAGIC_OIT_EXT) - but no such luck. The real issue is that, since our product already does a lot of 'back end' tricks within OpenGL, the cost of getting our shaders to run in a compute-style environment might be a bit high. (This is looking less burdensome with some of the newer extensions, but it still seems to me that it would be difficult to port legacy apps to OIT-style rendering without having compute-shader features like atomic counters inside the GLSL shading environment.)

As a side note, I also looked closely at depth peeling and even hacking the blend equation (e.g. accumulate and average) and both would probably be producable for X-Plane, which tends not to have that much translucent overlap - the most common case for us is windows.

The Traditional Approach - Automated

Now the traditional approach to translucency in X-Plane goes something like this:
  • Force opaque drawing first.
  • Use one-sided drawing and order the translucent polygons so they appear from back to front from any viewpoint.
That second point is key: consider an airplane with windows. If we draw the interior facing windows first and the exterior facing windows second, then from any viewpoint, we are drawing 'back to front'. This works because whenever we see two windows at once, we are seeing the inside window behind the outside one. Isn't topology grand?

Well, it turns out that this approach can be generalized: as long as none of our triangles intersect (except at their edges and corners), given any two triangles, we can always find a draw order between them that is correct. Given a set of triangles, we can always sort the whole mesh to be appropriately back-to-front. (At least, that's my theory until someone proves me wrong.)

There are basically three cases:
  1. Triangle B is fully on one side or the other of triangle A's plane. B should be clearly before or after A depending on which side it's on.
  2. Triangle A is fully on one side or the other of triangle B's plane. A should be clearly before or after B depending on which side it's on.
  3. Triangle B and A are both on one side of each other's plane; we can use either triangle to determine correct order - they will not conflict. (That is, this is a disjoint case, and either the two triangles are going to give you the same answer or they're facing in opposite directions and thus no visible at the same time.)
The fourth case would be two intersecting triangles - that's the case we can't necessarily get right.

The ability to find this sort order depends on using one-sided triangles - this is what lets us decouple the sort order for two opposite directions. By definition if a triangle is visible to a vector V, its back side is visible to -V.

This approach of course doesn't solve all problems:
  • Animation can deform the mesh in a way that violates our correct order.
  • Multiple unrelated objects still need a relative ordering that makes sense.
Theoretical Angst

Just a touch of angst...I'm no theoretician, and I can't help but wonder if there is a screwy case that this doesn't handle. In particular, the sort order needs to be a strict weak ordering or we're going to get goofy results, and I'm not entirely sure that it is.

Saturday, February 19, 2011

Mmm....C0FFEE.

I must just be late to the party, but: I just realized (approximately a decade later than I should) that C0FFEE can be spelled in hex. How have I never seen a code base use this as a 'token word' (albeit with some high-bit junk)? Actually you'd want to pad the low bits to make it odd too.

The most common cookies I've seen in production code are 0BADF00D, DEADBEEF, and FEEDFACE.

Tuesday, February 15, 2011

Permalink Shortcode

This must exist somewhere in WordPress or as part of a plugin, so if you know what plugin I should have used, feel free to heap on the abuse in the comments section. Anyway...

I wanted a way to create a link to a WP page from inside a post or page that wouldn't need modification when the target page's parent was changed. WordPress's permalink scheme uses a hierarchy of parent/child/grandchild/ to identify pages, and this can change as a page is reparented. If the parenting scheme is meant to represent a navigational hieararchy, you could have dead links.

I ended up with this function, loosely based on snippets I found on the web:
function permalink_func( $atts, $content=null ) {
extract( shortcode_atts( array(
'p' => '1',
), $atts ) );
if ($content == null)
$content = get_the_title($p);
$link = get_permalink($p);

return "$content";
}
add_shortcode( 'prm', 'permalink_func' );

(It lives in functions.php inside a php block.)

The short code is used like this: [prm p=1] or [prm p=6]link title[/prm]. If the short code is used with no closing tag, the article's title is used to label the link. The parameter (p=17) is the ID of the page, and can be seen by mousing over the page or post in the admin interface. The URL generated by the shortcode matches the current permalink scheme.

Once again, I am amazed by how easy it is to get things done with WordPress. It doesn't seem right...