Shawn Hargreaves Blog
There are many reasons for differences between platforms, and several ways an API designer can try to rationalize such things.
So, there will always be some differences across platforms. What should we do about them?
XNA Game Studio currently uses a mishmash of all five techniques, chosen for individual features on an ad-hoc basis. This is an area I think we have much room to improve.
The ultimate goal can be summed up in two words: "avoid surprises".
A surprise occurs when a friend tries to play your game, but it renders incorrectly because his GPU does not support TextureAddressMode.Border, and you didn't know you were supposed to check the caps for that. Or when you try to port your game from Windows to Zune, only to realize you must rewrite your sound code because Zune does not support XACT.
The XNA Framework provides a set of features that are available and work the same way on all platforms, surrounded by a smaller set of features that are not so consistent. Trouble is, there is no good way to tell which features are which! It is too easy to accidentally stumble outside the consistent zone.
Let's look again at my list of five techniques, this time with the goal of avoiding surprise:
Verdict: #1 and #5 are appropriate for some APIs. #2 is good at a coarse level, but should not be too fine grained. #3 and #4 should be avoided.
Can you tell us why "the powers that be didn't want [to] [allow full LIVE support for Windows games]"? Or at least your understanding?
Erzengel: I'm guessing probably a fair bit of politics behind something like that. Just think about this: if the whole Live platform was free to develop for and run on, what would prevent developers from making games and sucking the Live bandwidth dry, while Microsoft foots the bill for everything? At least with Xbox Live, Gold members pay a fee to use the service, which I imagine is used to maintain all the servers that everything is running on. That, and what would prevent people from reverse engineering the Live components, especially if they have access to the API, more information, and the distributable DLLs? I'm guessing cheating would definitely become a bigger issue for Live enabled PC games.
That's just a guess, but that could be part of it.
How does your verdict fit with an example like TextureAddressMode.Border?
One thing to note is that "Game for Windows" and "Game for Windows LIVE" are not just a brand name that takes lots of place on the cover of your game's box.
They are APIs, a good run of QA testing and more importantly TCRs (A TCR (Technical Certification Requirement) is a test case that must pass before software can be certified as compliant to a given standard).
In XNA they are only good practices, with the GRWL stamp you MUST implement some things in a specific way if you ever want your game being released.
And here I was rooting for cap bits for being the least surprising.
While they certainly require more work on the client programmer's end--they ought to query anything they want to do, and come up with an alternative (or let the user know why they're failing--e.g. they're on 1995 hardware) if it's not supported. You could even throw an exception if they call something unsupported.
At some point, you'll have to rely on the programmer to do a little bit of thinking on their own.
Cap bits don't really become a surprise if it's the standard procedure for most of the API.
I can see where they would fail, though: In the worst case, cap bits could be essentially useless--if they're going to have to roll their own Audio system because they're doing cross-3-platform audio, then it might be easier to just ditch XACT entirely and use the same custom code on all three devices.
On the other hand, cap bits make the most sense in an environment like the GPU department where you can ask things like "do you support x in hardware?" and if so taking that path, otherwise doing it in code.
Are you seriously implying that anyone cares about avatars on Xbox? :)
> And here I was rooting for cap bits for being the least surprising.
Caps bits suffer from what Raymond Chen likes to call the "what if everybody did that" problem.
For any single feature, sure, attaching a boolean to it is simple and not too confusing.
But what happens when you have 100 different features, all with their associated boolean?
That gives more possible feature permutations than a 32 bit integer can evaluate without overflowing :-)
I think the resulting minefield is a poor developer experience. When you can't do anything without checking caps first, that makes it hard to write clear, maintainable code! And it's even harder to properly test code that contains so many conditional branches and optional execution paths.
In practice, I've never seen anyone properly check all of the possible DX9 caps. Most developers choose one of these simplification strategies:
- Some people just code for whatever happens to work on their development PC, and are surprised when the resulting game doesn't run on other hardware.
- Some people code for the lowest common denominator hardware, carefully avoiding any optional features. Trouble is, pretty much everything in DX9 is technically optional!
- Some people pick just two or three "feature levels" that their game will adjust to: the fancy version with all the special effects, the more limited version where they disable some of the fancier stuff, plus maybe a super stripped down version where they just render basic textured objects with no effects at all. They check caps at startup to decide which of these codepaths best fits the current hardware, then just use the selected rendering logic. This way they have just a handful of codepaths to maintain and test, as opposed to billions of possible overlapping permutations.
Shawn: That's one thing that amazed me about the Halo port for PC - it'd run on some amazingly low hardware (like Intel integrated graphics). I was impressed that they even went that low with things. It ran like a dog, but it did run.
Oh, the amazing things you find out when bored in a college class and the person before you had installed Halo on the machine...
When you enumerated that list of possibilities, it gave me another possible idea:
Instead of checking cap-bits, why not *request* certain capabilities.
This removes the possibility that you use a function without checking for capability (and end up with an exception or a mess when you port it). It also means the runtime could report what you're using ("XNA: this won't run on Zune").
That lack of the border caps bit being set on old ATI cards is not because the cards can't do border addressing at all.
I'm not sure if XNA will let you do this, but with D3D9 on ATI cards that don't have the cap bit set, if you ignore that and set the render state anyway it will usually work correctly.
That doesn't work with the debug runtimes enabled, because they stop the call getting through to the driver.
According to the discussion linked below the cap bit is disabled because there are some edge cases where border doesn't work according to spec, so the driver would fail WHQL testing if the cap bit was set.
> Instead of checking cap-bits, why not *request* certain capabilities.
That makes the opt-in more explicit, but it doesn't really fix the underlying problem if there are so many optional features that it isn't clear how to navigate your algorithms through the resulting maze. What do you do when you request 50 optional features, but only 20 of them come back as supported? How do you test all the crazy number of possible permutations that can result?