Buffers

Updated: April 19, 2023

A buffer is an area of memory that stores pixel data. Multiple buffers can be associated with a window or stream, but only one buffer can be associated with a pixmap.

Creating buffers

The buffers that you can create are:

internal buffers
Internal buffers are those that were created by calling one of the following Screen API functions:
external buffers
External buffers are those that were created by calling screen_create_buffer() and whose memory was allocated by applications. You can use (e.g. render into) an external buffer only after you've attached it to one of a pixmap, window, or stream.

Internal buffers

You can create buffers for pixmaps, streams, and windows by calling one of the following Screen API functions.

You may set the SCREEN_PROPERTY_USAGE on the Screen API object before you create its buffer if SCREEN_USAGE_READ or SCREEN_USAGE_WRITE is insufficient for your intended usage of the buffer. Otherwise, simply create the buffer for the Screen API object by calling the appropriate function. For example, to create buffers for a window:

...
screen_context_t ctx;                      
screen_window_t win;
int num_bufs = 2;
...
screen_create_window(&win, ctx);
screen_create_window_buffers(win, num_bufs);
...
            

You can now proceed to use the buffer. See “Using buffers”.

External buffers

Applications such as drivers may have a requirement to allocate their own buffers because of memory location, or a buffer size requirement. These applications must ensure that all usage constraints are met when allocating the buffers to enable Screen to successfully attach the buffers. Failure to meet the usage constraints may also result in artifacts and system instability.

Applications that create their own buffers and allocate their memory must:

  1. Create the the buffer of type screen_buffer_t by calling screen_create_buffer(). For example:
    ...
    screen_buffer_t created_buf = NULL;
    ...
    screen_create_buffer(&created_buf);
    ...
                        
  2. Set the buffer properties.

    At a minimum, you must set the following properties for the buffer that your application allocates:

    • SCREEN_PROPERTY_FORMAT
    • SCREEN_PROPERTY_BUFFER_SIZE
    • SCREEN_PROPERTY_SIZE
    • SCREEN_PROPERTY_STRIDE

    If you're using one of the planar YUV formats, then you should also set the SCREEN_PROPERTY_PLANAR_OFFSETS property.

    For example for a buffer of format SCREEN_FORMAT_NV12:

    ...
    const int width=1024, height=64, stride=4096;
    const int nbytes = stride*height + stride*(height/2);
    ...
    screen_set_buffer_property_iv(created_buf,SCREEN_PROPERTY_FORMAT,(const int[]){SCREEN_FORMAT_NV12});
    screen_set_buffer_property_iv(created_buf,SCREEN_PROPERTY_BUFFER_SIZE,(const int[]){width, height});
    screen_set_buffer_property_iv(created_buf,SCREEN_PROPERTY_SIZE,(const int[]){nbytes});
    screen_set_buffer_property_iv(created_buf,SCREEN_PROPERTY_STRIDE,(const int[]){stride});
    screen_set_buffer_property_iv(created_buf,SCREEN_PROPERTY_PLANAR_OFFSETS,(const int[]){0,(height*stride),-1});
    ...
                        
  3. Allocate the memory for the buffer and assign access to it.

    You can use either the SCREEN_PROPERTY_POINTER property or the SCREEN_PROPERTY_FD property to assign memory. However, note that you may need to set different attributes on the memory depending on which property you use:

    SCREEN_PROPERTY_POINTER
    1. Allocate the memory for the buffer.

      Allocate some memory for the buffer. How and from where the memory is allocated is up to the application; it can be statically allocated (mapped from a fixed address) or allocated from a shared memory object. For example:

      ...
      int fd;
      uint32_t *mmap_ptr;
      ...
      fd = shm_open(SHM_ANON, O_CREAT|O_EXCL|O_RDWR, 0400);
      
      shm_ctl(fd, SHMCTL_ANON, 0, nbytes);
      
      mmap_ptr = mmap(NULL, nbytes , PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
      ...
                          
    2. Assign the pointer to the buffer.

      You must set the SCREEN_PROPERTY_POINTER property to point to the memory that's been allocated for this buffer. For example:

      ...
      screen_set_buffer_property_pv(created_buf, SCREEN_PROPERTY_POINTER, (void**)&mmap_ptr);
      ...
                                              
    SCREEN_PROPERTY_FD
    1. Allocate the memory for the buffer.

      How and from where the memory is allocated is up to the application; it can be statically allocated (mapped from a fixed address) or allocated from a shared memory object. For example:

      ...
      int fd;
      ...
      fd = shm_open(SHM_ANON, O_CREAT|O_EXCL|O_RDWR, 0400);
      ...
      
      /* Seal the memory so it can't be modified */
      if (0 != shm_ctl(shm_fd, SHMCTL_SEAL, 0, 0)){
      		/* error */
      		...
      	}
      ...
                          
      Note: When you're using the SCREEN_PROPERTY_FD property to indicate memory, you must ensure that you:
      • call shm_ctl() to set the SHMCTL_SEAL flag to prevent the shared memory object from being modified.
      • ensure that the SHMCTL_REVOCABLE flag is NOT set on your shared memory object. You can call shm_ctl() with the SHMCTL_GET_FLAGS flag to verify which flags have been set on the object that you're using. If the SHMCTL_REVOCABLE flag has been set, you can't associate this object with your buffer.
    2. Assign the file descriptor to the buffer.

      You must set the SCREEN_PROPERTY_FD property to point to the memory that's been allocated for this buffer. For example:

      ...
      screen_set_buffer_property_iv(created_buf, SCREEN_PROPERTY_FD, &fd);
      ...
                                              
  4. Attach the external buffer to a pixmap, window, or stream before you can use it. See “Attaching buffers”.
    Note:

    You must be using a privileged Screen context that includes SCREEN_BUFFER_PROVIDER_CONTEXT in its type to attach the buffer. For example:

    ...
    screen_context_t screen_ctx;
    ...
    screen_create_context(&screen_ctx, (SCREEN_APPLICATION_CONTEXT | SCREEN_BUFFER_PROVIDER_CONTEXT));
    ...
                           

Attaching buffers

You can attach a buffer (of type screen_buffer_t) to associate it with a pixmap, stream, or window by calling the following functions respectively:

To attach buffers:

  1. Get a screen_buffer_t.

    To get access to a buffer, you can retrieve any of the following:

    • an external buffer (i.e., one that was created by calling screen_create_buffer() and has memory allocated to it)
    • a buffer from a stream by calling the screen_acquire_buffer() function. For example:
      screen_stream_t astream = NULL;
      screen_buffer_t buffer = NULL;
      ...
      screen_acquire_buffer(&buffer, astream, NULL, NULL, NULL, SCREEN_ACQUIRE_DONT_BLOCK);
      ...
                                  
    • a buffer from a pixmap, stream or window by calling the screen_get_*_property_pv() function with one of its valid properties that returns a buffer
      pixmap
      Call screen_get_pixmap_property_pv() with one of the following properties:
      • SCREEN_PROPERTY_BUFFERS
      • SCREEN_PROPERTY_RENDER_BUFFERS
      stream
      Call screen_get_stream_property_pv() with one of the following properties:
      • SCREEN_PROPERTY_BUFFERS
      • SCREEN_PROPERTY_FRONT_BUFFERS
      • SCREEN_PROPERTY_RENDER_BUFFERS
      window
      Call screen_get_window_property_pv() with one of the following properties:
      • SCREEN_PROPERTY_BUFFERS
      • SCREEN_PROPERTY_FRONT_BUFFER
      • SCREEN_PROPERTY_RENDER_BUFFERS
      For example:
      ...
      screen_buffer_t *win_buf;
      win_buf = calloc((size_t)num_bufs, sizeof(screen_buffer_t));
      ...
      screen_get_window_property_pv(win, SCREEN_PROPERTY_RENDER_BUFFERS, (void**)win_buf));
      ...
                                  
  2. Set the SCREEN_PROPERTY_USAGE on the Screen API object (i.e., pixmap, stream, or window) that you're attaching the buffer to.

    From the pixmap, stream, or window, you should indicate the intended usage of the buffer before you attach it because if the usage isn't set appropriately, Screen may prevent access to the buffer.

    ...
    screen_window_t a_win;
    ...
    int usage = SCREEN_USAGE_NATIVE | SCREEN_USAGE_READ | SCREEN_USAGE_WRITE;
    screen_set_window_property_iv(a_win, SCREEN_PROPERTY_USAGE, &usage);
    ...
                        
    Note: If the intended usage that you set on the Screen API object that you're attaching the buffer to isn't compatible with the buffer's usage that is was originally created with, then Screen may fail to attach the buffer or the buffer may not be usable for your intended purpose.
  3. Attach the buffer to a valid Screen API object by calling the appropriate Screen API function.

    For example:

    ...
    screen_attach_window_buffers(a_win, num_bufs, win_buf);
    ...
                        
    Note: Unlike pixmaps and windows, you can't attach buffers to streams that already have buffers associated with them without first calling screen_destroy_stream_buffers().

Using buffers

To use a buffer that's associated with a Screen API object, you must retrieve it from the object. The following are ways that you can retrieve a buffer that's valid for use (e.g., for blitting or rendering purposes):

pixmap
Call screen_get_pixmap_property_pv() with one of the following properties:
  • SCREEN_PROPERTY_BUFFERS
  • SCREEN_PROPERTY_RENDER_BUFFERS
For example:
...
screen_context context;
screen_pixmap_t screen_pix = NULL;
screen_buffer_t pix_buf;
uint32_t *pix_ptr;
...
screen_get_pixmap_property_pv(pixmap, SCREEN_PROPERTY_RENDER_BUFFERS, (void**)&pix_buf);
screen_get_buffer_property_pv(pix_buf, SCREEN_PROPERTY_POINTER, (void**)&pix_ptr);
...
screen_fill(context, pix_buf, (const int[]){SCREEN_BLIT_COLOR, 0xabcd, SCREEN_BLIT_END});
...
             
stream
  • Call screen_get_stream_property_pv() with one of the following properties:
    • SCREEN_PROPERTY_BUFFERS
    • SCREEN_PROPERTY_FRONT_BUFFERS
    • SCREEN_PROPERTY_RENDER_BUFFERS
    For example:
    ...
    int num_bufs = 0;
    screen_buffer_t *stream_buf = NULL;
    screen_get_stream_property_iv(stream, SCREEN_PROPERTY_RENDER_BUFFER_COUNT, &num_bufs);
    a_stream_buf = calloc((size_t)num_bufs, sizeof(screen_buffer_t));
    ...
    screen_get_stream_property_pv(stream, SCREEN_PROPERTY_RENDER_BUFFERS, (void**)stream_buf);
    ...
    screen_post_stream(stream, stream_buf[0], 0, NULL, 0);
    ...
                 
  • Call screen_acquire_buffer() function. For example:
    ...
    screen_context ctx;
    screen_stream_t a_stream;
    screen_buffer_t buffer, dest_buffer;
    ...
    screen_acquire_buffer(&buffer, a_stream, NULL, NULL, NULL, SCREEN_ACQUIRE_DONT_BLOCK);
    ...
    screen_blit(ctx, buffer, dest_buffer, (const int[]){SCREEN_BLIT_COLOR, 0xabcd, SCREEN_BLIT_END});
    ...
                                
window
Call screen_get_window_property_pv() with one of the following properties:
  • SCREEN_PROPERTY_BUFFERS
  • SCREEN_PROPERTY_FRONT_BUFFER
  • SCREEN_PROPERTY_RENDER_BUFFERS
For example:
...
int num_bufs = 0;
screen_buffer_t *win_buf = NULL;
screen_get_window_property_iv(win, SCREEN_PROPERTY_RENDER_BUFFER_COUNT, &num_bufs);
other_win_buf = calloc((size_t)num_bufs, sizeof(screen_buffer_t));
...
screen_get_window_property_pv(win, SCREEN_PROPERTY_RENDER_BUFFERS, (void**)win_buf);
...
screen_post_window(win, win_buf[0], 0, NULL, 0);
...