Topic: 7. MAME

7.    M A M E

7.1. Introduction.

7.2. How MAME handles the video emulation. The different video and sound options.

7.3. "'The New Video System" and where it lead us. Configuring MAME's current video options.

7.4. How to restore audio and video synchronization in modern MAME.

Re: 7. MAME

This article was written for the most part during February of 2012, but for several reasons it hadn't been published yet. Today, with MAME v0.155 about to be released, the information contained here is outdated to some extent. However I preferred to leave the article in its original form.

Bear in mind that the configuration that's explained here only applies to official MAME. It is NOT applicable to GroovyMAME, which already targets the issues discussed in this article with its own implementation.

I want to thank MAME developers for keeping up the good work improving this awesome emulator day by day.

Calamity

Re: 7. MAME

7.1. Introduction.

MAME -Multiple Arcade Machine Emulator- is a very complex object. With more than a hundred different options interacting with each other, it will certainly intimidate the novice user. Just from a software engineering point of view, MAME is something truly awesome. It is hard to believe that such a huge project could emerge just out of the collaborative effort of independent developers over the years. At the time this article is written, MAME has just celebrated its 15th Anniversary, with its v0.145 release.

This article is the result of a long research on MAME's video emulation options. We will explain how to properly configure MAME in order to achieve a perfect video emulation experience and we will teach you how to use the right options for each situation, but note that it's assumed your system is ready for customized video modes without limitations. We'd also like to raise awareness on some issues concerning MAME's video synchronization, that unfortunately have been ignored for too long. We'll try to explain the causes, provide workarounds when possible, and finally demonstrate how these problems could get fixed, by suggesting modifications to the source code that efficiently solve these issues without major changes to MAME's design.

Re: 7. MAME

7.2. How MAME handles the video emulation. The different video and sound options.

Since its start in 1997, the MAME project has gone through various transformations affecting how video emulation is handled. One of these changes was required due to the transition from DOS to Windows, back in 2001, which meant the end of direct hardware access era, imposed by the Windows operating system design -think that once upon a time, main line MAME had native support for 15 kHz video modes-. Anyway, this was quite a natural move since DOS was definitely a dead end. A second revolution, also known simply as the new video system, came in 2006 with version 0.107. This is the system we'll be discussing in the next chapter. We may consider the recent integration of HLSL as a third fundamental change, though this one is definitely on the antipodes of the way we understand video emulation.

But before going into further details on how modern MAME handles video emulation, let us introduce some of the concepts by following MAME's evolution since its early days. As DOS-based MAME was abandoned and the development focus moved to Windows, MAME's video system evolved accordingly by taking advantage of the DirectDraw API. For a 2-D-graphics programmer, DirectDraw was a godsend, as it provided an intuitive way to access the frame buffer, without the hassle of dealing with specific hardware issues. The basic idea is to gain access to a portion of memory -known as a surface- that represents, one by one, the RGB values of each pixel on the screen, in a sweet sequential way. Now, although DirectDraw supports hardware stretching of the game's frame to any arbitrary screen resolution, this is generally contrary to our purposes; rather than that, we need to ensure that the pixels on the game's frame are faithfully transcribed into the screen. What we users have been doing during these years -through all sorts of hacks and methods- is providing MAME with a bunch of usable low resolution video modes in order to allow displaying the games without any kind of stretching artifact. Usually we tell MAME which specific video mode to pick, and DirectDraw is responsible for creating the full screen display. In case there's a small difference between original and target resolutions, MAME will either leave black borders or crop the frame as required, without any additional artificial adjustment.

The options involved here are -nohwstretch, that disables hardware stretching, and -switchres, that tells MAME to enable video mode switching. MAME will try to find the video mode that best fits the one of the game, from the list reported by Windows.

But generally it's not a good idea to let MAME do this. Instead, we will explicitly tell MAME which video mode to pick, and in MAME pre-0.107 this is performed by the pair of options -resolution and -refresh. By displaying emulated games on a CRT screen at their native resolutions, we will enjoy a picture that's undistinguishable from the real thing... until things start moving. Unfortunately, as soon as animation starts, a new family of visual artifacts becomes evident: tearing and scroll hiccups. As you know, these nasty artifacts happen whenever an application updates the contents of the video memory outside of the vertical blanking interval. This is something that the real video game hardware wouldn't do, and for this reason, the human eye automatically detects the fake and the illusion vanishes. It is definitely amazing how some people can live with these issues and still pretend they don't ruin emulation altogether!

For a very good reason, MAME implements an option specifically designed to solve this problem: the -syncrefresh option. What this option is supposed to do is to ignore the game's original refresh rate and adjust the emulation speed to be synchronized with the video card's refresh. Now, before you go through hours of frustration, be aware that the -syncrefresh option doesn't work any more since version 0.114, as we'll be explaining a bit later.

It's just important to grasp the concept of what we're trying to achieve here. Enabling -syncrefresh used to result in perfectly smooth scroll and animation, however, there's an obvious side effect to this approach: the game's action will be either accelerated or slowed down. How much? That will depend on the difference between the original refresh and the one that the video card is outputting. For instance, a game which originally run at 55 Hz, when synchronized to a 60 Hz video mode -the most common case with today's screens-, will run smoothly, though accelerated up to 109% of its original speed (60/55 = 1,09). The traditional workaround has been creating different instances of each resolution, combined with the required vertical refresh values for our target games, and then manually telling MAME which one to use for each specific case. On this regard, it's important to notice that, in practice, it's almost impossible to get a perfect match for a given refresh rate, though it's generally possible to get a good-enough approximation -about 0.1 Hz precision-. By using a refresh rate that is sufficiently close to the original one, the difference in emulation's speed introduced by -syncrefresh will be negligible.

On the other hand, inexplicably, the sound code is not aware of the -syncrefresh option, so it keeps trying to work at the original speed, causing sound glitches, as the sound tries to catch up video emulation. So, in a way, we're transferring the video problem to the audio side. This issue, however, will be hardly noticeable as long as we manage to create a video mode with a refresh rate that's very close to the original one.

Anyway, if we're actually capable of generating any desired target refresh, at least with a reasonable level of accuracy, you may be wondering why in the world we need the -syncrefresh option at all. After all, our refresh matches the game's refresh, doesn't it? There are some common misconceptions regarding video synchronization and MAME. Usually people tend to believe that just by selecting a refresh that matches the target refresh, combined with the -waitvsync or -triplebuffer options, the video synchronization issue should be solved in MAME. In practice, this removes tearing indeed, but at the price of adding some degree of scroll and sound stuttering in most situations. But, why? Fortunately, there is a simple explanation for this, under the hood. By default, MAME will try to keep the game running at the theoretical speed it is supposed to run. This is performed by an internal throttling mechanism, which uses the computer's CPU clock to keep the frame-time adjusted to the values measured in the original hardware. In modern computers, most games emulated by MAME would run too fast, so what the throttling mechanism does is to introduce an accurate delay between frames, of the required length, in terms of CPU clock cycles. This is controlled by the -throttle option, which is enabled by default -when disabled, MAME will run at full speed-.

mame rtype    -throttle / -nothrottle

Keeping a game throttled like this reproduces the original game's speed, with more or less accuracy, but as we said, results in tearing, as the CPU-based clock completely ignores the cadence of our physical video signal. Now what happens when we add -waitvsync or -triplebuffer as an attempt to fix tearing, is that, in addition to the throttling delay, MAME will need to wait for the video card's vertical retrace -essential to avoid tearing' so we'll be introducing a second clock in the scene, the one in the video card's GPU. In fact, our video card can be seen as an external clock that produces vertical retrace pulses at a constant rate, for instance 16.17 ms for a 60 Hz refresh. Indeed, MAME is trying to emulate the same thing with its CPU-based clock.

And this leads us to one of the most important concepts we need to understand here: in the scope of PC hardware, trying to keep two clocks synchronized like this, is impossible in practice. There will always be some degree of phase lag between them. On this regard, it's irrelevant if this is a tiny value as 0.05 Hz or as huge as 5.00 Hz; the difference will get accumulated after several frames, and at some point, unavoidably, one vertical blank interval will get missed, resulting in a visible scroll hiccup. Of course, the magnitude of this difference will matter in how often this dreaded artifact occurs, but the only way to guarantee a hiccup-free animation is getting rid of one of the two clocks, and being video fluency critical for us, it's the CPU clock what needs to go. This is exactly what the -syncrefresh option does -well, did, remind we're still talking about MAME pre-v0.114-, and why it's preferred to the other two. By enabling -syncrefresh, MAME internally instructs its throttling mechanism to just wait for vertical retrace without any other CPU-based delay, adjusting the emulation speed to our video card's current refresh rate. Still, it's our responsibility to provide MAME with the proper refresh rate to synchronize to; otherwise sound and emulation's speed will be noticeably affected. This involves the creation of custom video modes with the desired refresh rate.

Compared to other emulators, MAME is somewhat confusing, as it has three different implementations for the video synchronization thing. Then, is there any use for the -waitvsync and -triplebuffer options? Well, according to our extensive tests, as long as there is a functional -syncrefresh option -as is the case for MAME pre-0.114-, we don't need the others at all. Both -syncrefresh and -waitvsync contain the same wait for vertical sync functionality. But unlike -syncrefresh, -waitvsync keeps the throttling mechanism enabled, so both clocks continue interacting with each other, resulting in erratic frame rates and scroll stuttering. On the other hand, the -triplebuffer option is meant to improve performance by creating two back buffers, so in theory one of them will always be free for MAME to draw to, while its counterpart is locked, waiting to be displayed as soon as the next vertical retrace happens. As a consequence of this, MAME should be able to start drawing to either one of the two back buffers immediately after a new frame is computed, resulting in smoother performance than enabling -waitvsync alone, which implies there's no practical use for -waitvsync in MAME. The third buffer is known as the front buffer, and it's the one which contents are currently being transferred to the screen at the raster pace. Each one of the two back buffers will become the front buffer at some point: this is known as surface flipping.

The ultimate purpose of the triple buffering idea is to release the game loop code from having to wait for vertical retrace. Obviously the wait for vertical retrace still needs to be performed somewhere, otherwise tearing would happen, but it's supposed to be done by different code that only deals with the screen update, running in parallel. So, the triple buffering concept implies that both game loop and screen update routines must run asynchronously in different threads of execution. Unfortunately, DirectX doesn't support such functionality natively, although its documentation is misleading on this regard: v-sync'ed flipping methods don't return immediately as requested and expected -they are not scheduled-, which causes the game's speed to be limited, in practice, by the video card's refresh, if this one is lower than the original game's refresh.

But it's even worse. The triple buffering concept also involves that the system must be capable of dropping frames when required. For instance, if a new frame is ready and the previous one is still in the queue, the older frame should be dropped. This is to make sure that it is always the most recent frame the one that ends up showing, at the time the vertical retrace occurs, so there's no additional lag derived from the use of buffered frames. Unfortunately for us, DirectX arranges the buffers in a stupid circular queue, so adding more buffers to the queue only results in adding more frames of input lag -actually video lag-. This completely defeats the purpose of triple buffering.

So the theoretical benefits of triple buffering don't apply here, and what we get is just a sophisticated sort of double buffering. Nevertheless, we'll see how we can exploit this issue in modern versions of MAME, as a workaround to mimic the behaviour of the -syncrefresh option, although with a lag penalty.

The -triplebuffer option does not disable the throttling mechanism either, because its purpose is to eliminate tearing while keeping games running at their original speed. This causes tearing artifacts to disappear, but introduces scroll stuttering instead, for reasons explained above. However, MAME's -triplebuffer option only works as it's supposed to -keeping game's speed at 100%- when the video card's refresh is higher than the game's refresh. Notice that, with a correct triple buffering implementation, this shouldn't be the case!

So, putting it all together, here is a sample of how a correct configuration for MAME pre-v0.107 would look like:

mame rtype   -ddraw
                   -switchres
                   -resolution 384x256
                   -refresh 55
                   -nohwstretch
                   -throttle
                   -syncrefresh

Notice that MAME can't create the 384 x 256 @ 55 video mode by its own means. MAME needs this video mode to be already present in the system, so it will just request it to the display driver. Before we can use this video mode in MAME, we must have taught the driver how to build this particular mode, to our desired specs. This is done by means of a modeline, although the details on how this is performed are worth for a whole different chapter. For our sample, this is the modeline we'd prepare behind the 384 x 256 @ 55 label:

Modeline "384x256@55" 7.710 384 400 440 504 256 258 261 278 -hsync -vsync

R-Type's native refresh is 55.0176 Hz. This modeline, theoretically, will make for a refresh of 55.0274 Hz. This means that by synchronizing R-Type to this modeline, it should run at 100.017 % of its original speed. Not perfect but not so bad.

So that was, summarized, how MAME was configured back in the pre-0.107 days, for what video emulation is concerned.

Re: 7. MAME

7.3. "The New Video System" and where it lead us. Configuring MAME's current video options.

At this point, we'd recommend you to take a break and read these two articles from Aaron Giles' blog: Monkey, and very specially, The New Video Landscape. The later article was written for the new video system presentation with the release of MAME version 0.107, and has been shipped with any MAME official build since then, in the form of the newvideo.txt file.

Aaron Giles divides MAME potential users in three categories depending on their video setup. We're interested in category 3:

"Category 3: Anal video mode types. These are the guys who have generally built their own cabinets and set them up with a CRT display where they have several dozen carefully hand-tweaked video modes that approximate the original video modes the games ran at. They want MAME to pick that hand-tweaked mode and use it, drawing one pixel on the screen for each pixel in the original game. They don't give a whit about artwork or anything other than the raw pixels going to the right place. Fortunately, you can still configure MAME for this case as well."

As you may have guessed, those guys are us: all what's demanded by the above mentioned users sounds wise and reasonable to us. It's a relief the MAME team considered to continue supporting this mode, seriously.

So what's new about the new video system? First off, it is mainly focused on the Direct3D API, contrary to the old DirectDraw based video system we've been discussing. You may be wondering why Direct3D should be necessary at all, as it's well known that even 3-D hardware is emulated by software in MAME -so, at the end of the day, it's all about displaying 2-D graphics-. Of course, there was a good reason for this. Modern video cards are way faster at dealing with 2-D graphics when taking advantage of 3-D acceleration. This includes frame compositing: the use to 3-D hardware for high performance frame stretching and scaling while adding artwork overlays at the best possible resolution. Again, implementing Direct3D was quite a natural move. Microsoft had been struggling to deprecate DirectDraw for years, recommending programmers to use Direct3D instead. However, Direct3D has nothing that remotely resembles a simple frame buffer.  Basically, the game's frame is going to be represented by a texture quad floating in a three-dimensional scene. There's something intrinsically bizarre in this construct that makes the 2-D programmer uneasy, although things are perfectly normal from the user's point of view.

But, is this new video system any better for us, anal video mode types? The answer is: not much, although it's not necessarily worse. Most of its new features and improvements -the bells and whistles- are for the enjoyment of Category 1: people who will stretch games over LCD screens without switching resolutions or really caring about refresh rates. That's the target user of MAME since version 0.107. This is perfectly reasonable, but it's important to remark it, as it will help us understand some of the issues that, unfortunately, came later.

As stated, the new video system still supports the old way of doing things. And, as a matter of fact, DirectDraw is still implemented and works as good as it did before. Indeed, according to the newvideo.txt document, DirectDraw is the suggested setup for Category 3 users. Even though, Direct3D can also be configured to get the exact same results that DirectDraw achieves, with just two limitations we'll discuss in a minute. We specify the video mode we want much the same as before, though now, resolution and refresh are both entered in a more convenient way:

-resolution 384x256@55

Here is a sample of a proper configuration for MAME 0.107-0.113, both, DirectDraw and Direct3D video setups:

mame rtype  -video ddraw
                  -switchres
                  -resolution 384x256@55
                  -nohwstretch
                  -throttle
                  -syncrefresh

mame rtype  -video d3d
                  -switchres
                  -resolution 384x256@55
                  -nofilter
                  -throttle
                  -syncrefresh

As we said, the DirectDraw system works much the same as before. We're interested, however, in the new Direct3D system. We notice the new option -nofilter. This one is necessary as, otherwise -if -filter is enabled- the game's frame will go through bilinear filtering, producing a blurry picture, so we just disable it. Another interesting bit is that the -nohwstretch option is no longer used. With DirectDraw, we used to disable -hwstretch to prevent MAME from stretching the game's frame to the screen resolution, so to avoid the resulting artifacts. However, -hwstretch is a DirectDraw-only option. There's no equivalent for Direct3D, because this system is set to stretch the game's frame by design!

Actually, the only workaround to avoid stretching with Direct3D is to use the exact same resolution that the game frame has, and disable the -filter option. By doing this, the results are identical to DirectDraw. Even though, this has a serious drawback in practical situations: creating a separate video mode for each unique resolution required by MAME results in just too many of them, much more than what the driver supports. This is not such a big problem with DirectDraw, where we can actually reuse a container resolution for many different lower resolutions. This trick works especially well for the vertical resolution, as DirectDraw will just add black borders if necessary without affecting geometry. So for instance, take these three native video modes:

-    320 x 224 @ 60
-    320 x 232 @ 60
-    320 x 240 @ 60

Here, we'd just need to create the last one: 320 x 240, as it already contains the other two. In fact, by creating the lower ones we'd be wasting valuable space in the driver's custom mode list, because from the user's point of view they will look exactly the same!: it's a matter of the video card generating padding black lines by means of hardware blanking or MAME adding them by software into the frame. So, by using DirectDraw, a game with a native resolution of 320 x 224, will display on a 320 x 240 resolution with no visible artifacts. On the other hand, Direct3D will stretch the game vertically by a factor of 240 / 224, completely ruining the visual quality. This is the reason why the DirectDraw setting is mandatory when using the Arcade-VGA card, as this card supports a set of 240-line resolutions but no 224-line one is provided. If we still wanted to use Direct3D without any stretching, an explicit 320 x 224 resolution should be created.

There's still another limitation to Direct3D, a nearly undocumented one. Direct3D will refuse to switch to a display mode which is supposedly not supported by our monitor. DirectDraw will too, but in this case we can override this behaviour by disabling the Hide modes this monitor can't display check box in the advanced Display Properties dialog. Direct3D won't change its mind whatever we do. You may be wondering why we would want to damage our monitor by using an unsupported video mode. Well, sometimes we have created a video mode that we're positive our monitor supports, but Windows just decides to hide this mode from us. This may happen when using monitors with a valid EDID, as a regular PC monitor, so it's not a problem for arcade monitors or TV sets. Windows uses the information retrieved from the monitor's EDID to filter the list of video modes reported by the driver, resulting in some of these video modes being black-listed. Usually, a group or video modes with vertical resolutions like 256 or 512 lines will be marked as unsupported, apparently without any logical reason. As we said, the only workaround in these situations is to force Windows not to hide those modes and use DirectDraw instead of Direct3D.

An additional feature that was introduced with the new video system is a preliminary support for multithreaded execution -actually, it wasn't really available until version 0.108, but was developed during the intermediate updates from version 0.106 to 0.107-. The purpose of the multithreading approach in MAME is to improve overall performance by taking advantage of multi-core systems, moving the window and video processing to a second thread of execution. Unfortunately, the implementation of multithreaded video processing was not seen as an opportunity for adding a truly asynchronous triple buffering support.

However, due to the encapsulated design of MAME's core, there's a serious drawback to this multithreaded implementation: the emulator's core becomes unaware of what's going on in the video side, and this includes vertical retrace. Strictly speaking, the emulator's core is always blind to the vertical retrace state as this is checked in the OSD layer, but when running in a single thread, the emulator core needs to wait for the video routines to finish screen updating in order to resume execution, so both layers are forced to run synchronized. On the other hand, when multithreading is enabled, the emulator's core will be free to go on with execution without waiting for the video routines to finish, making our video synchronization options useless. That's why, starting from version 0.108, we'll make sure to add the -nomultithreading option to our MAME setup -it's the default value anyway-.

At this point, we must take a minute to mention AdvanceMAME. This was the name of a mythical derivative build of MAME, maintained by Andrea Mazzoleni, which shared the world with the official MAME build during several years. In fact, this was the preferred alternative for the exigent CRT user. AdvanceMAME kept releasing DOS and Linux builds well after the official project had moved to Windows. Thanks to this, AdvanceMAME had retained the capability to access the video hardware directly, without the interference of the operating system, thus allowing on-the-fly generation of any desired custom video mode. Although Windows builds were available too, the functionality under this operating system was way more limited and rather experimental. The last version of AdvanceMAME ever released was version 0.106. Sadly, Mazzoleni gave up updating AdvanceMAME as a consequence of the new video system being introduced. According to his words, it would have required a massive rewrite of AdvanceMAME's code. With the AdvanceMAME project halted, users who wanted to do a proper use of their CRT monitors and still keep updated with main line MAME, necessarily had to choose the Windows route combined with a 15 kHz output solution like the Arcade-VGA card or any of the incipient software methods. Apart from that, things weren't that bad in the early days of the new video system. The fundamental stuff still worked as usual.

But, unfortunately, something truly catastrophic happened in version 0.114 release. An apparently innocuous change in the source code, affecting the throttling mechanism, made the -syncrefresh option useless. By useless we mean that it doesn't block CPU throttling anymore, so MAME will still try to force the game to run at its theoretical speed, ruining video emulation smoothness, for the reasons explained above. So the situation is this:

THE SYNCREFRESH OPTION IS SERIOUSLY BROKEN SINCE MAME v0.114.

This particular issue has caused tons of pain to MAME users during these years, as can be proved by searching through the different specialized internet forums. MAME devs must be aware of this as there's a specific issue open since 2008 on the MAME Testers board. It seems to be a real flaw that becomes obvious when analyzing the source code, rather than some functionality that was dropped consciously. And in fact, there's a nice and straightforward source code fix which restores the -syncrefresh functionality without any side-effect, as we'll explain in the last chapter, but obviously it involves MAME compiling. Fortunately, MAME is so good that it's still possible to reproduce the -syncrefresh functionality in versions post-0.114, through a specific combination of options, without messing with compilation at all. The workaround consists in disabling the -throttle function manually, and then enabling some sort of vertical retrace synchronization. Now, be aware that when using the DirectDraw setting, both -syncrefresh and -waitvsync options are ignored if the -throttle option is disabled, so in this case the only option we can use for the synchronization purpose is -triplebuffer. On the other hand, if Direct3D is used, any of the three synchronization options will work fine even with throttle disabled, and they are interchangeable in practice.

For clarity and simplicity, in the previous chapter we stated that the throttling mechanism always needs to be disabled in order to achieve fluent video animation, either by enabling the good old -syncrefresh option -when functional- or just by disabling -throttle manually for newer MAME versions as explained above. However, this is only partially true. Actually, the throttling mechanism only gets in the middle if the video card's refresh is higher than the game's native refresh. There's an explanation for this paradoxical fact. Assuming we're running MAME with video synchronization enabled, there are two possible situations:

- If the video card's refresh is lower than the native refresh, MAME will notice the game is running slower than required, so the throttle routine will exit automatically, resulting in the game running smooth at the video card's refresh rate.

- If the video card's refresh is higher than the native refresh, MAME will realize the game is running too fast, and will try to compensate for this by adding some extra delay. This is the exact cause of scroll stuttering when enabling video synchronization.

So one tenth of Hz above or below the required refresh will produce completely different results in terms of smoothness, which appears as random behaviour to the user. This inconsistency has traditionally resulted in a common perception of video synchronization and MAME as something esoteric, always shrouded in uncertainty.

A possible approach is to calculate modelines so that their refresh is always a bit lower than the required one, slowing MAME down just enough to force the throttle delay to get bypassed. However, this is not a very good idea for practical situations, because the resulting refresh could turn out to be higher than calculated due to pixel clock granularity issues. In our experience, it's better to just go for the best approximation, whether it's above or below of the desired refresh, and always turn throttling off as a rule.

Putting everything together, these are the right settings for versions post-0.114 -up until this day-:

mame rtype  -video ddraw
                  -switchres
                  -resolution 384x256@55
                  -nohwstretch
                  -nothrottle
                  -triplebuffer
                  -nomultithreading

mame rtype  -video d3d
                  -switchres
                  -resolution 384x256@55
                  -nofilter
                  -nothrottle
                  -syncrefresh | -waitvsync
                  -nomultithreading

By using these options, you should obtain consistent results and silky smooth scrolling for any game. If you apply these settings and still experience scroll hiccups with any game, then chances are that your CPU is not powerful enough to run that specific game with video synchronization enabled. Actually, video synchronization should only be applied when a game can be fluently emulated at a rock solid 100% speed percentage with just throttling enabled. If doing this results in erratic speed, it's probably due to some of the frames taking too long to be computed, longer than it took in the original hardware. As vertical synchronization involves updating the screen at a constant pace, and we're setting our video card so that the time per frame matches the one in the emulated hardware, the time spent computing these slower frames won't fit between two vertical retraces, causing one retrace to get missed. This will force MAME to wait for nearly twice as long as it should for that frame, causing a noticeable drop in speed, often as dramatic as 50%. Therefore, in order to run MAME fluently with video synchronization enabled, our computer needs more CPU power than what's strictly needed to consider the game playable.

However, we end up feeling that we didn't achieve a fully satisfactory solution here. On the one hand, if we decide to use the DirectDraw interface, as a way to ensure proper integer scaling, we have to assume an input lag penalty, due to the compulsory use of the -triplebuffer/-nothrottle combination. But on the other hand, if we choose Direct3D to benefit from the less laggy -syncrefresh/-nothrottle combination, then we have to deal with the problem of MAME's hard coded fractional scaling, unless we have a perfect match for the game's resolution already available in the system.

Finally, let us comment on this statement from "The New Video Landscape" article:

"To avoid tearing artifacts, I recommend using the -triplebuffer option as well. Just make sure your monitor's refresh rate is higher than the game you are running."

As we've been discussing here, running MAME with -triplebuffer enabled at a refresh rate that's higher than the native one, always produces scroll hiccups. But as this is the officially recommended setting, we must conclude that scroll stuttering is assumed as normal by MAME devs, unfortunately. The above statement implies that MAME has silently renounced to smooth animation. This explains why there's no single mention to the -syncrefresh option in that article, although it's still documented by MAME as functional. And also why the -syncrefresh option stopped working short after that, and a bastard option named -refreshspeed was created instead. MAME is designed for flat panels these days and all that seems to matter is that the game's refresh is below the one of the panel.

Re: 7. MAME

7.4. How to restore audio and video synchronization in modern MAME.

In the previous chapters, we have followed the evolution of MAME's main video features, and how certain changes introduced in their implementation have lead us to a situation of compromise, to say the least, even if workarounds can be found in some cases.

There are several fronts where a certain degree of progress can definitely be achieved. Some of the features that are already being implemented by alternative builds of MAME, like system independent integer scaling or multithreaded video management, represent a tangible improvement and a clean solution for some of the issues we have discussed.

But, in our opinion, the most important single feature, that should be addressed at some point, is the broken syncrefresh functionality. Restoring a properly working syncrefresh feature is fundamental, for several reasons:

- It will provide a single option (instead an obscure combination of options) to use the video card as the master timing source.
- Things will work as advertised in the documentation.
- The use of triple buffering will become unnecessary for Direct Draw, eliminating its associated input lag.

We are going to demonstrate how it is possible to re-implement this feature, just like it worked for older versions of MAME, and we will take a further step forward, by making audio and video work together in perfect synchronization. The beauty of this is that we won’t be adding any uncertain hack at all; everything is achieved by using the gears that are already built in MAME. At the end of the chapter, you will find the complete source code for the patch.

The first thing that we have to do is, in appearance, a very trivial change in the source. However, it is responsible for the bulk of the patch code. We need to move the –syncrefresh option definition from the OSD layer into the core layer of the emulator. This deserves a brief explanation.

MAME is structured in several layers. Simplified, there is a core layer, that embeds the emulator itself, and then there are several OSD layers. OSD stands for “operating system dependent”, because this is the layer that deals with the different implementations for each specific operating system, like Windows or Linux. The core layer would be the piano nobile of the emulator's building, and it is common to all platforms. On the ground level, we have the OSD layer, dealing with the mundane world of operating system's bureaucracy. Each layer has its own set of options the user can play with. The relevant point to understand here is this: the core layer of the emulator is unaware of the options set on the OSD layer.

At some point of MAME’s evolution, it was decided that the right place for the syncrefresh feature was down, with the service. One might argue that this decision makes sense, because, after all, synchronizing with the refresh involves waiting for the vertical retrace, which is something definitely operating system specific. And here is where the conceptual error lies: synchronizing with the refresh and waiting for v-sync are related but completely separate things. The syncrefresh feature must be in the very heart of MAME, telling the emulator what to use as the master timing source: either the CPU or the video card. Its role is to control the behaviour of the throttling mechanism, but it has nothing to do with how the vertical retrace is read from the real hardware. Then, the particular implementation of waiting for v-sync can stay in the OSD layer, doing its job as usual.

Due to the way things are now, the syncrefresh feature is foreign to MAME’s core. This design keeps the throttling mechanism out of the retrace discipline. We need to revert this situation by moving the syncrefresh feature back to where it belongs. This is quite a simple change, however it requires patching several files (see the complete patch for detais):

src/osd/windows/winmain.h
src/osd/windows/winmain.c
src/emu/emuopts.h
src/emu/emuopts.c
src/emu/video.h
src/emu/video.c

Once we have an operative -syncrefresh option inside MAME’s core, we can focus on the patch itself. The key is to modify the video_manager::update_throttle method inside src/emu/video.c, which is responsible for the throttling mechanism, and just check if -syncrefresh is enabled, and if so, just exit:

    // if we're only syncing to the refresh, bail now
    if (m_syncrefresh)
        return;

So simple. This restores the syncrefresh full functionality. The funny part is these two lines are actually c-o-p-i-e-d from MAME pre-v0.114 source! Yes, this was included in the throttling function back when syncrefresh still worked. The reason why this was removed remains a mystery.

Now, although it’s a relief to have a reliable syncrefresh feature again, which ensures totally glitch-free video, you’ll probably remember from the previous chapters that the sound code in MAME has a mind of its own. So even with the syncrefresh option enabled, it will insist on emulating the sound at the pre-defined speed, regardless of the actual emulation speed. This will go unnoticed as far as the refresh of the video card is very close to the original, but it’s definitely there. Wouldn’t it make much more sense that both audio and video worked hand by hand at the same speed when the syncrefresh option is enabled? We whole-heartedly believe the answer is yes.

Fortunately for us, MAME already has the required mechanisms to achieve this in a painless way. The sound code is indeed prepared to accept a speed factor that is applied to the final mix (this is the principle behind the –speed option). We only need to make use of this internal speed factor, by making it aware of the current speed, as imposed by the video card. A straightforward approach for this is to use the video_manager::recompute_speed method, still in src/emu/video.c, adding this:

        if (m_syncrefresh && m_throttle)
            m_speed = m_speed_percent * 1000;

That’s all! Now the audio will be forced to be sampled at the emulation speed, whatever it is, and being the emulation speed the result of the refresh, this means that video and audio will be perfectly synchronized. Of course, this also means that if the CPU can’t keep the emulation at the required speed, the sound will reflect the slowdowns as pitch variations, like a vynil player does. But, isn’t it much more sincere than letting the sound stutter?

Once this patch is applied, the correct MAME configuration is the following:

mame rtype  –video ddraw
                  –switchres
                  –resolution 384x256@55
                  –nohwstretch
                  –syncrefresh

mame rtype  –video d3d
                  –switchres
                  –resolution 384x256@55
                  –nofilter
                  –syncrefresh

A couple of notes here. First, now the –throttle option is assumed to be enabled all the time. In fact, disabling –throttle (for instance, by pressing F10), results in MAME going full speed, as it is supposed to be. So now, the syncrefresh functionality is conditioned to having the –throttle option enabled, which makes all sense. Second, the –multithreading option can now be safely enabled or disabled without affecting the syncrefresh functionality.

There are more sophisticated changes that could be done to the source code in order to improve several features, as the ones being done by the derivative builds Cab MAME and Groovy MAME. But the one described here is probably the most simple but still effective possible change, which, at the same time, respects the existing design without breaking anything.

The source code for the syncrefresh fix patch (based on MAME v0.148):

diff -Nrup src/emu/emuopts.c src/emu/emuopts.c
--- src/emu/emuopts.c    2013-01-11 08:32:46.000000000 +0100
+++ src/emu/emuopts.c    2013-03-03 21:26:50.000000000 +0100
@@ -103,6 +103,7 @@ const options_entry emu_options::s_optio
     { OPTION_FRAMESKIP ";fs(0-10)",                      "0",         OPTION_INTEGER,    "set frameskip to fixed value, 0-10 (autoframeskip must be disabled)" },
     { OPTION_SECONDS_TO_RUN ";str",                      "0",         OPTION_INTEGER,    "number of emulated seconds to run before automatically exiting" },
     { OPTION_THROTTLE,                                   "1",         OPTION_BOOLEAN,    "enable throttling to keep game running in sync with real time" },
+    { OPTION_SYNCREFRESH ";srf",                         "0",         OPTION_BOOLEAN,    "enable using the start of VBLANK for throttling instead of the game time" 

},
     { OPTION_SLEEP,                                      "1",         OPTION_BOOLEAN,    "enable sleeping, which gives time back to other applications when idle" 

},
     { OPTION_SPEED "(0.01-100)",                         "1.0",       OPTION_FLOAT,      "controls the speed of gameplay, relative to realtime; smaller numbers are 

slower" },
     { OPTION_REFRESHSPEED ";rs",                         "0",         OPTION_BOOLEAN,    "automatically adjusts the speed of gameplay to keep the refresh rate 

lower than the screen" },
diff -Nrup src/emu/emuopts.h src/emu/emuopts.h
--- src/emu/emuopts.h    2013-01-11 08:32:46.000000000 +0100
+++ src/emu/emuopts.h    2013-03-03 21:26:48.000000000 +0100
@@ -114,6 +114,7 @@ enum
 #define OPTION_FRAMESKIP            "frameskip"
 #define OPTION_SECONDS_TO_RUN       "seconds_to_run"
 #define OPTION_THROTTLE             "throttle"
+#define OPTION_SYNCREFRESH            "syncrefresh"
 #define OPTION_SLEEP                "sleep"
 #define OPTION_SPEED                "speed"
 #define OPTION_REFRESHSPEED         "refreshspeed"
@@ -270,6 +271,7 @@ public:
     int frameskip() const { return int_value(OPTION_FRAMESKIP); }
     int seconds_to_run() const { return int_value(OPTION_SECONDS_TO_RUN); }
     bool throttle() const { return bool_value(OPTION_THROTTLE); }
+    bool sync_refresh() const { return bool_value(OPTION_SYNCREFRESH); }
     bool sleep() const { return bool_value(OPTION_SLEEP); }
     float speed() const { return float_value(OPTION_SPEED); }
     bool refresh_speed() const { return bool_value(OPTION_REFRESHSPEED); }
diff -Nrup src/emu/video.c src/emu/video.c
--- src/emu/video.c    2013-01-11 08:32:46.000000000 +0100
+++ src/emu/video.c    2013-03-03 22:18:06.000000000 +0100
@@ -115,6 +115,7 @@ video_manager::video_manager(running_mac
         m_overall_emutime(attotime::zero),
         m_overall_valid_counter(0),
         m_throttle(machine.options().throttle()),
+        m_syncrefresh(machine.options().sync_refresh()),
         m_fastforward(false),
         m_seconds_to_run(machine.options().seconds_to_run()),
         m_auto_frameskip(machine.options().auto_frameskip()),
@@ -735,6 +736,10 @@ void video_manager::update_throttle(atto
         3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, 4,5,5,6,5,6,6,7, 5,6,6,7,6,7,7,8
     };
 
+    // if we're only syncing to the refresh, bail now
+    if (m_syncrefresh)
+        return;
+
     // outer scope so we can break out in case of a resync
     while (1)
     {
@@ -1008,6 +1013,9 @@ void video_manager::recompute_speed(atto
         osd_ticks_t tps = osd_ticks_per_second();
         m_speed_percent = delta_emutime.as_double() * (double)tps / (double)delta_realtime;
 
+        if (m_syncrefresh && m_throttle)
+            m_speed = m_speed_percent * 1000;
+
         // remember the last times
         m_speed_last_realtime = realtime;
         m_speed_last_emutime = emutime;
diff -Nrup src/emu/video.h src/emu/video.h
--- src/emu/video.h    2013-01-11 08:32:46.000000000 +0100
+++ src/emu/video.h    2013-03-03 22:25:07.000000000 +0100
@@ -91,6 +91,7 @@ public:
     int speed_factor() const { return m_speed; }
     int frameskip() const { return m_auto_frameskip ? -1 : m_frameskip_level; }
     bool throttled() const { return m_throttle; }
+    bool sync_refresh() const { return m_syncrefresh; }
     bool fastforward() const { return m_fastforward; }
     bool is_recording() const { return (m_mngfile != NULL || m_avifile != NULL); }
 
@@ -168,6 +169,7 @@ private:
 
     // configuration
     bool                m_throttle;                 // flag: TRUE if we're currently throttled
+    bool                m_syncrefresh;              // flag: TRUE if we're currently refresh-synced
     bool                m_fastforward;              // flag: TRUE if we're currently fast-forwarding
     UINT32              m_seconds_to_run;           // number of seconds to run before quitting
     bool                m_auto_frameskip;           // flag: TRUE if we're automatically frameskipping
diff -Nrup src/osd/windows/video.c src/osd/windows/video.c
--- src/osd/windows/video.c    2011-12-15 15:10:46.000000000 +0100
+++ src/osd/windows/video.c    2013-03-03 23:39:40.000000000 +0100
@@ -424,7 +424,7 @@ static void extract_video_config(running
         video_config.mode = VIDEO_MODE_GDI;
     }
     video_config.waitvsync     = options.wait_vsync();
-    video_config.syncrefresh   = options.sync_refresh();
+    video_config.syncrefresh   = machine.options().sync_refresh();
     video_config.triplebuf     = options.triple_buffer();
     video_config.switchres     = options.switch_res();
 
diff -Nrup src/osd/windows/winmain.c src/osd/windows/winmain.c
--- src/osd/windows/winmain.c    2013-01-11 08:32:46.000000000 +0100
+++ src/osd/windows/winmain.c    2013-03-03 22:43:58.000000000 +0100
@@ -319,7 +319,6 @@ const options_entry windows_options::s_o
     { WINOPTION_KEEPASPECT ";ka",                     "1",        OPTION_BOOLEAN,    "constrain to the proper aspect ratio" },
     { WINOPTION_PRESCALE,                             "1",        OPTION_INTEGER,    "scale screen rendering by this amount in software" },
     { WINOPTION_WAITVSYNC ";vs",                      "0",        OPTION_BOOLEAN,    "enable waiting for the start of VBLANK before flipping screens; reduces 

tearing effects" },
-    { WINOPTION_SYNCREFRESH ";srf",                   "0",        OPTION_BOOLEAN,    "enable using the start of VBLANK for throttling instead of the game time" },
     { WINOPTION_MENU,                                 "0",        OPTION_BOOLEAN,    "enable menu bar if available by UI implementation" },
 
     // DirectDraw-specific options
diff -Nrup src/osd/windows/winmain.h src/osd/windows/winmain.h
--- src/osd/windows/winmain.h    2013-01-11 08:32:46.000000000 +0100
+++ src/osd/windows/winmain.h    2013-03-03 22:43:55.000000000 +0100
@@ -68,7 +68,6 @@
 #define WINOPTION_KEEPASPECT            "keepaspect"
 #define WINOPTION_PRESCALE              "prescale"
 #define WINOPTION_WAITVSYNC             "waitvsync"
-#define WINOPTION_SYNCREFRESH           "syncrefresh"
 #define WINOPTION_MENU                  "menu"
 
 // DirectDraw-specific options
@@ -183,7 +182,6 @@ public:
     bool keep_aspect() const { return bool_value(WINOPTION_KEEPASPECT); }
     int prescale() const { return int_value(WINOPTION_PRESCALE); }
     bool wait_vsync() const { return bool_value(WINOPTION_WAITVSYNC); }
-    bool sync_refresh() const { return bool_value(WINOPTION_SYNCREFRESH); }
     bool menu() const { return bool_value(WINOPTION_MENU); }
 
     // DirectDraw-specific options