5. Generic buffers and frames

The top-most elements of the graphics pipeline are Buffers. A Buffer is a memory region accessible by the graphics chipset. In this case they are used to store pixel color components (e.g. 32-bit RGBA values).

There are two general kinds of buffers:

  • driver specific buffers: they can be used to fully exploit the features of the device but they use different APIs depending on the device driver.

  • generic buffers: these buffers use the same API for all devices supporting them but they can’t be used to exploit the most advanced features of the device.

In this chapter we present the generic buffers as they are simple to use and avalable for many graphics chipsets. The source code for this chapter can be found here.

5.1. Allocating Generic Buffers

To allocate a generic buffer, we can use createGenericBuffer function. It takes a width and an height in pixels and the number of bits per pixels (must be a multiple of 8).

However we often want to create a Frame and the buffers according to the Frame format. Hence in the code example we use createGenericFrame instead which does all of this. Then it displays information about the allocated Frame and Buffers.

Example of run into QEMU with Linux 5.1.15:

> git clone https://github.com/haskus/haskus-system.git
> cd haskus-system/haskus-system-examples
> haskus-system-build test --init TutCreateGenericFrame

Frame 35
  Width:  1024 pixels
  Height: 768 pixels
  Pixel format: XRGB8888 (LittleEndian)
  Flags: []
  FrameBuffer 0:
    - Buffer handle: 1
    - Pitch: 4096
    - Offset: 0
    - Modifiers: 0
    - Buffer specific details:
       - Type: generic buffer
       - Width:  1024 pixels
       - Height: 768 pixels
       - Bits per pixels: 32
       - Flags: 0
       - Handle: 1
       - Pitch: 4096 bytes
       - Size: 3145728 bytes
       - Address: 0x00007efd406f5000

[    0.984017] reboot: Power down

You can see that a GenericBuffer object contains the effective size of the buffer (in bytes), the pitch (effective width of a line in bytes), an address (explained in the next section), etc.

Hint: if we want to create a generic Frame for a full screen mode, we can pass a Mode to createGenericFullScreenFrame. Frame and Buffers dimensions (in pixels) are then obtained from the Mode.

5.2. Writing into Generic Buffers

Generic buffers have a very useful property: they can be mapped into the process memory. haskus-system automatically maps them when they are created so you don’t have to worry about doing it.

The mapped region address is stored in a ForeignPtr which you can use with: withGenericBufferPtr (or withGenericFrameBufferPtr wrapper). For example:

-- fill the frame with a color
withGenericFrameBufferPtr fb \ptr ->
   forEachFramePixel frame \x y -> do
      let off = frameBufferPixelOffset fb 4 x y -- 4 is pixel component size in bytes
      pokeByteOff (castPtr ptr) (fromIntegral off) (color :: Word32)

However with generic buffers it is even easier: there is the forEachGenericFramePixel wrapper that does these tricky pointer computations for us. The code example uses it as follows:

-- fill the generic frame with a color
-- (0 is the FrameBuffer index)
forEachGenericFramePixel frame 0 \_x _y ptr ->
   poke ptr (0x316594 :: Word32) -- write a XRGB8888 color