id Software's Usenet Group Posts Archive!

id_notes/John C/1996-12-17



[idsoftware.com]


Login name: johnc In real life: John Carmack


Directory: /raid/nardo/johnc Shell: /bin/csh


Last login Tue Dec 17 01:05 on ttyp3 from idcarmack


Plan:



I am going to use this installment of my .plan file to get up on a


soapbox about an important issue to me: 3D API. I get asked for my


opinions about this often enough that it is time I just made a public


statement. So here it is, my current position as of december '96...



While the rest of Id works on Quake 2, most of my effort is now focused


on developing the next generation of game technology. This new


generation of technology will be used by Id and other companies all the


way through the year 2000, so there are some very important long term


decisions to be made.



There are two viable contenders for low level 3D programming on win32:


Direct-3D Immediate Mode, the new, designed for games API, and OpenGL,


the workstation graphics API originally developed by SGI. They are both


supported by microsoft, but D3D has been evangelized as the one true


solution for games.



I have been using OpenGL for about six months now, and I have been very


impressed by the design of the API, and especially it's ease of use. A


month ago, I ported quake to OpenGL. It was an extremely pleasant


experience. It didn't take long, the code was clean and simple, and it


gave me a great testbed to rapidly try out new research ideas.



I started porting glquake to Direct-3D IM with the intent of learning the


api and doing a fair comparison.



Well, I have learned enough about it. I'm not going to finish the port.


I have better things to do with my time.



I am hoping that the vendors shipping second generation cards in the


coming year can be convinced to support OpenGL. If this doesn't happen


early on and there are capable cards that glquake does not run on, then I


apologize, but I am taking a little stand in my little corner of the


world with the hope of having some small influence on things that are


going to effect us for many years to come.



Direct-3D IM is a horribly broken API. It inflicts great pain and


suffering on the programmers using it, without returning any significant


advantages. I don't think there is ANY market segment that D3D is


appropriate for, OpenGL seems to work just fine for everything from


quake to softimage. There is no good technical reason for the existance


of D3D.



I'm sure D3D will suck less with each forthcoming version, but this is an


oportunity to just bypass dragging the entire development community


through the messy evolution of an ill-birthed API.



Best case: Microsoft integrates OpenGL with direct-x (probably calling


it Direct-GL or something), ports D3D retained mode on top of GL, and


tells everyone to forget they every heard of D3D immediate mode.


Programmers have one good api, vendors have one driver to write, and the


world is a better place.




To elaborate a bit:



"OpenGL" is either OpenGL 1.1 or OpenGL 1.0 with the common extensions.


Raw OpenGL 1.0 has several holes in functionality.



"D3D" is Direct-3D Immediate Mode. D3D retained mode is a seperate


issue. Retained mode has very valid reasons for existance. It is a good


thing to have an api that lets you just load in model files and fly


around without sweating the polygon details. Retained mode is going to


be used by at least ten times as many programmers as immediate mode. On


the other hand, the world class applications that really step to new


levels are going to be done in an immediate mode graphics API. D3D-RM


doesn't even really have to be tied to D3D-IM. It could be implemented


to emit OpenGL code instead.



I don't particularly care about the software only implementations of


either D3D or OpenGL. I haven't done serious research here, but I think


D3D has a real edge, because it was originally designed for software


rendering and much optimization effort has been focused there. COSMO GL


is attempting to compete there, but I feel the effort is misguided.


Software rasterizers will still exist to support the lowest common


denominator, but soon all game development will be targeted at hardware


rasterization, so that's where effort should be focused.



The primary importance of a 3D API to game developers is as an interface


to the wide variety of 3D hardware that is emerging. If there was one


compatable line of hardware that did what we wanted and covered 90+


percent of the target market, I wouldn't even want a 3D API for


production use, I would be writing straight to the metal, just like I


always have with pure software schemes. I would still want a 3D API for


research and tool development, but it wouldn't matter if it wasn't a


mainstream solution.



Because I am expecting the 3D accelerator market to be fairly fragmented


for the forseeable future, I need an API to write to, with individual


drivers for each brand of hardware. OpenGL has been maturing in the


workstation market for many years now, always with a hardware focus. We


have existing proof that it scales just great from a $300 permedia card


all the way to a $250,000 loaded infinite reality system.



All of the game oriented PC 3D hardware basically came into existance in


the last year. Because of the frantic nature of the PC world, we may be


getting stuck with a first guess API and driver model which isn't all


that good.



The things that matter with an API are: functionality, performance,


driver coverage, and ease of use.



Both APIs cover the important functionality. There shouldn't be any real


argument about that. GL supports some additional esoteric features that


I am unlikely to use (or are unlikely to be supported by hardware -- same


effect). D3D actually has a couple nice features that I would like to


see moved to GL (specular blend at each vertex, color key transparancy,


and no clipping hints), which brings up the extensions issue. GL can be


extended by the driver, but because D3D imposes a layer between the


driver and the API, microsoft is the only one that can extend D3D.



My conclusion about performance is that there is not going to be any


significant performance difference (< 10%) between properly written


OpenGL and D3D drivers for several years at least. There are some


arguments that gl will scale better to very high end hardware because it


doesn't need to build any intermediate structures, but you could use tiny


sub cache sized execute buffers in d3d and achieve reasonably similar


results (or build complex hardware just to suit D3D -- ack!). There are


also arguments from the other side that the vertex pools in d3d will save


work on geometry bound applications, but you can do the same thing with


vertex arrays in GL.



Currently, there are more drivers avaialble for D3D than OpenGL on the


consumer level boards. I hope we can change this. A serious problem is


that there are no D3D conformance tests, and the documentation is very


poor, so the existing drivers aren't exactly uniform in their


functionality. OpenGL has an established set of conformance tests, so


there is no argument about exactly how things are supposed to work.


OpenGL offers two levels of drivers that can be written: mini client


drivers and installable client drivers. A MCD is a simple, robust


exporting of hardware rasterization capabilities. An ICD is basically a


full replacement for the API that lets hardware accelerate or extend any


piece of GL without any overhead.



The overriding reason why GL is so much better than D3D has to do with


ease of use. GL is easy to use and fun to experiment with. D3D is not


(ahem). You can make sample GL programs with a single page of code. I


think D3D has managed to make the worst possible interface choice at


every oportunity. COM. Expandable structs passed to functions. Execute


buffers. Some of these choices were made so that the API would be able


to gracefully expand in the future, but who cares about having an API


that can grow if you have forced it to be painful to use now and forever


after? Many things that are a single line of GL code require half a page


of D3D code to allocate a structure, set a size, fill something in, call


a COM routine, then extract the result.



Ease of use is damn important. If you can program something in half the


time, you can ship earlier or explore more approaches. A clean, readable


coding interface also makes it easier to find / prevent bugs.



GL's interface is procedural: You perform operations by calling gl


functions to pass vertex data and specify primitives.



glBegin (GL_TRIANGLES);


glVertex (0,0,0);


glVertex (1,1,0);


glVertex (2,0,0);


glEnd ();



D3D's interface is by execute buffers: You build a structure containing


vertex data and commands, and pass the entire thing with a single call.


On the surface, this appears to be an efficiency improvement for D3D,


because it gets rid of a lot of procedure call overhead. In reality, it


is a gigantic pain-in-the-ass.



(psuedo code, and incomplete)


v = &buffer.vertexes[0];


v->x = 0; v->y = 0; v->z = 0;


v++;


v->x = 1; v->y = 1; v->z = 0;


v++;


v->x = 2; v->y = 0; v->z = 0;


c = &buffer.commands;


c->operation = DRAW_TRIANGLE;


c->vertexes[0] = 0;


c->vertexes[1] = 1;


c->vertexes[2] = 2;


IssueExecuteBuffer (buffer);



If I included the complete code to actually lock, build, and issue an


execute buffer here, you would think I was choosing some pathologically


slanted case to make D3D look bad.



You wouldn't actually make an execute buffer with a single triangle in


it, or your performance would be dreadful. The idea is to build up a


large batch of commands so that you pass lots of work to D3D with a


single procedure call.



A problem with that is that the optimal definition of "large" and "lots"


varies depending on what hardware you are using, but instead of leaving


that up to the driver, the application programmer has to know what is


best for every hardware situation.



You can cover some of the messy work with macros, but that brings its own


set of problems. The only way I can see to make D3D generally usable is


to create your own procedural interface that buffers commands up into one


or more execute buffers and flushes when needed. But why bother, when


there is this other nifty procedural API already there...



With OpenGL, you can get something working with simple, straightforward


code, then if it is warranted, you can convert to display lists or vertex


arrays for max performance (although the difference usually isn't that


large). This is the right way of doing things -- like converting your


crucial functions to assembly language after doing all your development


in C.



With D3D, you have to do everything the painful way from the beginning.


Like writing a complete program in assembly language, taking many times


longer, missing chances for algorithmic improvements, etc. And then


finding out it doesn't even go faster.



I am going to be programming with a 3D API every day for many years to


come. I want something that helps me, rather than gets in my way.



John Carmack


Id Software