A basic 2D graphics card has to have three basic things:
- Some way to get data in
- Some way to store or generate an image
- Some way to get data to the screen
Looking at some traditional graphics systems we can see how these have often been done.
In the days of incredibly expensive memory some very clever tricks have been done to keep costs down. This typically means using very very small amounts of memory at the cost of increased CPU usage. For instance, on some early games consoles the image data was generated realtime by the game software itself, with the CPU knowing at all times just where on the screen the current scan line of the electron beam is, and generating just the graphic data needed for that.
As memory became cheaper, and demands for a better user interface became greater, the processing and generating of the image data moved out of the CPU and into the graphics chipset. So we get the more traditional environment we are more used to now.
A graphics card, primarily, needs somewhere to store the current image. Basically a block of memory. That memory could be memory mapped into the CPU's address space, or even be part of the CPU's main memory in some cases. But memory is needed however it's provided. How much memory very much depends on how the rest of the graphics system works.
The memory can basically be used in two different ways, depending on how much there is available and what you want to display. The two modes are Direct and Interpreted framebuffering.
Direct framebuffering is the most simple to understand. You have a block of memory where each address within it represents the colour of a point on the screen. The data is split into the three channels (for RGB) or treated as a single brightness value (monochrome) and fed into DACs to generate the video waveform. Pretty simple, but needs lots of memory.
In the time while memory was still in the process of getting cheaper interpreted framebuffering was more in use.
In this mode each address in the memory represents something other than a single pixel. It may be that it represents a cluster of pixels, with each value being used to look up the colour to use in a "CLUT" (Colour Look-Up Table). The PC's old CGA graphics mode worked like this, where you had the ability to display 4 colour pixels (including black) with a choice of 2 palettes to choose from. Those palettes were actually hard wired to give you Black/Red/Green/Yellow or Black/Magenta/Cyan/White (basically turning the blue channel on or off for each palette), but each pixel would then only take 2 bits, enabling you to fit 4 times as much video data into memory.
Another popular method for interpreted framebuffering was the ROM character set. In this mode each address in the memory represents a single character (often with attributes like colour or brightness) on the screen. The graphics engine then takes each pixel of the screen in turn, works out which character cell is under that pixel, gets the character from that cell, then looks up the colour of the pixel for that location within the character from ROM. This meant that you could have a much higher resolution text display than graphics display - ideal for word processing and similar applications where all you need is text.
For example, an 80x24 text display would use just bytes, but for the same display using pure graphics would take (say with 8x8 fonts) 15360 bytes.
Once memory got cheap, and computers started having hundreds of KB, or even megabytes of RAM in them, manufacturers started to get much more creative.
Being able to have blocks of RAM allocated purely to graphical work opened up lots of new doors.
Graphics chipsets started being able to maniuplate the graphics themselves offloading some of the processing from the CPU. Moving chunks of data around in RAM very fast ("blitting") allowed high speed display updates for more graphically rich games. Being able to get graphical data from multiple locations and combine them together at image output time ("sprites") allowed for much simpler interaction with the user. For instance, the player character in a game would be a sprite overlaid on the graphics "playfield" by the graphics hardware. The mouse cursor would be a sprite placed over the top of everything else. This separation of "foreground" sprites and "background" framebuffer meant that you didn't need to remember what was under the mouse cursor (or whatever was moving around) to re-draw it after movement.
Then you had the more advanced systems where you had a fully programmable controller chip - basically a graphical co-processor (such as the Amiga's "Copper" chip) which could perform all sorts of complex operations synchronised with the display timing.
But basically most accelerated graphical operations were about moving large amounts of data around, not just incredibly quickly, but also at the right time and in the right way.