<< Newer | Article #173 | Older >> |
A Peek Inside the New Renderer (part 1)
The new rendering system that was introduced at the start of the 0.106 development cycle represents a whole new way of thinking about video in MAME. This article is the first in an attempt to explain what's really going on behind the scenes.
The new system is generally rather object oriented, even though it is still written in straight C. There are a number of fundamental rendering objects that are used to describe what gets rendered to the screen. We'll first go over the objects, and then talk about how it all comes together.
One concept to keep in mind while reading this article is that the rendering system serves as a bridge between the MAME core and the OS-dependent code (the OSD layer). There are some objects that are primarily outward facing (i.e., operated on primarily by the OSD layer), and some objects that are primarily inward facing (i.e., operated on by other core components).
The core outward-facing object in the new rendering system is the render_target. A render_target is an object that communicates to the core exactly how the OSD layer would like the final video to look. The OSD layer is responsible at startup time (in the osd_init callback) for allocating one or more render_targets. In the most common case, there will be just one render_target allocated (though in the Windows code if you request more than one screen, then one render_target will be allocated per screen).
Each render_target contains information about the width, height, orientation, and pixel aspect ratio of the destination. For example, if you were running full screen on a 640x480 display, the OSD layer would allocate a render target and configure it with a width of 640, a height of 480, a standard orientation (no rotation), and a pixel aspect ratio of 1.0 (assuming your 640x480 was being displayed on a 4:3 screen). This information is used later by the MAME core when it is time to actually provide the data necessary to draw the final output.
On the opposite side of the fence, the core inward-facing object in the new system is the render_container. A render_container is an object that contains a list of items (lines, quads, or characters) to draw. There can be any number of render_containers in the system, but the current system simply allocates one render_container per screen in the game, and an extra render_container for the user interface. Code in the core is responsible for adding items to each render_container. For example, at video update time, the video.c module adds the bitmap for each screen to the render_container for that screen. In contrast, vector games don't have a bitmap, and so their update routines add a bunch of lines to the render_container to describe what should be displayed on its screen.
In addition to holding a list of items to draw, each render_container also holds several additional rendering parameters, including brightness, contrast, and gamma controls, as well as scaling and offsetting functions. When applied to screen-specific render_containers, these controls give most of the common adjustments present on an actual arcade screen. These controls are now (as of MAME 0.106u11) all available from the on-screen display menu, and work independently for each screen in the system.
So, given what's been described so far, there are two separate sets of objects for rendering. The first set is the collection of render_targets, which are allocated and managed by the OSD layer. The second set is the collection of render_containers, which are allocated and managed by the core. The obvious question is: how to these two sets of objects map to each other? That is, how do we get the contents of the render_containers to show up on the render_targets?
The answer is the layout_view. Each render_target object has an associated layout_view, which describes exactly how the video system should position and orient all the elements that go to make up the final video output. These elements can be items from one of four classes: backdrops, overlays, bezels, or screens. The first three types of elements are artwork pieces which are static, preloaded, and pre-scaled graphics that are intended to be intermixed with the screen elements. The screen elements specify the location and orientation of the render_container that holds the screen items.
There are several important implications of this setup. First, each render_target has its own layout_view, completely independent of any other render_targets. This means that you can have multiple render_targets active at a time, each with the same layout_view, or completely different layout_views. Second, there is only one render_container per screen in the system, but multiple layout_views can reference each screen. In fact, a single layout_view can even include multiple references to the same screen; this is how the "Cocktail" view works in the current system.
Where do layout_views come from? From layout (.lay) files. Layout files are fundamentally just XML descriptions of layout_views, and each layout file can contain descriptions of multiple layout_views. At startup, MAME will attempt to load layout files from multiple sources. All layout files that are found are effectively concatenated together into one big list of layout_views, which you can dynamically select at runtime in the new "Video Options" menu.
So, in summary: at startup, the rendering system creates render_containers for each game screen, and the OSD layer creates render_targets for each output screen. When a render_target is created, the selected layout files are loaded in order to create a list of layout_views. These layout_views describe how to intermix the render_containers for each game screen with artwork and other adornments to produce the final output in the render_target.
In the next part of this article, I'll talk a bit about how the graphics actually get assembled and what the mixing rules are.