To test or not to test (for graphics modes)?

BlitzMax Forums/BlitzMax Programming/To test or not to test (for graphics modes)?

Grey Alien(Posted 2006) [#1]
Hiya, my recent BlitzMax GameFramework uses GraphicsModeExists to check to see if 800x600x32-bitx60Hz exists and if not it tries 16-bit, and failing that does windowed mode. I thought this was neat until Big10p said that it went straight to windowed mode on his, even though his card can handle 800x600x32bitx60Hz. When I sent him a test app that showed all available video modes, it turned out that his card returned a Hz parameter of 0 for all modes!

So anyway, the idea behind my code was that the game would auto-switch to a suitable mode depending on what was available, but if it excludes some poeple just because their card drivers don't report properly, the auto-switching method might be a bit crap.

Anyone have any opinions on this. I'm not into showing the user a dialog of modes btw as this is for a non-tech savvy casual game audience.

My current code is this:

		If FullScreen Then
			'Try 32 bit, failing that try 16 bit, failing that use windowed mode
			If GraphicsModeExists(ScreenWidth, ScreenHeight,32,Hertz)
				Graphics (ScreenWidth, ScreenHeight,32,Hertz)
			ElseIf GraphicsModeExists(ScreenWidth, ScreenHeight,16,Hertz)
				Graphics (ScreenWidth,ScreenHeight,16,Hertz)	
			Else
				ForceWindowedMode = 1
				FullScreen = 0
				'Code will continue after EndIf and call Graphics in windowed mode
			EndIf
		EndIf
		'No else on purpose
		If FullScreen = 0 Then 
			'Create Windowed Mode at same Hz as Desktop to avoid vertical tearing
			If Not Graphics (ScreenWidth,ScreenHeight,0,DesktopRefreshRate)
                            Then RuntimeError "No 3D Hardware Found"
                        EndIf
                EndIf


I guess I could just try to set the mode instead of testing for it, and check the result of Graphics, but come to think of it, I tried this before with some silly test sizes like 801x601 and it does actaully make a graphics window that you can't see instead of returning an error, which is rubbish.

Also, btw, I tried the above code and on one PC with a crap video card it just bombed out, it didn't come up with the runtime error. Perhaps this is because the card or OS still needs to be DirectX7 and 3D to even show an error? I would have thought this unlikely. Does anyone have any non-compatible hardware they can test it on? Thanks...


Damien Sturdy(Posted 2006) [#2]
You'd need to test for DX and then for the graphics mode.

For the problem above, why not just check for a mode with 0 hz, 60hz, 75hz,80hz... That should get it going!


TartanTangerine (was Indiepath)(Posted 2006) [#3]
Personally I am not quite sure why you are even testing for availability. If you are that concerned about a GFX card not supporting 800x600x32 then you really need to be sticking with BlitzPlus since 3D hardware support is where the non compatibility lies not bit depth support.

Apologies if that sounds a bit negative but 3D support is really something you should be more concerned with.


Grey Alien(Posted 2006) [#4]
Cyg: How do I test for the correct level of DirectX? any ideas?

Indiepath: Well how do I test for 3D support then and exit cleanly? That's was the thing the last bit of my code above was testing for anyway (but I'm not sure it works). I don't want to exclude any players and if a bit of code can choose a screen mode that works for them, this may mean more customers, which is never to be sniffed at.

Also, it may still be worth testing for 32-bit because my old Voodoo 2 and 3 cards were *only* 16-bit, yet I'm sure they should've been able to play a BlitzMax DirectX7 game.


TartanTangerine (was Indiepath)(Posted 2006) [#5]
Well you could bomb to software emulation, it's damn slow but at least you'd get a chance to show a few screens and warn them what is going on.

I've posted how to do it some months back, no idea where the post it but it shows how to iterate through all the modes.


Grey Alien(Posted 2006) [#6]
Is this the thread you meant? http://www.blitzbasic.com/Community/posts.php?topic=51795#578201


TartanTangerine (was Indiepath)(Posted 2006) [#7]
Yeah that;s the baby.


ImaginaryHuman(Posted 2006) [#8]
Hey, I find it funny that you have a ForcedWindowMode variable, as I have the exact same variable in my app as part of checking for what mode to use.

Anyway... I am right on the same page as you as I am trying to deal with this scenario right now, plus a few extra problems.

In my game I am going to have a menu screen within the game that lets you choose a resolution. It's there if people want to use it but obviously if you aren't interested there's no need to touch it.

Meanwhile, and the first time the game is run, and without the presence of an existing prefs file, it will start by defaulting to 1024x768 32-bit as the resolution it desires. This is my assumption of what would be a good res to play the game in on most systems. It then goes about trying to find a resolution that is close or matches. When I say close, I mean that I measure the distance between the spec I am seeking and the ones I find and use the one that is nearest.

I first generate an array of GFXMode types which contains five fields - Width,Height,Depth,Hertz,Availability. I read all graphics modes and put the data in the array.

Now, I have certain priorities - at the moment 16-bit mode is way down on the list because it doesn't support stencils which I was using (but I may change this). With that in mind, I now sort the array on each field in turn, in the order of what's important to me. I use a bubble sort because I know the array isn't going to be that big or take much time. I sort by Width. Then by Height. Then by Hertz. Then by Depth. This is to ensure I have a consistent list because different platforms give you the modes in different orders. At the end I have a sorted array with the largest-resolution modes at the top. Where there is the same res more than once, you then see it sorted by hertz, and then by depth.

And yes, you get 0 hertz sometimes. On both my lcd-screen platforms (macs) I get 0 Hertz for every mode. I assume these to be equivalent of `a good mode` at around 60Hz. Obviously if I select such a mode and don't know the hertz rate then I am only assuming that it's 60Hz and it may not be, so I now have the situation of a software refresh rate which might not match the video hertz. *problem*

At that point, I then go through 8 combinations of searches in more or less a `binary` order. In binary, 001 represents hertz, 010 represents resolution, and 100 represents color depth. This just happens to reflect my priorities, which are that 16-bit is not a good thing since it doesn't support stencilling. In each search I go through the array of modes from top to bottom starting with the highest mode first - because I'd prefer to go with a higher mode than a lower one if they are both the same difference/distance of resolution. For the resolution search, I prioritize finding a Width over a Height. If two resolutions have the same width I go with the closest matching height. So I sort of do two searches in one, for resolution. Overall there are 8 searches, trying to find a close matching mode. In each search, based on the binary on-off state of that search, I allow certain parameters to be either fixed or flexible. So 011 might mean the resolution has to match and the hertz has to match but the color depth can be different.

If that fails, I search from top to bottom of the array for a 32-bit mode in the highest resolution possible. If that fails, I search from top to bottom of the array for a 16-bit mode (but maybe I should ignore that, not sure - I am changing my mind about whether I need stencil buffers). If that fails, it will open in a desktop window.

Now, I have another issue, that on my ibook it doesn't have enough video mem to support Quartz Extreme, requiring 16mb, and since it doesn't support that, it doesn't support OpenGL in fullscreen mode. If I try to open fullscreen it crashes the application. It is an untrappable unhandled exception. I also can't prevent this, by not opening the screen when it's not supported, because there is no known way of finding out if full-screen opengl is supported. So, I've implemented a file-detecting system, where when the game starts it looks file a `failure` file. If it exists, it gives the option to open in windowed mode. If the file doesn't exist, the previous execution of the game successfully opened a display, so it goes with the same mode from the prefs file. If the prefs file doesn't exist it goes through all the steps above to find a suitable mode to try. Then, upon the game re-running, it writes the `failure` file again, and tries to open a display. If the display opens, it deletes the failure file, so that next time it knows it doesn't have to force window mode.

So the first time I run the game, it will try to find a mode close to my default and open it. If it fails, the next time the game runs it will open in a window. This is the only way I've found for dealing with the possibility that full-screen in any resolution/depth/hertz may not be supported.

I treat 0 hertz modes as equivalent of a good hertz rate, but a last resort after finding some other hertz-rate mode. Of course that means you don't know the hertz, so then I have a difference between the software's assumption of 60hz and the actual possible display refresh rate. It would be good to find the actual refresh rate of the screen - I guess I can use GraphicsHertz() or whatever it is, after the display is open. Then adjust my variable to that rate.

I also in my game have the option of opening in a window if the user wants to, or doing fullscreen, or choosing a mode within the game, or guessing at a mode based on the defaults. I probably will do away with needing stencil buffers and prioritize 16-bit over window mode like you have - I have an idea for a different way of doing the same effect that I needed stencils for.

I don't know if this helps or gives you some ideas. I have generally found that if the graphics fails to open, you're out of luck, so get things as sure as possible before you try it. All this isn't foolproof and there's probably some other issues that I am not taking into account.

Overall I *think* that it's highly likely the game will be able to find a decent resolution depth and hertz and open a fullscreen mode. Unsupported fullscreen isn't going to be that common. But you probably do need to check for 0 hertz being good because I think that applies mostly to LCD displays which are popular.

And of course you can try checking to see if the graphics opened, with Context=GLGraphics() or whatever, and then `If Not context`. I don't know how reliable that is, thought, for staying within the app and not just bombing out from the driver. But it's worth putting in anyway.

I also should mention that I prefer to find a `higher specification` mode before going with a lower-spec mode. Just my preference. If I find a higher hertz mode, having failed to find the exact hertz, I will use that first over downgrading. At least then the `desired` mode is included within the performance of the mode you choose, whereas a lower hertz would mean a drop or loss of some kind. I probably would also choose a higher resolution than a lower one, and a higher res with 32-bit color than a lower one with 16.


Grey Alien(Posted 2006) [#9]
AngelDaniel: wow, that's certainly pretty thorough. So if all modes show 0Hz, you just end up picking a suitable screen res adn setting it to 60Hz and hope that the software refresh rate *does* match the video Hertz.

Well it's sound a bit annoying that you can't test if OpenGL Fullscreen works or not, but the file solutions sounds workable. I also have an ini file that records the findings in. The only problem with the file method you've implement is I wonder how many people try the game again after it crashes initially. I do sometimes I admit, even though it's not logical to do so, but your game *will* work on the second try :-)

I've been using this to detect the desktop refresh rate (and the in-game refresh rate) but it's directX only (or manybe just Windows only): RefreshRate = GetDeviceCaps(GetDC(PrimaryDevice.hWnd), VREFRESH)

I don't know if this helps or gives you some ideas.
yes it has, thanks :) I need to mull this over some more and see what anyone else has to offer too.

What are these stencils you talk about and why is 32 bit required for them? I found that my game which uses alpha blending and Pngs seems to be fine in 16 bit mode.

Yes I might try to go for higher Hz if it's there. To be honest I just needed to get some basic detection up and running so I could get on with other things.

I did try checking the result of graphics() like I said but even if it failes to create the correct mode full-screen it still makes a weird little application box on the task bar which you can't get rid of without Ctrl+Alt+DEl, which is annoying.


Chris C(Posted 2006) [#10]
ya know there should be a brl way of doing this by now

preferendgraphics(800,600,32,BRL_OPENGL|BRL_SHADER|BRL_STENCIL)
or
preferendgraphics(640,480,16,BRL_DIRECTX|BRL_SHADER)

and it would find the nearest mode it can

you should also be able to query the capabilities of the current mode regardless of what rendering backend you are using.

This would save *many* people *many* problems, maybe it could be a community project?


Grey Alien(Posted 2006) [#11]
ChrisC: yeah that would be a nice function for sure!


ImaginaryHuman(Posted 2006) [#12]
Hmm. I'm using OpenGL only since I'm on a Mac, not planning to support DirectX, so I can't use that method for getting the refresh rate.

Off hand doesn't BlitzMax have like a GraphicsHertz() command that returns the hertz rate of the currently open context? Wouldn't calling that after it's open allow you to get the exact hertz rate of the display so that you are synchronized? Or doesn't that command exist?

As to the file solution... I have been looking in to a reliable way to have one program execute another. If a way can be found that definitely reliable executes a second blitz program from the first, on all platforms under all conditions, then you can use this as an abstraction technique. You could have this one small program which the user clicks on, thinking they are starting the game. This small program launches the main game program which is the real game, unbeknownst to the user. The real game checks the failure file if it exists, and if so opens in a window. Otherwise it tries to open fullscreen. If it fails to open fullscreen it has meanwhile written a new failure file (if it didn't exist) and now upon returning to the small program the small program sees that the failure file exists. Now the small program re-launches the main game program a second time, and this time it should detect the failure file and open in a window. The user will only see that the first program bombs out and then the game comes up. Hopefully it wont display a crash dialog inbetween.

Abstracting it in this way is one way to get around the twice-run requirement. But then, I didn't yet see a super definite method of executing a file from within a program, and being able to wait for it to exit, and knowing that it has exited, and not using up lots of cpu time to monitor it.

As to stencils .... you don't need a stencil buffer to do alpha blending. Alpha blending, as with png's etc, just uses the alpha channel which is part of the texture data. It doesn't even matter if the OpenGL display has or has not got an alpha channel as part of the backbuffer (could be a 24-bit RGB mode). The stencil is a separate stencil buffer, usually 8-bits, which you can draw shapes into and upload data into etc, which you can then use with the OpenGL stencil test to check if bits are set in the stencil for each pixel, etc... so you can mask off irregular parts of the screen for whatever reason. You can do much the same thing with alpha channels, which is what I'm probably going to switch to.

If you need a stencil buffer, someone else on here had said that for the most part, almost no graphics card support stencil buffers in 16-bit color mode. I don't know what the reason is, you'd think it wouldn't matter. Maybe it requires too much extra hardware or something. But on my systems 16-bit mode does not allow a stencil buffer. And if you try to open with a stencil buffer in 16-bit, it will bomb out with the error that the `drawable is invalid`. You also cannot always specify to have an alpha channel as part of the backbuffer, in 16-bit mode, it does the same thing.

Considering I Was going to use the stencil buffer, it meant pretty much throwing away 16-bit mode. But I think maybe if I can get around it and use alpha channels to do what I want then I don't need to use stencilling.

It would be pretty nice to have a system default official mode-promotion-selection-adjusting thing.

It would also be pretty nice to be able to officially query the driver and get back some information such as if fullscreen is supported, if dithering is supported, if it is hardware accelerated, etc.


Grey Alien(Posted 2006) [#13]
GraphicsHertz() is broken in in DirectX, it returns 0, no worries though as that other function I listed does it anyway. Also I don't need to sync anything as my timing routines are independant of Hz and won't go jerky.

As for one app launching another, that should be possible, I've done it in Delphi before for an automatic upgrade over the network for client/server software.

Thanks for the heads up on stencil buffers, I don't need them though.

I think the whole problem with driver querying is that many drivers don't report correctly or at all, mind you, some info is better than no info.


N(Posted 2006) [#14]
I do test for them. My system doesn't even let you specify the width and height directly, it only lets you search for the closest compatible mode.

It's handy for making menu options and the like with a list of supported resolutions, since I don't have to ever switch from the actual res to a mode number and back again, I can just pick an index and use it to set the graphics mode.

Rambling done for now.


ImaginaryHuman(Posted 2006) [#15]
If GraphicsHertz() works in OpenGL that's fine with me.

I don't know if i'll do the `one program executes another` thing. It seemed like nobody has quite a solid enough way to go about it reliably. If it were supported properly by BRL that would be different.


TartanTangerine (was Indiepath)(Posted 2006) [#16]
As for one app launching another, that should be possible, I've done it in Delphi before for an automatic upgrade over the network for client/server software.

Download the full version of GEOM and see this in action.


Yan(Posted 2006) [#17]
I've only skimmed this thread, but it's seems to me the problem is with you trying to find a mode with a particular refresh rate.

Shouldn't you be using...
If GraphicsModeExists(ScreenWidth, ScreenHeight, 32)...ETC..
...instead?

Since you're using fixed rate logic, the refresh rate doesn't really matter anyway.


Grey Alien(Posted 2006) [#18]
Ian, yes I'd come to that conclusion, and having you say it makes it even more concrete, thanks.