User:Combuster/UDI Graphics
While this may be hijacking of the OS development wiki for now, its imo the best place for collaborative editing and public scrutiny.
Long story short, the last revival of UDI led to a movement that would be trying to make it viable - initially with hobbyist drivers, but also for a larger audience when it does pick up. I was invited (and I agreed) to draft a specification for an UDI sub-protocol, targeted at graphics drivers.
Please add comments - they can be used for error checking now, and as a justification later.
Video card basics
There is only one "standard" video analogy that is in common use - the framebuffer. It is not alone however, there are still many devices that do not comprise a square grid of pixels, like text LED/LCD displays. And even when you do have a square array of pixels, it might not be possible to address and control each pixel individually and freely. The latter is especially the case in the default video mode during boot of desktop hardware - text mode. Also, in the console market tile-attribute engines are not uncommon - in fact they are the bigger brothers which have sprite engines. They have a certain amount of freedom in the location of images compared to the fixed grid of a tile-only engine, but lack the freedom that is provided by framebuffer engines.
The specification hopes to cover the vast majority of graphics hardware, without requiring too much special casing.
Connectors
Graphics hardware serve no purpose if the result is not available to the user. Any device should thus have at least one output. On a desktop video card, the output is connected through a connector on the rear, VGA was the standard, DVI is the current. The choice is however not limited to these two. Industry video standards include PAL and NTSC, which are used in TV, and their data is transmitted in a signal of various forms: RF, composite, component, HDMI amongst others. A device has a number of outputs, each with a transfer type attached. Some connectors (DVI/SCART) allow video data to be transferred in multiple formats. That implies there is no 1-on-1 mapping between connector types and signal types. When enumerating the outputs, the information returned should contain the type of connector, and the signal used over the connector. Depending on the signal, sideband data may also be transferred - it may be DDC for VGA (with the purpose of identifying and manipulating the attached monitor), it may be audio (for HDMI, DVI, SCART, RF), it may be USB (DVI). The interface should thus support various combinations of side channels. Some video-related side channels should be included in the graphics specification.
A graphics device has one or more outputs, each has a connector type (which is constant over time), and a signal type (which is variable). The connectors to include are:
- VGA
- DVI
- S-video
- Component
- Composite
- RF
- SCART
- HDMI
- Hidden (when there is no physical connector, such as laptop screens)
The signals they carry:
- RGBHV (VGA, DVI-A)
- RGBS
- RGsB
- YPbPr (which is the common "Component" signal)
- DVI-D
- Composite YUV (PAL)
- Composite YIQ (NTSC)
- Separated Y and UV (PAL S-Video)
- Separated Y and IQ (NTSC S-Video)
- HDMI
- Integrated (when the display device is inseparable from the graphics controller, including LED displays and emulated displays)
- Internal (proprietary)
Based on the signal and connector, software using the device may base choices such as resolution. Software may also change the signal used on each connector when applicable. Important to note is that this excludes the capabilities of the receiving end.
A connector can be queried for the signals it supports, and a signal can be selected. To implement specifics of a signal, a signal-specific parameter block containing the parameters can be passed. When a new signal is chosen for a connector, a default configuration for that signal is assumed to ease configuration. This configuration is designed to be as widely supported as possible.
For VGA or DVI-D signals this shall be 640 pixels by 480 progressive lines at 60Hz according to the original VGA standard. The configuration includes:
- sync, frontportch, backporch, left overscan, right overscan and active display in both dimensions
- status of synchronisation pulses (positive, negative, disabled)
- the presence of a half interleaving line
- the pixel clock
For YUV signals, the signal shall match a PAL display. For YIQ signals, the signal shall match an NTSC display. Integrated protocols assume a implementation defined resolution. The only configuration option is the output resolution.
Connectors are to be listed according to probability of use. This ordering must be constant across instantiations of the driver in order to reliably track connected peripherals. Drivers may pose limitations on the ranges of configuration values that they deem valid.
Video sources
Connectors require an input - colour data. This data is generated from the internal state of the graphics controller. This state may be raw pixel data (in framebuffer engines), or it may be references to objects (in tile engines). An important observation is that most real instances share a integrated form of image generation - layering. The most obvious form in tile engines is the sprite layer present in 3rd generation consoles, where the background consists of tiled objects, and where another set of objects is positioned above it. This layering is also found in framebuffer devices, most obviously in the form of overlays, however the mouse pointer is in essence a single hardware-accelerated fixed-dimension sprite.
Hardware may have one or more display engines. Engines are connected together into display pipes (independent of the actual method of implementation, which usually isn't pipe-based). Display pipes may be wired to one or more connectors. A connector may however not be linked to more than one display pipe. The rules that determine whether a pipe may be connected or not is hardware-specific, and thus, implementation defined. A configuration that was once accepted must however be accepted in all future cases.
Display pipes can be edited if the hardware supports it. In most cases however, the layout of the pipe is fixed, and certain components can only be turned on and off. Some properties of the pipe may only be modified when the pipe is not connected. The properties of any attached connector may also be locked by the connection.
Pipe Layouts
Each engine in the pipe combines its inputs, and the local state into the pipe's output. The result is a tree of operations, with each node applying its local data to the data it receives. It may choose to overwrite the data, it may choose to pass through the data unmodified, or it may alter the data in some form. In all cases, the information about the pipe and the engines must be complete enough so that the result can be computed exactly. This has to be the case, because the information seen on screen should match the expectations of the programmer.
State manipulation
In order to render information to the screen, the graphics controller requires that its internal state is manipulated. In some cases, this state can only be modified directly by outside influences (like all older graphics cards do), more recent hardware have integrated processing units that can manipulate the state of the device with little to no intervention of the host system. In some configurations, direct access is very limited and the state can only be manipulated via operations on the graphics controller.
To level the playing field, a framebuffer configuration requires a SetPixel command, as this represents the basic unit. However, this approach has significant overhead and should be avoided unless there is no viable alternative.
When direct access is supported and the buffer has an identity mapping to the pixel or tile data, the device may expose a function that returns the physical offset and size of the framebuffer. If there is no such mapping, the function should yield an error.
Reference
You can use this as a basis for creating your UDI implementation. Details follow...
All drivers must include the the following declaration in their udiprops.txt:
requires udi_gfx 0x101
Each driver source code file must contain the following:
#define UDI_GFX_VERSION 0x101
#include <udi.h> // Must come before udi_gfx.h
#include <udi_gfx.h> // Contains the UDI GFX interfaces
Versions that have the same major version number but a larger minor number are strict supersets of those with smaller minor numbers.
State changes
typedef udi_ubit8_t udi_gfx_state_op_t;
#define UDI_GFX_OP_WRITE 2 // Write a state property
#define UDI_GFX_OP_READ 1 // Read a state property
#define UDI_GFX_TARGET_CONNECTOR (0 << 2) // The target is an output connector
#define UDI_GFX_TARGET_PIPE (1 << 2) // The target is a component in a pipeline
typedef struct {
udi_cb_t *gcb; // generic control block
udi_ubit_32_t unit; // the unit to address
udi_ubit_32_t index; // the property's index
udi_ubit_32_t value; // the property's value
udi_gfx_state_op_t operation; // general operation parameters
} udi_gfx_state_req_cb_t;
The following properties can be used:
#define UDI_GFX_PROP_ENABLE 0
- The connector or engine is enabled (nonzero) or disabled (zero). A disabled engine forwards all data from the previous stage unmodified. A disabled connector does not send any data over the connector. Drivers may power down the physical counterparts of disabled components to preserve power, and users should expect delays when enabling connectors or components representing framebuffers. Disabling is however not recommended for sprite layers, which may repeatedly be enabled and disabled. A disabled component can still have its state changed, and the driver must complete all commands and other state changes as expected, regardless of disabled or power state. The valid ranges reported for this property may be 1 (always enabled) or 0-1 (either enabled or disabled).
#define UDI_GFX_PROP_INPUT 1
- Points to the engine that is processed before this unit. In the case of a connector, it points to the last engine in a pipeline, and each engine points to the next engine in the sequence. A value of -1 indicates a source that only yields black pixels. Implementations must not allow cyclic structures. Changing this value may reallocate resources, and engines that are no longer referenced may lose their data (but not their state) when it is not part of any pipeline. If preservation is required, the ENABLE state should be used instead. Valid ranges includes one or more from the list of engines and -1 combined. In most cases, this property can not be modified.
#define UDI_GFX_PROP_WIDTH 2
- Contains the amount of pixels in the horizontal direction. For connectors, this is the amount of data pixels rendered horizontally. For engines, this is the width in pixels of the image. Pixels requested from an engine outside the range (0..width-1) are defined according to the CLIP property. In some cases, hardware may support only fixed combinations of width and height. In such cases, changing the width will also change the height to a corresponding valid number. Valid ranges include any values strictly above zero. For connectors, expect large continuous ranges, large ranges with a certain modulus, a limited number of fixed values, or a constant value.
#define UDI_GFX_PROP_HEIGHT 3
- Contains the amount of pixels in the vertical direction. Functions similar to the width property, but changing it will not alter the width property, and it's range at any time contains the valid range for the currently selected width.
#define UDI_GFX_PROP_CLIP 4
- For engines, contains the behaviour for pixels requested outside the width and height of the engine. Can be either 0 (pass from next stage), 1 (the coordinates are wrapped modulus the height and width), 2 (the coordinates overflow onto the next scanline horizontally, and wrap vertically), 3 (the coordinates overflow onto the next column vertically, and wrap horizontally). Valid ranges contain one or more of these options. For overlays and sprites, a value 0 is common. For framebuffers, 2 is the most common value. For connectors, this property is always 0
#define UDI_GFX_PROP_UNIT_WIDTH 5
- For engines, represents the width in pixels for each entry in the buffer. For framebuffers, this is normally either 1 (1 pixel per memory entry) or two (pixel doubled). For tile engines, this is the width of each individual tile. As an example, a driver for a VGA compatible card supports modes that either are 1 pixel wide (4-bit color), 2 pixels wide (8-bit color; data of two pixels are used to generate one higher-depth pixel), 8 and 9 (in alphanumeric mode, where each character is 8 wide, with an optional 9th line). Attempts to change this number may change the width and height properties when they must be a multiple of the unit sizes. UNIT_HEIGHT may also be changed according to hardware restrictions. Valid values include any positive number.
#define UDI_GFX_PROP_UNIT_HEIGHT 6
- For engines, represents the height in pixels for each entry in the buffer. For framebuffers, this is normally either 1 (1 pixel per memory entry) or two (doublescanned). For tile engines, this is the height of each individual tile. See also UNIT_WIDTH. Valid values include any positive number.
#define UDI_GFX_PROP_TILESHEET 7
- For engines, represents a texture object that contains the tile data. This object can be used as the target of a glTexSubImage2D command, and can be selected for reading using the glReadBuffer command, for which it is a valid argument. It's height equals the UNIT_HEIGHT; its width equals UNIT_WIDTH times the maximal addressable tile. Multiple tilesheets may exist. Changing this state will cause the output to use the new tilesheet. A value of zero indicates that no tilesheet is present, and such a tilesheet can not be addressed by the format. Tilesheets which are not selected must be preserved during the lifetime of the pipeline. Tilesheets may be read-only, in part or in whole. Valid values are zero, or any pointers to unique tilesheets, they can't point to objects that might be returned by a glGenTextures command, or any other command that yields a hardware resource.
#define UDI_GFX_PROP_PALETTE 8
- For engines, represents a texture object that contains the palette information. This object can be used as the target of a glTexSubImage2D command, and can be selected for reading using the glReadBuffer command, for which it is a valid argument. In contrast to common palettes, not all entries are required to be of the same format. Valid values are any pointers to unique palettes, they can't point to objects that might be returned by a glGenTextures command, or any other command that yields a hardware resource. Palettes can be read-write or read-only, partially or in whole. Valid values are zero, or any pointers to unique tilesheets, they can't point to objects that might be returned by a glGenTextures command, or any other command that yields a hardware resource.
#define UDI_GFX_PROP_BUFFER 9
- For engines, represents the texture that contains the currently visible data. This texture is often different from the currently active framebuffer, which is the target of rendering calls. It's value is arbitrary, other than that different engines may point to the same buffer, in which case it is a shared resource. This is often the case where multiple pipelines are active. This state is commonly read-only. All numbers are valid values for this state.
Metalanguage definition
typedef void (udi_gfx_command_cb_t *) udi_gfx_command_op_t;
typedef void (udi_gfx_request_cb_t *) udi_gfx_request_op_t;
typedef void (udi_gfx_state_cb_t *) udi_gfx_state_req_op_t;
typedef void (udi_gfx_range_cb_t *) udi_gfx_get_range_op_t;
// the metalanguage
typedef const struct {
udi_gfx_command_op_t *gfx_command_op; // send commands to change data
udi_gfx_request_op_t *gfx_request_op; // send commands to read data
udi_gfx_state_req_op_t *gfx_state_req_op; // set or get socket or pipe state
udi_gfx_get_range_op_t *gfx_get_range_op; // read valid socket and pipe values
} udi_gfx_device_ops_t;
See Also
- udi_gfx.h - nightly version