Articles posted July 2006 Older >>

Side-Effects

The problem with a project as huge as MAME is that you really can't change anything without breaking something else. Such is the case with the new feature I added late in the 0.106 cycle that lets you control the horizontal/vertical positioning and stretching of the screen. This feature is intended so that MAME can more accurately output video that corresponds to the real arcade screen.

On many real arcade PCBs (and especially on older PCBs), the video system actually generates signals beyond the immediate area of the screen where the gameplay is. What would happen is that arcade operators would use the horizontal and vertical scaling/positioning knobs to expand the gameplay area of the screen to fill the screen and thus cropping out anything that wasn't intended to be seen.

The way MAME has handled this in the past is to have a screen bitmap where pixels are placed (this is equivalent to the video signal), and then game drivers would provide a "visible area", which is the cropped area where the gameplay is focused. This generally works well, but doesn't allow you (the operator) to control the positioning and scaling of the image. When I decided to add these controls, it meant that I needed to dynamically adjust this "visible area" to display a different subset of the pixels in the screen bitmap.

This sounded pretty straightforward. But it appears to have some side effects.

The first issue is that some games are showing a pixel or two worth of garbage at the edges of the screen. If you bring up the sliders in the UI and adjust the horizontal or vertical scaling, you should see that this garbage is actually extra pixels that are right on the edge of the screen. The reason they are visible at all is thanks to bilinear filtering on your video card. Even though MAME is correctly computing how to position the screen so that those pixels aren't visible, the bilinear filter is grabbing color data from those pixels as it scales up the image. You can work around this by simply adjusting the horizontal/vertical scale and position controls until those pixels are gone.

The second issue is that some games got really slow as a result. Previously, MAME used to only copy the pixels from the visible area to your video card for rendering. But in order to have access to all of the video signal information, the current code will copy all of the screen bitmap pixels to your video card. For most games, the screen bitmap is only slightly larger than the visible area, so this didn't really hurt anything. But several drivers have huge screen bitmaps and small visible areas. robokid, for an extreme example, has a 2048x512 screen bitmap even though it is only displaying 256x192 pixels. Other games with this problem include games that switch resolutions, because the screen bitmap has to be big enough to accommodate the largest resolution that will ever be switched to, which is often much larger than the actual resolutions used in the game.

I'm trying to come up with a non-gross way of fixing both of these problems. The first problem probably needs the introduction of the concept of blanking areas. On a lot of actual PCBs, you won't actually see this garbage because they have set up a blanking signal that turns off the video signal after a certain point. The visible area used to sort of account for that, but didn't allow for the possibility of adjustment. By having support for a separate blanking area in addition to the visible area, some of these problems can be eliminated.

The second problem is more severe and in the end more straightforward to fix. The core is already computing the effective visible area of the screen and only asking the driver's video update to draw just those pixels. It just needs a way of communicating that information to the rendering code so that it doesn't waste time copying pixel data that will never be seen.

Updated Artwork for MAME 0.107

Since nobody has stepped up to do this, I've set up a temporary place on this site to handle high quality artwork that is compatible with MAME 0.107. I've just put up a few of the most requested pieces, and will add a few more shortly. I am not going to bother converting crap artwork (pretty much anything below 1000x1000), so only the good stuff will appear here. I have also tweaked the positioning of some of the artwork to be more accurate. For example, the Space Invaders backdrop is much more accurately positioned now, and the Mario Bros. bezel has been backed off enough that you can actually see most of the screen.

A Peek Inside the New Renderer (part 2)

In Part 1 of this article, I explained all about the three core objects in the new renderer: the render_target, the render_container, and the layout_view. In this part, I'll tackle how the final collection of things to render is arrived at, based on all of this information.

As I mentioned before, the layout_view is essentially a description of how to position the various render_containers amongst external artwork elements within the confines of the render_target. In order to do this, we need to introduce the concept of an object_transform. In a fully 3D world, a transformation is usually a matrix, and can describe rotation, translation, and scaling. Although the new renderer handles all three parts of a 3D transformation, it is still strictly a 2D engine. So to keep things simple, an object_transform describes a simple 2D translation (2 parameters: X and Y offset), a simple 2D scaling (2 parameters: X and Y scaling), and basic rotation (orientation described via X flip, Y flip, and X/Y swap). In addition, an object_transform also contains a color factor (4 parameters: red, green, blue, and alpha).

In order to assemble the list of items to render, the OSD layer calls render_target_get_primitive_list(). This is different compared to the old way of operating.
In the old model, when MAME had something that needed to be drawn, it called to the OS-dependent code (OSD layer) and handed it everything it needed to update the video. In the new system, it is the OSD layer who is responsible for calling back to the renderer for the list of items to render for a target. Usually this is done in response to the more generic osd_update() call which is used as a signal to indicate that something interesting has happened.

Anyway, render_target_get_primitive_list() returns a list of render_primitive objects, which constitute a description of what to render. Each render_primitive object specifies a type, a set of coordinates, a color, and a set of flags, along with some additional type-specific data. At this time there are only two types of primitives that will ever be returned: lines and quads.

A line primitive is simply two points that should be rendered with a line drawn between them, using the color, alpha, and width specified. Points can be drawn by specifying a line with equal start and end points. Lines are used mostly for drawing the user interface and for vector games (though that may change in the future).

A quad primitive is also specified by two points: a top, left coordinate and a bottom, right coordinate. This limits MAME to specifying rectilinear quads, so there is currently no way to do anything fancy or 3D with the system. (Believe it or not, this is intentional.) Quads can be either textured or non-textured. Non-textured quads are rendered as a solid color using the specified color and alpha values. Textured quads are rendered using the specified texture, with the color and alpha values providing modulation of the texture data.

Common to both primitives is a set of rendering flags. These flags control how the primitive is to be blended against previously-rendered graphics on the screen, among other details. The important thing to keep in mind is that the primitives are provided in back-to-front rendering order. This means that the system that does the final rendering should turn off all Z buffering and simply render each primitive in order. Another interesting tidbit is that the screen does not need to be explicitly cleared before drawing because the primitive list contains non-textured quads that erase any relevant areas of the screen before drawing them.

The easiest way to understand how it all comes together is to walk through an example. Let's say you're running Space Invaders, and you have selected a layout_view that includes a backdrop, an overlay, and a bezel, as well as the screen. Let's also say that you've configured MAME to draw a special effect overlay on top of the screen graphics, just to throw everything in together. Now what?

Well, internally, MAME has examined the layout_view and grouped all of its elements into one of four categories: backdrop, overlay, screen, and bezel. Once that is done, it then starts with an empty list of primitives, and goes through each layer one at a time, adding primitives to the end of the list. The trick is that ordering is very important to get all of the effects to mesh together.

The first thing that gets drawn is the screen layer. I know you're asking, what? Why aren't you drawing the backdrop layer first? It's in back! Well, the screen layer needs to be rendered first in order to apply overlay effects before the screens are added to the backdrop; otherwise, you get ugly results as the overlay effects are applied to the backdrop graphics as well.

In order to draw the screen layer, the renderer loops over all the screen elements that were specified in the layout, and copies the render_container contents for that screen to the end of the primitive list. A couple of notes about this. First, what does a render_container actually contain? Well, it contains objects that are called container_items. These items are kind of like primitives, but they are less primitive than the render_primitives that we've been looking at so far. Container_items can be lines, quads, or characters. Another important thing to understand about render_containers is that they have an internal coordinate system that goes from 0.0-1.0 along both axes. This is where the object_transforms that I mentioned above come in. Since a screen element in a layout_view can position the actual contents of the screen anywhere within the view, we need to know how to transform the container_items from their basic coordinate system to the final screen coordinates. To do this, we build an object_transform describing that mapping, and apply the object_transform to each line, quad, or character we encounter in the screen's render_container. Note that during this process, characters get translated into quad render_pritimives.

As container_items within a render_container are being converted into render_primitives, they are added to the end of current working list of primitives. Along the way, they are set to render with an additive blending mode. This means that the RGB values of the lines/quads/characters are added to the RGB values of what is already there. In most cases, what is already there is just black, so it doesn't make much of a difference.

Once each screen's render_container has been added to the working list, the special effect overlay is applied. This is simply a replicated copy of a texture that is rendered on top of the screen area using a multiplicative RGB blend. This means that the RGB values of the overlay texture are multiplied by the RGB values of what has already been rendered. This allows the overlay texture to control how much red, green, or blue from the final screen is actually shown.

Having added the screens' containers to the render list, the next step is to add the graphical overlays that are specified by the layout_view. These are simply bitmaps that are described by the layout XML, which I covered in a previous article. Like the effect overlays, these are rendered with multiplicative RGB blending. The easiest way to think about it is with vector games. Black and white vector games only drew white lines. White is R=G=B=1.0, while black is R=G=B=0.0. When you multiply the pixels of an overlay by these values, you will get black in the areas where nothing is drawn, and the overlay color in the areas where what was drawn.

Now that we have the screens and overlays assembled, it is time to add the backdrops. Backdrops and screen data are combined using additive RGB blending again. This is why you can draw the backdrops after the screens. If you remember in school, addition is commutative (a+b = b+a), so it doesn't matter which you draw first, and which you add on top of it. The main issue to be aware of is if there are overlapping backdrop elements, they will be added to each other. Right now, Golly Ghost uses overlapping backdrop elements, so as a workaround, if we find multiple backdrop elements, we draw the backdrop first and add the screens second.

Finally, we add the bezels. Bezels are drawn using a standard alpha blend, which means each pixel has an alpha component that controls how much of the previously rendered stuff shows through.

Now we have an ordered list of everything to draw. The OSD layer runs through this list and draws things in order, displaying the final result. And I think that's enough for one article. Thanks for hanging in there!

MAME 0.106u11

Okay, so there's a new release of MAME available. A bunch of new features are in this one, hopefully the last set of major functionality before we close down this cycle and get a real 0.107 release out there.

One thing I did slip in there was the ability to load an overlay file that gets applied on top of screens. I spent a lot of time at California Extreme this year staring up close at monitors to get a feeling for what the aperture masks look like. I've come up with a few that look pretty good when run at reasonably high resolutions. You can download them here and place them unzipped in the artwork directory. Use -effect aperture4x6 if you run at really high resolutions (2000x1500). I recommend -effect aperture2x4rb if you run at medium resolutions (1280x1024 or so). The other PNG files can be selected by just specifying their name after the -effect parameter.

If anyone manages to create nice masks, feel free to post links to them here. The masks are just standard PNG files. The RGB of each pixel is multiplied against the RGB values of the pixels of the screen, so you can just edit the masks in Photoshop or Paint or whatever you like.

California Extreme 2006

So I spent this past weekend checking out California Extreme. This year was the 10th year for the show, though I think I've only made it there for the last 7 or 8. Regardless, it was, as usual, a great time. Played a few good games of Mappy (barely missed cracking 150k), made my usual pilgrimage to In-n-Out Burger, played some rather challenging games of Elevator Action with the difficulty cranked waaay up, and checked out the usual collection of Atari prototypes, which included a couple of new ones this year, including Firebeast (coming soon to MAME) and the laserdisc version of Road Runner.

But the real highlight of the show, as usual, was the Atari panel, where a bunch of former Atari/Midway developers get up on stage and basically reminisce about the old days while answering questions from the audience. What was even cooler was this year Eugene Jarvis joined them. In fact, before the Atari panel, he basically spent an hour talking about the evolution of Robotron, which was just fascinating. He was asked all kinds of questions about the various bugs and their causes, and discussed the development of the game from start to finish.

Right at the end of the Atari panel, someone in the audience asked them what they thought of MAME.
Holding my breath for the responses (I know at least one or two of the ex-Atari guys are not supportive), I was completely stoked to get a hugely positive response from Eugene Jarvis saying that he loved what we were doing to preserve arcade history. Ed Rotberg also had some nice things to say about the work we do. If anything, that made my year right there.

After the panel, I got a chance to shake hands with Eugene and talk to him a little bit about the blitter timings in his games, especially Robotron, where everyone complained how it impacted gameplay, and Cruisin' USA, where the lack of blitter timings makes for a better framerate than the original. Eugene was really great to talk to and completely gracious in every way. I know he also got stopped for some interviews along the way, so hopefully other folks out there will get a chance to hear him speak.

I also had the pleasure to meet Dave Shepperd, former Atari/Midway tools and OS guru. Dave is just an amazing fount of information: if you get him started talking on a topic, he can go on at length with incredible detail. He worked for Atari from the late 70's and kept right on through the Warner buyout, the Midway buyout, and right up to the end, so he has a really broad perspective on what went on in the company throughout its history.

At the show itself they had the usual large assortment of games, though it was more video game-centric and less pinball-centric than past shows, from what I remember (this is good for me, as I'm not much of a pinball guy). The laserdisk game collection was smaller than previous years, which means they didn't have Cliffhanger (the only laserdisk game I really like). They did still have a decent set of laserdisk games, though I could have done without Thayer's Quest, which was right next to the Mappy and which just drones on and on and on with its incessant terrible computer generated speech synthesizer. I was seriously tempted to, ah, temporarily disable the machine, but chose to restrain my anger. :)

Apart from the Road Runner laserdisk prototype, the main other notable new game I hadn't seen before was Moonquake, which was the last Bally/Sente game that was never released. It runs on an Amiga 500 with ROM board, but there is still something mysterious about the game code that makes it not work in MAME (it appears to be very well encrypted, which is odd for a prototype). The game itself is sort of a slow-paced Q*Bert game set in space. Like most prototypes, you kind of realize why it was a prototype (i.e., it's not very good), but it was worth seeing.

All in all, a good show this year!