Thursday, September 27, 2012

Outrun 2 Progress

Wait, what?  I haven't updated this blog in a year?  Hm, I can't believe it's been this long...

Well, haven't had anything too interesting to report until now though.  I'll start off by saying this once again... I've had a rather ridiculous amount of requests for Outrun 2 (XDK 5849, the very first title using it [that I know of] that does anything on Cxbx) and various other SEGA titles.  It's not that I'm not interested in these games, it's just that SEGA's Xbox titles are not exactly easy to emulate!  They're ALWAYS problematic in one way or another, therefore they aren't easy to emulate.  Outrun 2 is no exception, but I'll get to that in a minute.  Surprisingly, Outrun 2 is much easier to work with than I originally thought.  It was just a matter of adding missing functionality and fixing a bug or two in the existing implementation(s), and unlike most SEGA titles, the problems are rather easy to identify this time.  Keep in mind, this isn't Outrun 2 SP [DX]; I don't have that version... YET!

"So, what's stopping it from working, and when will this become playable?" Good question(s).  I've determined that it's an audio related issue.  Outrun 2 uses some Xbox exclusive high level code to stream audio.  No one has ever implemented the XMediaFileObject interface or any of it's relatives, so unless I do it myself, it's not going to get done.  There is no PC equivalent so I have to emulate everything myself.  For loading and streaming .wav files (even if they use the Xbox ADPCM codec), emulating this is a piece of cake.  For .wma files and other supported audio files, it may not be as easy (unless I can find some documentation on the file format).  Games like Whacked and Quantum Redshift, I could just leave out the functionality and the game would work fine, but not for Outrun 2, obviously.  So I'll have to take some time out to learn about audio loading/streaming with these APIs and write some test code to verify Cxbx can do it properly.  Good thing I have my handy Debug Xbox sitting next to me. But Lord, where's caustik when you need him??  lol.  So until this is fixed, I can't guarantee this game will be on our playable list in the future.  Don't worry, I'll keep trying!

One more thing, you may have noticed the fluctuation of framerates on those screenshots above.  It's because (like most Japanese titles) the game is using D3DDevice::BlockUntilVerticalBlank() to stall/synchronize threads for audio and other time sensitive threading procedures.  While I support and recommend this approach personally, Cxbx's previous implementation wreaks havoc on the host's CPU usage!  On the console, this is not an issue, but on Windows, this is a problem.  Since DirectDraw is the only way to accurately keep track of vertical blank (via IDirectDraw7::WaitForVerticalBlank), that's what we've been using.  Unfortunately, this causes us problems when used in many different threads and bogs the whole system down.  So instead, I've been using Sleep(1000/60) where 60 is the theoretical refresh rate Cxbx is running at.  I'll add a menu option to switch between the functionality just in case you need one over the other.  But for Outrun 2, it fixed the frame rate issue and should for a few other titles using the same methods.

So, that's all I have to say about Outrun 2 at the moment.  Looking forward to making more progress on this game because it's a rather high priority game for me.  Thanks for reading!



  1. Wouldn't it make a lot more sense to use this [Unless you do the VBlank in an entirely different thread which has a proper priority]:

    unsigned int div = 1000;
    unsigned int fps = 60;
    unsigned int frameTime = (1000*div)/fps;
    Sleep((frameTime - ((GetTickCount()*div) % frameTime))/div);


    1. Okay, I know it's a bit old now, but the implementation I used works just fine (assuming that the refresh rate is 60hz), but the intention isn't to sync based on frame rate itself (sorry if I'm making a bad/wrong assumption here). Either way, it's not wise to assume 60hz for every machine; should actually get the real refresh rate the host machine's monitor is running at.

      Also, the XDK samples sometimes use this method to simulate blocking until vertical blank. I even use it in my bullet hell game I've been working on. It seems fairly accurate so far and hasn't caused any problems, but if it does, I'll change it later. At the time, I just wanted to throw in a quick solution.