Linux DRM Entry Point#

/*!***************************************************************************
 \File         OpenGLESHelloAPI_LinuxDRM.cpp
 \Title        OpenGL ES 2.0 HelloAPI Tutorial
 \Author       PowerVR by Imagination, Developer Technology Team
 \Copyright    Copyright (c) Imagination Technologies Limited.
 \Description  Basic Tutorial that shows step-by-step how to initialise OpenGL ES 2.0, use it for drawing a triangle and terminate it.
               Entry Point: main
 *****************************************************************************/

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>

 #include "OpenGLESHelloAPI.h"

 #define DRIDEVNAME "/dev/dri/card0"

 struct DRMVariables
 {
    unsigned int drmDisplayId;
    int drmFile;
    unsigned int drmCrtcId;
    unsigned int drmConnectorId;
    unsigned int drmEncoderId;
    drmModeResPtr drmResources;
    drmModeCrtcPtr drmCrtc;
    drmModeEncoderPtr drmEncoder;
    drmModeModeInfoPtr drmMode;
    drmModeConnectorPtr drmConnector;

    OpenGLESAPI helloAPI;

    DRMVariables()
        : drmDisplayId(0), drmFile(0), drmCrtcId(0), drmConnectorId(0), drmEncoderId(0), drmResources(NULL), drmCrtc(NULL), drmEncoder(NULL), drmMode(NULL), drmConnector(NULL)
    {}
 };

Helper function used in RenderScene.

struct SDrmFbWrapper
{
   struct gbm_bo* psGbmBo;
   unsigned int ui32FbId;
   int i32Fd;
};

/*****************************************************************************
 Helper Functions
*****************************************************************************/
static void pfnCallbackDrmFbDestroy(struct gbm_bo* bo, void* data)
{
   struct SDrmFbWrapper* psFb = (struct SDrmFbWrapper*)data;

   if (psFb->ui32FbId)
   {
       drmModeRmFB(psFb->i32Fd, psFb->ui32FbId);
   }

   delete psFb;
}

static void pfnCallbackDrmPageFlip(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void* data)
{
   int* pi32WaitFlip = (int*)data;
   *pi32WaitFlip = 0;
}

struct SDrmFbWrapper* DrmFbGetFromBo(DRMVariables& data, struct gbm_bo* bo)
{
   struct SDrmFbWrapper* fb = (struct SDrmFbWrapper*)gbm_bo_get_user_data(bo);
   uint32_t width, height, stride, handle;
   int ret;

   if (fb)
   {
       return fb;
   }

   fb = new struct SDrmFbWrapper;
   fb->psGbmBo = bo;
   fb->i32Fd = data.drmFile;

   width = gbm_bo_get_width(bo);
   height = gbm_bo_get_height(bo);
   stride = gbm_bo_get_stride(bo);
   handle = gbm_bo_get_handle(bo).u32;

   ret = drmModeAddFB(data.drmFile, width, height, 24, 32, stride, handle, &fb->ui32FbId);

   if (ret)
   {
       delete fb;
       return NULL;
   }

   gbm_bo_set_user_data(bo, fb, pfnCallbackDrmFbDestroy);
   return fb;
}

/*****************************************************************************
 Application Functions
*****************************************************************************/

/*!***************************************************************************
\Function      CreateNativeDisplay
\Output        data Variables used for the windowing system
\Return        Whether the function succeeded or not.
\Description   Creates a native isplay for the application to render into.
*****************************************************************************/
bool CreateNativeDevice(DRMVariables& data)
{
   /*
       In the future it may be possible to get the drm device from udev.
       By default just use card0.
   */
   if ((data.drmFile = open(DRIDEVNAME, O_RDWR)) < 0)
   {
       printf("failed to open drm device %s : %s\n", DRIDEVNAME, strerror(errno));
       return false;
   }

   data.drmResources = drmModeGetResources(data.drmFile);

   if (!data.drmResources)
   {
       drmClose(data.drmFile);
       printf("drmModeGetResources failed: %s\n", strerror(errno));
       return false;
   }

Find a connected connector.

   unsigned int drmDisplayId = 0;
   bool found = false;

   for (int i = 0; i < data.drmResources->count_connectors; ++i)
   {
       data.drmConnector = drmModeGetConnector(data.drmFile, data.drmResources->connectors[i]);

       if (data.drmConnector->connection != DRM_MODE_CONNECTED)
       {
           drmModeFreeConnector(data.drmConnector);
           continue;
       }

       if (drmDisplayId == 0)
       {
           found = true;
           break;
       }

       if (drmDisplayId == data.drmConnector->connector_id)
       {
           found = true;
           break;
       }
   }

   if (found == false)
   {
       drmModeFreeResources(data.drmResources);
       drmClose(data.drmFile);
       printf("No Connector found for requested device\n");
       return false;
   }

   data.drmConnectorId = data.drmConnector->connector_id;
   data.drmMode = &data.drmConnector->modes[0];

   found = false;

   for (int j = 0; j < data.drmResources->count_encoders; ++j)
   {
       data.drmEncoder = drmModeGetEncoder(data.drmFile, data.drmResources->encoders[j]);

       if (data.drmEncoder->encoder_id == data.drmConnector->encoder_id)
       {
           found = true;
           break;
       }

       drmModeFreeEncoder(data.drmEncoder);
   }

   if (!found)
   {
       drmModeFreeConnector(data.drmConnector);
       drmModeFreeResources(data.drmResources);
       drmClose(data.drmFile);
       printf("No Encoder found for requested Connector\n");
       return false;
   }

   data.drmEncoderId = data.drmEncoder->encoder_id;
   data.drmCrtcId = data.drmEncoder->crtc_id;

   for (int j = 0; j < data.drmResources->count_crtcs; ++j)
   {
       data.drmCrtc = drmModeGetCrtc(data.drmFile, data.drmResources->crtcs[j]);

       if (data.drmCrtc->crtc_id == data.drmCrtcId)
       {
           break;
       }

       drmModeFreeCrtc(data.drmCrtc);
   }

   data.helloAPI._surfaceData.deviceContext = gbm_create_device(data.drmFile);
   return true;
}

/*!***************************************************************************
\Function      CreateNativeWindow
 @Input            data Variables used for the windowing system
\Return        Whether the function succeeded or not.
\Description   Creates a native window for the application to render into.
*****************************************************************************/
bool CreateNativeWindow(DRMVariables& data)
{
   data.helloAPI._surfaceData.window =
       gbm_surface_create(data.helloAPI._surfaceData.deviceContext, data.drmMode->hdisplay, data.drmMode->vdisplay, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);

   return true;
}

/*!***************************************************************************
\Function      ReleaseWindowAndDisplay
 @Input            data Variables used for the windowing system
\Description   Releases all resources allocated by the windowing system
*****************************************************************************/
void ReleaseNativeResources(DRMVariables& data)
{
   gbm_surface_destroy(data.helloAPI._surfaceData.window);
   gbm_device_destroy(data.helloAPI._surfaceData.deviceContext);
   drmModeFreeCrtc(data.drmCrtc);
   drmModeFreeEncoder(data.drmEncoder);
   drmModeFreeConnector(data.drmConnector);
   drmModeFreeResources(data.drmResources);
   drmClose(data.drmFile);
}

/*!***************************************************************************
\Function      main
 @Input            argc                        Number of arguments passed to the application, ignored.
 @Input            argv           Command line strings passed to the application, ignored.
\Return        Result code to send to the Operating System
\Description   Main function of the program, executes other functions.
*****************************************************************************/
int main(int /*argc*/, char** /*argv*/)
{

Structure for the DRM variables.

DRMVariables data;

Get access to a native display.

CreateNativeDevice(data);

Setup the windowing system, create a window.

CreateNativeWindow(data);

data.helloAPI.initializeEGL();
data.helloAPI.initializeGLES();

Renders a triangle for 800 frames using the state setup in the previous function.

for (int i = 0; i < 800; ++i)
{
    if (!data.helloAPI.drawFrame() || !data.helloAPI.swapEGLBuffers())
    {
        break;
    }

Perform the flip.

    struct gbm_bo* bo = gbm_surface_lock_front_buffer(data.helloAPI._surfaceData.window);
    struct SDrmFbWrapper* fb = DrmFbGetFromBo(data, bo);

    int ret = drmModeSetCrtc(data.drmFile, data.drmCrtcId, fb->ui32FbId, 0, 0, &data.drmConnectorId, 1, data.drmMode);

    if (ret)
    {
        printf("display failed to set mode: %s\n", strerror(errno));
        break;
    }
}

data.helloAPI.releaseGLState();
data.helloAPI.releaseEGLState();

Release the windowing system resources.

ReleaseNativeResources(data);

Destroy the eglWindow.

   return 0;
}