@Latch and Ashingda 27
I've been giving this a bit of thought, do you think these explanations would be any good? (I've never written a tutorial before
)
Getting DBC to work on Vista
Exe’s built using DBC v1.13 or older would start by writing temp files to the ‘C:\Windows\Temp’ folder. However, in Vista this folder is locked and cannot be edited unless the current computer user is running as an administrator. To allow Vista compatibility, DBC 1.20 (also known as DBC 1.14) changed the folder used to hold temp files to ‘C:\dbTemp’, where ‘C’ was the drive that the exe was located on.
If the folder ‘<root drive>:\dbTemp’ cannot be created, then the exe will fail to load and will instead show a blue splash screen that is normally only seen when the user exits a program made using the trial version of DBC. It is, therefore, impossible to run a DBC exe from a write-once CD, since the program will be unable to create or edit the contents of a temp folder on such a disc. Instead, you have to copy the exe and all associated media to a folder on either the hard drive or a pen drive and the exe will run normally.
Something else to bear in mind is that while the dbTemp folder is created automatically, it is never actually deleted. After the exe is closed, the files ‘_virtual.bmp’, '_virtual.dat', ‘_virtual.jpg’ and ‘_virtual.wma’ can exist, so when your program terminates, you may want to call this code:
sdir$=left$(windir$(),3)+"dbTemp\"
if file exist(sdir$+"_virtual.bmp")=1 then delete file sdir$+"_virtual.bmp"
if file exist(sdir$+"_virtual.dat")=1 then delete file sdir$+"_virtual.dat"
if file exist(sdir$+"_virtual.wma")=1 then delete file sdir$+"_virtual.wma"
if file exist(sdir$+"_virtual.jpg")=1 then delete file sdir$+"_virtual.jpg"
if path exist(sdir$)=1 then delete directory sdir$
It’s not essential, but it could be a good idea, both as a cleanup measure and as a way of preventing others from being able to access parts of the media used by your program.
Removing the 60fps lock
You may have noticed that no matter what you do in DBC, you can’t make your programs run faster than 60fps – you can’t make the monitor show more than 60 images a second. Even a piece of code as simple can’t do it:
Sync On
Sync Rate 0
Make Object Cube 1, 5
Do
Text 50, 50, Str$(Screen FPS())
Sync
Loop
This isn’t a technically a limitation of DBC, but something that depends on how you monitor is set up. Buried deep within your 3d display settings is a little parameter called VSync, which locks a computer game’s refresh rate to the monitor’s refresh rate – so if your monitor refreshes at just 60 times a second, no computer game you make (or run, for that matter) can put more than 60 images to your screen in a second.
If you’re wondering why this restriction was ever imposed in the first place, it was because some early computer games ran so quickly that they were sending a new image to the screen before the old one had been fully drawn, and so the screen began to show a flickering, disjointed mess – an effect known as ‘tearing’. This effect isn’t as much of an issue now, thanks to advances in software, but the limitation still remains to annoy DBC programmers.
You could just go into your control panel and set VSync to ‘Force Off’, but it’s far better (and easier for users on other computers) to leave it set to the default of ‘Let 3d application decide’ and make some adjustments within DBC itself:
1) Find and open the file ‘Setup.ini’, located with the file ‘DB.exe’. If you used the default install path, then it should be located in ‘C:\Program Files\Dark Basic Software\Dark Basic’.
2) Read through the file until you find the blitflipmode parameter, and make sure that the line says ‘blitflipmode=1’. This doesn’t do anything by itself, but it allows DBC to give higher frame rates if you do the next step. Don’t forget to save the setup file if you’ve had to make any changes to it.
3) Now you have a choice of what you want to do. You can either:
a) Load an mp3 audio file by calling ‘Load Music ‘myaudio.mp3’, MusicNum’. MusicNum can be any number you like. You don’t even have to play the music, but you’ll notice a sharp increase in frame rate. Note that this won’t work for a midi music file.
b) You could use the following code snippet to create a frame rate increase though using a midi file:
rem use winapi to load and play midi
rem by latch
rem august 2007
Sync On
Sync Rate 0
Make Object Cube 1, 5
winmm=1
filename$=chr$(34)+"C:\Program Files\Dark Basic Software\Dark Basic\media\music\Fantasy\Crystal.mid"+chr$(34)
open_midi(winmm,filename$)
Do
Text 50, 50, Str$(Screen FPS())
Sync
Loop
end
function open_midi(dllnum,filename$)
if dll exist(dllnum)=0
load dll "winmm.dll",dllnum
endif
if dll call exist(dllnum,"mciSendStringA")=0
break "dllnum may exist - or call not exist"
endif
rem open device command
lpszCommand$="open "+filename$+" type sequencer alias m1"
result=call dll(dllnum,"mciSendStringA",lpszCommand$,0,0,0)
call dll dllnum,"mciGetErrorStringA",result,error$,200
print error$
rem load file into sequencer
result=call dll(dllnum,"mciSendStringA","play m1 from 0",0,0,0)
call dll dllnum,"mciGetErrorStringA",result,error$,200
print error$
endfunction
4) When you build your final exe, always place a copy of the Setup.ini file in the same folder as your exe. Without it, the DBC program will default back to locking VSync on, and you’ll still lock at 60fps.
Further Notes
The actual value of 60fps as the maximum program refresh rate will vary from monitor to monitor. Most modern LCD monitors are made to run at 60Hz (that is, they give 60fps), but some 120Hz monitors are around. Also, if you change the display resolution then you may be able to make your monitor refresh faster.
You should also be aware that some computers systems don’t have a frame rate cap – they appear to be ones with older versions of DirectX installed.
Reducing the 100% CPU usage
If you have a CPU meter on your computer, you’ll have probably noticed that whenever you run a Dark Basic program, one core of your CPU is always running at 100%. It is possible to reduce the CPU workload by making the program sleep for a short time each program loop. However, you shouldn’t use the DBC ‘Sleep’ command because this will make text drawn on screen appear to flicker. You have to load the Kernel32.dll, and call the Windows ‘Sleep’ function:
load dll "kernel32.dll", 1
Sync On
Sync Rate 0
Make Object Cube 1, 5
Do
Text 50, 50, Str$(Screen FPS())
YRotate Object 1, WrapValue(Object Angle Y(1) + 0.5)
Sync
System_Sleep(10)
Loop
function System_Sleep(timeMs)
call dll 1, "Sleep", timeMs
endfunction
If you can review your CPU meter history after exiting the program, you should find that there will be a sharp peak for the first two or three seconds of running the exe, and after that the program will settle down, using around 40% of the CPU.
Why 40%? It’s because the exe is sleeping for 10ms after every sync, so at 60fps the processor spends 60*10 = 600ms lying idle. So, it must be doing all the work in just 400ms, which is 40% of one second!
With a bit more maths, you can work out how long each frame takes to be drawn to the screen and work backwards to ensure your desired frame rate. It takes 400ms to draw 60 frames, so it’s taking 400/60 = 6.6667ms to draw each frame on screen. For simplicity, let’s call it 7ms. If we want 30fps, then it will take 30*7 = 210ms to draw them on screen. We can let the processor lie idle for the remaining 1000-210 = 790ms, so the sleep period has to be 790/30 = 26.3ms (26 to the nearest integer). Plug in 26 to the above code snippet, and you can see it run at 31 to 32 frames a second.
So, in a nutshell the sleep time calculation is:
Sleep time = Int((1000 – (DesiredFrameRate*FrameDrawTime))/DesiredFrameRate)
You have to be careful about the value you specify for FrameDrawTime – for my computer a value of 7 works well, but this will almost certainly be different for others and I’m not sure it can be calculated at runtime using this method. Because of the 60fps lock (discussed above) a sleep value of anything from 0 to 7 will still produce a constant 60 frames a second. If you read on, you’ll find a neat trick to sidestep it.
Increasing frame rates and freeing up processing power
By combining the above two techniques it is possible to achieve higher frame rates than 60fps, without running one processor at 100%. Try a code snippet like this:
load dll "kernel32.dll", 1
Load Music "ANY_MP3.mp3", 1
Sync On
Sync Rate 0
Make Object Cube 1, 5
Do
Text 50, 50, Str$(Screen FPS())
YRotate Object 1, WrapValue(Object Angle Y(1) + 0.5)
Sync
System_Sleep(14)
Loop
function System_Sleep(timeMs)
call dll 1, "Sleep", timeMs
endfunction
This will return you to a maximum refresh rate of 60fps, but the processor is running at around 12%. There may be frequent processing spikes, but I believe this is due to Windows taking advantage of the fact that your program is ‘Sleeping’ and using the spare processing power for other things.
The sleep time may still be calculated using this formula:
Sleep time = Int((1000 – (DesiredFrameRate*FrameDrawTime))/DesiredFrameRate)
but it now becomes possible to calculate the frame draw time. Call this code at the start of your program:
Sync On
Sync rate 0
SyncNum = 0
load dll "kernel32.dll", 1
Load Music "ANY_MP3.mp3", 1
Make Object Cube 1, 5
rem Let the program run for one second.
TimerOne = Timer()
Repeat
Sync
Until TimerOne+1000<Timer()
FrameDrawTime# = 1000.0/Screen FPS()
DesiredFrameRate# = 30.0
SleepTime = Int((1000 - (DesiredFrameRate#*FrameDrawTime#))/DesiredFrameRate#)
Do
Text 50, 50, Str$(Screen FPS())
Sync
System_Sleep(SleepTime)
Loop
function System_Sleep(timeMs)
call dll 1, "Sleep", timeMs
endfunction
It works by allowing the program to run at its fastest rate for one second, so we can get access to an accurate Screen FPS() value. By dividing this by 1000 we get the length of time, in ms, the computer needs to draw one image on screen. We can then plug this value into the other formula, and work out how long the program can sleep each sync.
"I wish I was a spaceman, the fastest guy alive. I'd fly you round the universe, in Fireball XL5..."