This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Linux/AM5728: 3D GPU slower than 2D GPU

Part Number: AM5728

Tool/software: Linux

Hello,

In order to display 1280 * 720 size BMP images on the screen, 2D and 3D GPUs are used for each LCD.

The 3D GPU was more than twice as slow as the 2D GPU, causing problems with screen synchronization.

2D GPU Is 4 milli Second, but 3D GPU Is milli Second.

We are curious.

I expected the 3D GPU to be faster than the 2D GPU.

Are you hit late?

Attach the sample code used for the test.
(2D_galRunTest2.c,3D_kmscube.c,bmp720.bmp )

/****************************************************************************
*
*    Copyright (c) 2005 - 2015 by Vivante Corp.  All rights reserved.
*
*    The material in this file is confidential and contains trade secrets
*    of Vivante Corporation. This is proprietary information owned by
*    Vivante Corporation. No part of this work may be disclosed,
*    reproduced, copied, transmitted, or used in any way for any purpose,
*    without the express written permission of Vivante Corporation.
*
*****************************************************************************/


#include <galUtil.h>
#include "gutsystem.h"

/* drm headers */
#include <omap/omap_drm.h>
#include <libdrm/omap_drmif.h>
#include <xf86drm.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

/* leejoonho :  */
#define TIMEDEBUG

#ifdef TIMEDEBUG
#include <sys/time.h>
#endif

#ifdef TIMEDEBUG
static struct timeval timeStart;
static char g_timeBuf[20];

#define DPRINTF(fmt, args...) printf("[%s] :" fmt, GetStartedTime(), ## args)
char* GetStartedTime(void)
{
 	struct timeval currentTime;
    int hours, minutes, seconds;
    int milliseconds;

    gettimeofday(&currentTime, NULL);

    if (currentTime.tv_usec < timeStart.tv_usec)
    {
        milliseconds = (currentTime.tv_usec - timeStart.tv_usec + 1000000)/1000;
        currentTime.tv_sec--;
    }
    else    
    {
        milliseconds = (currentTime.tv_usec - timeStart.tv_usec)/1000;
    }

    /* Calculate the time   */
    hours = (currentTime.tv_sec - timeStart.tv_sec)/3600;
    minutes = (((currentTime.tv_sec - timeStart.tv_sec)/60))%60;
    seconds = (currentTime.tv_sec - timeStart.tv_sec)%60;
	
	memset(g_timeBuf, 0, sizeof(g_timeBuf));    
	sprintf(g_timeBuf, "%02u:%02u:%02u.%03u", hours, minutes, seconds, milliseconds);
 	return g_timeBuf; 	 	 	
}
#endif /* TIMEDEBUG */


//#define VIVANTE_DEBUG

#define GOOD_RECT(rect) (rect.left < rect.right && rect.top < rect.bottom)
/* Version info. */
static const char c_szFrameworkInfo[] = "\tGAL Test Framework Version 3.1 \n";

/* Base objects */
static GalRuntime g_Runtime;

#if defined(LINUX) || defined(ANDROID)
/* Video memory mapping. */
static gctPHYS_ADDR g_InternalPhysical, g_ExternalPhysical, g_ContiguousPhysical;
static gctSIZE_T    g_InternalSize,     g_ExternalSize,     g_ContiguousSize;
static gctPOINTER   g_Internal,         g_External,         g_Contiguous;
#endif

static gctSTRING        g_Argv2Test[MAX_ARG_COUNT] = {"",};

int g_alloc_fd = -1;

typedef struct Test2D {
    GalTest     base;
	GalRuntime  *runtime;

	// destination surface
	gcoSURF			dstSurf;
	gceSURF_FORMAT		dstFormat;
	gctUINT			dstWidth;
	gctUINT			dstHeight;
	gctINT			dstStride;
	gctUINT32		dstPhyAddr;
	gctPOINTER		dstLgcAddr;

	//source surface
	gcoSURF			src1Surf;
	gceSURF_FORMAT		src1Format;
	gctUINT			src1Width;
	gctUINT			src1Height;
	gctINT			src1Stride[3];
	gctUINT32		src1PhyAddr[3];
	gctPOINTER		src1LgcAddr[3];

	gcoSURF			src2Surf;
	gceSURF_FORMAT		src2Format;
	gctUINT			src2Width;
	gctUINT			src2Height;
	gctINT			src2Stride[3];
	gctUINT32		src2PhyAddr[3];
	gctPOINTER		src2LgcAddr[3];

	gctUINT			alpha;

} Test2D;

typedef struct SurfaceGeom {
	gctINT width;
	gctINT height;
	gceSURF_FORMAT format;

}SurfaceGeom;

Test2D* GalTest2D;
static SurfaceGeom dstGeom ={0,}, src1Geom= {0,}, src2Geom= {0,};
static 	gcsRECT dstrect={0,}, src1rect={0,}, src2rect={0,}, cliprect={0,}, src2auxdstrect={0,};
static gctUINT32 src2Angle = 0;
const char *dstFile = NULL, *src1File = NULL, *src2File = NULL;


gctBOOL IsFileVimg(const char* szFilename)
{
  gctINT len = strlen(szFilename);
  return(strcmp(&(szFilename[len-4]), "vimg") == 0);
}

gctBOOL IsFileRaw(const char* szFilename)
{
  gctINT len = strlen(szFilename);
  return((strcmp(&(szFilename[len-3]), "raw") == 0)
   || (strcmp(&(szFilename[len-3]), "yuv") == 0));
}

gctBOOL IsFileBmp(const char* szFilename)
{
  gctINT len = strlen(szFilename);
  return(strcmp(&(szFilename[len-3]), "bmp") == 0);
}

gctINT GetStride(gctINT width, gceSURF_FORMAT format)
{
	size_t stride;
	gcsSURF_FORMAT_INFO_PTR info[2]; 
	gcmVERIFY_OK(gcoSURF_QueryFormat(format, info));
	stride = ((width + 15) & ~15);
	switch(format)
	{
		case gcvSURF_YUY2:
		case gcvSURF_UYVY:
		case gcvSURF_YVYU:
		case gcvSURF_VYUY:
			stride *= 2;
			break;

		case gcvSURF_YV12:
		case gcvSURF_I420:
		case gcvSURF_NV12:
		case gcvSURF_NV21:
		case gcvSURF_NV16:
		case gcvSURF_NV61:
			break;

		default:
			stride *= info[0]->bitsPerPixel / 8;
			break;
	}
	return(stride);
}

gctUINT GetAddressStrideNum(gceSURF_FORMAT format)
 {
    gctUINT numPlanes;
    switch (format)
    {
    case gcvSURF_YUY2:
    case gcvSURF_UYVY:
        numPlanes = 1;
        break;

    case gcvSURF_I420:
    case gcvSURF_YV12:
        numPlanes = 3;
        break;

    case gcvSURF_NV16:
    case gcvSURF_NV12:
    case gcvSURF_NV61:
    case gcvSURF_NV21:
        numPlanes = 2;
        break;

    default:
        numPlanes = 1;
    }
	
	return numPlanes;
 }	
gceSTATUS LoadFileToSurface(const char* source, Test2D *t2d)
{
	gcoSURF *surf;
	const char* file;
	SurfaceGeom src;
	gceSTATUS status = gcvSTATUS_OK  ;
	
	printf("DBG111 : at %s\n", __FUNCTION__);
	
	if(!strcmp(source, "src1")) {
		surf = &t2d->src1Surf;
		file = src1File;
		memcpy((void*)&src, (void*)&src1Geom, sizeof(SurfaceGeom));
	}
	else {
		surf = &t2d->src2Surf;
		file = src2File;
		memcpy((void*)&src, (void*)&src2Geom, sizeof(SurfaceGeom));
	}
	
	printf("DBG222 : file(%s) at %s\n", file, __FUNCTION__);
	if ( !file ) 
	{
			status = -1;
	}		
	else
	{		
		if(IsFileVimg(file))
			status = GalLoadVimgToSurface(file, surf);
		else if(IsFileBmp(file)){	
			printf("DBG : after IsFileBmp. at %s\n", __FUNCTION__);
			*surf = GalLoadDIB2Surface(t2d->runtime->hal, file);
			if(*surf == NULL)
			  status = -1;
		}	
		else if(IsFileRaw(file)) {
			gctUINT stride;
			if(src.width <=0 || src.height <= 0) {
				sysOutput("For raw source files Geometry needs to be set. example: -src1 NV12 720x480\n");
				return -1;  
			}
			stride = GetStride(src.width, src.format);
			status = GalLoadRawToSurface(file, src.width, src.height, stride, src.format, surf);		
		}
		else {	 
			sysOutput("Not a valid type file. Only bmp, raw/yuv or vimg supported\n");
			status = -1;;
		}
	}
	
	printf("DBG33 : at %s\n", __FUNCTION__);
	return status;

}

gctBOOL TextToInt(gctINT*  val,
               const char** p,
               const char end)
{
  gctBOOL success = gcvTRUE;
  gctINT v = 0;
  gctBOOL vneg = gcvFALSE;
  if(**p == '-')
  {
    vneg = gcvTRUE;
    (*p)++;
  }

  do
  {
    if((**p <'0') || (**p >'9'))
    {
      success = gcvFALSE;
      goto Error;
    }

    v = (v * 10) + (**p - '0');
    (*p)++;
  }while(**p != end);

  *val = vneg ? -v : v;
Error:
  return(success);
}

gctBOOL TextToValPair(gctINT *val1,
                         gctINT *val2,
                         const char **p,
                         const char   sep,
                         const char   end)
{
  gctBOOL success;
  const char* szVals = *p;

  if(!(success = TextToInt(val1, p, sep)))
  {
    sysOutput("Invalid first value of value pair: %s\n", szVals);
  }
  else
  {
    (*p)++;
    if(!(success = TextToInt(val2, p, end)))
    {
      sysOutput("Invalid second value of value pair: %s\n", szVals);
    }
  }

  return(success);
}
const char* GetRect(gcsRECT* rect, const char* p)
{
  const char* szErr = 0;

  if(!TextToValPair(&rect->left, &rect->top, &p, ',', '-'))
  {
    szErr = "Invalid upper left coordinate";
    goto Error;
  }
  if(*p)
    p++;

  if(!TextToValPair(&rect->right, &rect->bottom, &p, 'x', '\0'))
  {
    szErr = "Invalid size";
    goto Error;
  }

Error:
  return(szErr);
}
const char* HexToUL(unsigned long *val,
                    const char*    p)
{
  const char* szErr = 0;

  *val = 0;

  char c = *p;
  while(c)
  {
    if((c >= '0') && (c <= '9'))
      *val = (*val * 16) + (c - '0');
    else if((c >= 'A') && (c <= 'F'))
      *val = (*val * 16) + (10 + c - 'A');
    else if((c >= 'a') && (c <= 'f'))
      *val = (*val * 16) + (10 + c - 'a');
    else
    {
      szErr = "Invalid hex value";
      break;
    }
    p++;
    c = *p;
  }

  return(szErr);
}

gctBOOL ParseFile(const char** szFile,
               gctINT          argc,
               const char*  argv[],
               gctINT*         arg)
{
  gctBOOL success = gcvFALSE;
  if((*arg + 1) >= argc)
  {
    sysOutput("No filename provided with %s option (ex. %s image.bmp).\n", argv[*arg], argv[*arg]);
    goto Error;
  }

  *szFile = argv[*arg + 1];
  sysOutput("%s %s\n", argv[*arg], *szFile);
  *arg += 2;

  success = gcvTRUE;
Error:
  return(success);
}

gctBOOL ParseAlpha(gctINT         argc,
                const char* argv[],
                gctINT       *arg)
{
  gctBOOL success = gcvFALSE;
  const char* szErr;
  unsigned long alpha;

  szErr = HexToUL(&alpha, argv[*arg + 1]);
  if(szErr)
  {
    sysOutput("%s with -alpha option.\n", szErr);
    goto Error;
  }

  if(alpha > 0xFF)
  {
    sysOutput("Invalid alpha value provided for -alpha option: %s (ex. -rop 80).\n", argv[*arg + 1]);
    goto Error;
  }

//  GalTest2D->alpha = (unsigned char)alpha;
  sysOutput("-alpha %02X\n", alpha);
  *arg += 2;
  
  success = gcvTRUE;
Error:
  return(success);
}

gctBOOL ParseRect(gcsRECT*  rect,
               gctINT         argc,
               const char* argv[],
               gctINT*        arg)
{
  const char* szErr = 0;

  if((*arg + 1) >= argc)
  {
    sysOutput("No rectangle provided with %s option (ex. %s 0,0-640x480)\n", argv[*arg], argv[*arg]);
    goto Error;
  }

  szErr = GetRect(rect, argv[*arg + 1]);
  if(szErr)
  {
    sysOutput("%s with option %s (ex. %s 0,0-640x480)\n", szErr, argv[*arg], argv[*arg]);
    goto Error;
  }
  sysOutput("%s %d,%d-%dx%d\n", argv[*arg], rect->left, rect->top, rect->right, rect->bottom);
  rect->right += rect->left;
  rect->bottom += rect->top;
  *arg += 2;

Error:
  return(szErr == 0);
}
const char* GetDimensions(gctINT *w,
                          gctINT *h,
                          const char*   p)
{
  const char* szErr = 0;
  gctINT width = 0, height = 0;

  if(!TextToValPair(&width, &height, &p, 'x', '\0'))
  {
    szErr = "Invalid size specified";
    goto Error;
  }

  
  if((width <= 0) || (height <= 0))
  {
    szErr = "Invalid surface size";
    goto Error;
  }

  *w = width;
  *h = height;

Error:
  return(szErr);
}

gctBOOL ParseSurf(SurfaceGeom *geom,
               gctINT         argc,
               const char* argv[],
               gctINT        *arg)
{
  gctBOOL success = gcvFALSE;
  const char* szErr = NULL;

  if((*arg + 2) >= argc)
  {
    sysOutput("Details not provided with %s option.\n", argv[*arg]);
    goto Error;
  }

  geom->format = GalQueryFormat(argv[*arg + 1]);
  if(geom->format == gcvSURF_UNKNOWN)
  {
    sysOutput("unknown format %s.\n", argv[*arg]);
    goto Error;
  }
  // Next param is dimension
  // Only calculate dimensions if the next char is a digit.
  if ( (argv[*arg + 2][0] >= '0') && (argv[*arg + 2][0] <= '9') )
  {
    szErr = GetDimensions(&geom->width, &geom->height, argv[*arg + 2]);
    if (szErr)
    {
      sysOutput("Error '%s' while getting surface dimensions\n",szErr);
      *arg += 3;
      success = gcvFALSE;
      goto Error;
    }
    sysOutput("%s %s %dx%d\n", argv[*arg], argv[*arg + 1], geom->width, geom->height);
  }
  else
  {
      sysOutput("invalid parameter '%s' with option %s.\n", argv[*arg + 2], argv[*arg]);
      goto Error;
   }
 

  *arg += 3;

  success = gcvTRUE;

Error:
  return(success);
}

void Usage(const char* szapp)
{
  
  sysOutput("USAGE:  %s\n", szapp);
  sysOutput("-dst <format> ###x### \n"
      "  Specify a destination surface format and size.\n"
      "  Destination format is always forced to A8R8G8B8\n"
      "  If not specified 800x480 will be considered\n");
  sysOutput("-dstrect ###,###-###x###\n"
      "  Specify a rectangle within the destination surface which receives the BLT.\n"
      "  If not specified dst surface width and height will be considered\n");
  sysOutput("-dstfile <file.>bmp\n"
      "  Specify a file into which to save the results.  Must be a BMP \n");
  sysOutput("-src1 <format> ###x### \n"
      "  Specify a source surface format and size.\n"
      "  Not needed for bmp and vimg files. Required only for raw files\n");
  sysOutput("-src1rect ###,###-###x###\n"
      "  Specify a rectangle within the source surface used as the source for the BLT.\n"
      "  If not specified, src1 actual image width and height will be considered\n");
  sysOutput("-src1file <file.>bmp|vimg|raw\n"
      "  Specify a file from which the source surface is initialized.  The image is\n"
      "  placed in the upper left corner of the source surface and clipped if\n"
      "  necessary.\n");
  sysOutput("-alpha <AA>\n"
      "  Specify the global alpha value as a hexadecimal value between 00\n"
      "  (transparent) to FF (opaque).\n");
  sysOutput("-src2 <format> ###x### \n"
      "  Specify a source2 surface format and size.\n"
      "  Not needed for bmp and vimg files. Required only for raw files\n");
  sysOutput("-src2rect ###,###-###x###\n"
      "  Specify a rectangle within the source2 surface used as the\n"
      "  source2 for the BLT.\n"
      "  If not specified, src2 actual image width and height will be considered\n");
  sysOutput("-src2file <file.>|bmp|raw|vimg"
      "  Specify a file from which the source2 surface is initialized.  The\n"
      "  image is placed in the upper left corner of the source2 surface and\n"
      "  clipped if necessary.\n");
  sysOutput("-src2auxdstrect ###,###-###x###\n"
      "  Specify a destination source2 surface used as the\n"
      "  source2 for the Strectching on src1 background.\n"
      "  If not specified, 0,0-256x256 will be considered\n");
  sysOutput("-src2angle ###\n"
      "  Specify orientation of source2 in degrees supported values are 0, 90, 180 and 270.\n");
  sysOutput("-blend clear|src1|src2|src1over|src2over|src1in|src2in|src1out|src2out|src1atop|src2atop|xor|plus|remote|global\n"
      "  Specify the type of blend to use. Multiple -blend parameters can be specified.\n");
  sysOutput("-cliprect ###,###-###x###\n"
      "  Specify a rectangle which is used to indicate the area in the destination\n"
      "  which is written, and outside of which the destination is not modified.\n");
    
}

gctBOOL ParseCmdLine(gctINT         argc,
                   const char* argv[])
{
  gctBOOL success = gcvFALSE;

  for(gctINT arg = 1; arg < argc;)
  {
    if(!strcmp(argv[arg], "-dst"))
    {
      if(!ParseSurf(&dstGeom, argc, argv, &arg))
        goto Error;
    }
    else if(!strcmp(argv[arg], "-dstfile"))
    {
      if(!ParseFile(&dstFile, argc, argv, &arg))
        goto Error;
    }
    else if(!strcmp(argv[arg], "-dstrect"))
    {
      if(!ParseRect(&dstrect, argc, argv, &arg))
        goto Error;
    }
    else if(!strcmp(argv[arg], "-src1"))
    {
      if(!ParseSurf(&src1Geom, argc, argv, &arg))
        goto Error;
    }
    else if(!strcmp(argv[arg], "-src1rect"))
    {
      if(!ParseRect(&src1rect, argc, argv, &arg))
        goto Error;
    }
    else if(!strcmp(argv[arg], "-src1file"))
    {
      if(!ParseFile(&src1File, argc, argv, &arg))
        goto Error;
    }
    else if(!strcmp(argv[arg], "-src2"))
    {
      if(!ParseSurf(&src2Geom, argc, argv, &arg))
        goto Error;
    }
    else if(!strcmp(argv[arg], "-src2file"))
    {
      if(!ParseFile(&src2File, argc, argv, &arg))
        goto Error;
    }
    else if(!strcmp(argv[arg], "-src2rect"))
    {
      if(!ParseRect(&src2rect, argc, argv, &arg))
        goto Error;
    }
    else if(!strcmp(argv[arg], "-src2angle"))
    {
      if(arg + 1 >= argc)
      {
        sysOutput("No angle provided with -src2angle option.\n");
        goto Error;
      }
      src2Angle = atoi(argv[arg + 1]);
      sysOutput("src2 orientation %d\n", src2Angle);
      arg += 2;
    }
    else if(!strcmp(argv[arg], "-src2auxdstrect"))
    {
      if(!ParseRect(&src2auxdstrect, argc, argv, &arg))
        goto Error;
    }
    else if(!strcmp(argv[arg], "-cliprect"))
    {
      if(!ParseRect(&cliprect, argc, argv, &arg))
        goto Error;
    }
    ///////////////////////////////
    else if(!strcmp(argv[arg], "-alpha"))
    {
      if(!ParseAlpha(argc, argv, &arg))
        goto Error;
    }
    
    else
    {
      sysOutput("Unrecognized option %s\n", argv[arg]);
      goto Error;
    }
    ///////////////////////////////
  }
  success = gcvTRUE;
Error:
  return(success);
}


static const struct PoolInfo
{
    gctCONST_STRING name;
    gcePOOL         type;
} c_PoolInfos[] =
{
    {"DEFAULT", gcvPOOL_DEFAULT},
    {"VIRTUAL", gcvPOOL_VIRTUAL},
    {"SYSTEM",  gcvPOOL_SYSTEM},
};


static void close_fd()
{
    close(g_alloc_fd);
    g_alloc_fd = -1;
}

static void* create_drm_buffer(gctUINT width, gctUINT height, gceSURF_FORMAT format, size_t *stride)
{
    /* omapdrm vars */
    struct omap_device *dev;
    struct omap_bo *bo;

    uint32_t size, flags = 0;
    void *vaddr = gcvNULL;
    size_t stride1;

    /* Vivante DDK vars */
    gcsSURF_FORMAT_INFO_PTR info[2];    /* format parameters */
    int bpr;

    //g_alloc_fd = drmOpen("omapdrm", NULL);
    if(g_alloc_fd == -1) {
    	g_alloc_fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
    	if (g_alloc_fd <= 0) {
        	sysOutput("*ERROR* Failed to open omapdrm device\n");
        	return gcvNULL;
    	}
        sysOutput("\tINFO: opened omapdrm device %d\n", g_alloc_fd);
   }	
    dev = omap_device_new(g_alloc_fd);
    if (dev == gcvNULL) {
        sysOutput("*ERROR* Failed to get omap device\n");
        return gcvNULL;
    }
    //sysOutput("\tINFO: got omap_device\n");

    /* calculate the size of the buffer needed. Use gcoSURF_QueryFormat API to
     * format parameters descriptor - this contains information on format as
     * needed by the Vivante driver
     */
    gcmVERIFY_OK(gcoSURF_QueryFormat(format, info));
    //sysOutput("\tINFO: bits per pixel: %d\n", info[0]->bitsPerPixel);

    /* Vivante HAL needs 16 pixel alignment in width and 4 pixel alignment in
     * height.
     */
    stride1 = ((width + 15) & ~15);
    bpr = ((width + 15) & ~15) *(double) ((double)info[0]->bitsPerPixel / 8);
 //   sysOutput("\tINFO: bytes per row: %d bpp %d\n", bpr, info[0]->bitsPerPixel);
    size = bpr * ((height + 3) & ~3);
    switch(format)
    {
	    case gcvSURF_YUY2:
	    case gcvSURF_UYVY:
	    case gcvSURF_YVYU:
	    case gcvSURF_VYUY:
		    stride1 *= 2;
		    break;

	    case gcvSURF_YV12:
	    case gcvSURF_I420:
	    case gcvSURF_NV12:
	    case gcvSURF_NV21:
	    case gcvSURF_NV16:
	    case gcvSURF_NV61:
		    break;

	    default:
		    stride1 *= info[0]->bitsPerPixel / 8;
		    break;
    }
    *stride = stride1;
       
    /* TODO: need to get the flags right here */
    /* MEM_CACHED, MEM_WRITE, */
    //flags &= ~OMAP_BO_CACHE_MASK;   /* MEM_CACHED */
    flags = OMAP_BO_WC;            /* write combine */
    //flags &= ~OMAP_BO_TILED_MASK;   /* non-Tiler */
    sysOutput("size = %d stride %d\n", size, stride1);
    bo = omap_bo_new(dev, size, flags);
    if (!bo) {
        sysOutput("*ERROR* Failed to create omap bo\n");
        return gcvNULL;
    }
   
    vaddr = omap_bo_map(bo);
    if (!vaddr) {
        sysOutput("*ERROR* could not mmap dmabuf - %s\n", strerror(errno));
        return gcvNULL;
    }
    /*
     * `omap_bo` might not have backing pages at this point, make sure you got
     * them by writing into it.
     */
    memset(vaddr, 0, size);
  
    return vaddr;

}

/*
 *  Name:       Initialize()
 *  Parameters: case_name, the name of the dll of the test case.
*/

static gctBOOL Initialize()
{
//    PGalCreateTestObject createTestObject;
    gceSTATUS status;

    /* Construct the gcoOS object. */
    status = gcoOS_Construct(gcvNULL, &g_Runtime.os);
    if (status < 0)
    {
        sysOutput("*ERROR* Failed to construct OS object (status = %d)\n", status);
        return gcvFALSE;
    }

    /* Construct the gcoHAL object. */
    status = gcoHAL_Construct(gcvNULL, g_Runtime.os, &g_Runtime.hal);
    if (status < 0)
    {
        sysOutput("*ERROR* Failed to construct GAL object (status = %d)\n", status);
        return gcvFALSE;
    }

    if (!gcoHAL_IsFeatureAvailable(g_Runtime.hal, gcvFEATURE_2DPE20))
    {
        switch (g_Runtime.format)
        {
        /* PE1.0 support. */
        case gcvSURF_X4R4G4B4:
        case gcvSURF_A4R4G4B4:
        case gcvSURF_X1R5G5B5:
        case gcvSURF_A1R5G5B5:
        case gcvSURF_X8R8G8B8:
        case gcvSURF_A8R8G8B8:
        case gcvSURF_R5G6B5:
            break;

        default:
            sysOutput("*ERROR* the target format %d is not supported by the hardware.\n",
                g_Runtime.format);
            return gcvFALSE;
        }
    }

#if defined(LINUX) || defined(ANDROID)
    /* Query the amount of video memory. */
    status = gcoHAL_QueryVideoMemory(g_Runtime.hal,
                                     &g_InternalPhysical, &g_InternalSize,
                                     &g_ExternalPhysical, &g_ExternalSize,
                                     &g_ContiguousPhysical, &g_ContiguousSize);
    if (gcmIS_ERROR(status))
    {
        sysOutput("gcoHAL_QueryVideoMemory failed %d.", status);
        return gcvFALSE;
    }
    /* Map the local internal memory. */
    if (g_InternalSize > 0)
    {
        status = gcoHAL_MapMemory(g_Runtime.hal,
                                  g_InternalPhysical, g_InternalSize,
                                  &g_Internal);
        if (gcmIS_ERROR(status))
        {
            sysOutput("gcoHAL_MapMemory failed %d.", status);
            return gcvFALSE;
        }
    }

    /* Map the local external memory. */
    if (g_ExternalSize > 0)
    {
        status = gcoHAL_MapMemory(g_Runtime.hal,
                                  g_ExternalPhysical, g_ExternalSize,
                                  &g_External);
        if (gcmIS_ERROR(status))
        {
            sysOutput("gcoHAL_MapMemory failed %d.", status);
            return gcvFALSE;
        }
    }

    /* Map the contiguous memory. */
    if (g_ContiguousSize > 0)
    {
        status = gcoHAL_MapMemory(g_Runtime.hal,
                                  g_ContiguousPhysical, g_ContiguousSize,
                                  &g_Contiguous);
        if (gcmIS_ERROR(status))
        {
            sysOutput("gcoHAL_MapMemory failed %d.", status);
            return gcvFALSE;
        }
    }
#endif

    status = gcoHAL_Get2DEngine(g_Runtime.hal, &g_Runtime.engine2d);
    if (status < 0)
    {
        sysOutput("*ERROR* Failed to get 2D engine object (status = %d)\n", status);
        return gcvFALSE;
    }


    if (g_Runtime.createTarget)
    {
        void *vaddr = gcvNULL;
        size_t stride;
        gceHARDWARE_TYPE type;
        unsigned long phys = ~0U;

        vaddr = create_drm_buffer(g_Runtime.width, g_Runtime.height, g_Runtime.format, &stride);
        if (!vaddr) {
            sysOutput("*ERROR*: Failed to create drm target surface ");
            return gcvFALSE;
        }

        gcoHAL_GetHardwareType(gcvNULL, &type);

        /* now that we created gem buffer and mapped it to this process, we need to
         * map this to GC320 MMU. First we get the hardware type, to figure which
         * version of MMU is the HAL using. Then set this buffer as an underlying
         * buffer to the gcoSURF object.
         */
        //set_default_hardware_type();

        /* create a wrapper gcoSURF surface object, because we are mapping the user
         * allocated buffer pool should be gcvPOOL_USER */
        status = gcoSURF_Construct(
                gcvNULL,
                g_Runtime.width,
                g_Runtime.height,
                1,
                gcvSURF_BITMAP,
                g_Runtime.format,
                gcvPOOL_USER,
                &g_Runtime.target);
        if (status < 0) {
            sysOutput("*ERROR* Failed to create gcoSURF object");
            return gcvFALSE;
        }

        /* set the underlying buffer */
        status = gcoSURF_SetBuffer(
                    g_Runtime.target,
                    gcvSURF_BITMAP,
                    g_Runtime.format,
                    stride,
                    vaddr,
                    phys);
        if (status < 0) {
            sysOutput("*ERROR* Failed to set buffer for gcoSURF object");
            return gcvFALSE;
        }
        
        /* set window size */
        status = gcoSURF_SetWindow(
                    g_Runtime.target, 0, 0, g_Runtime.width, g_Runtime.height);
        if (status < 0) {
            sysOutput("*ERROR* Failed to set window for gcoSURF object");
            return gcvFALSE;
        }

        gcoHAL_SetHardwareType(gcvNULL, type);

    }
    else
        g_Runtime.target = gcvNULL;


    g_Runtime.pe20      = gcoHAL_IsFeatureAvailable(g_Runtime.hal, gcvFEATURE_2DPE20);
#if gcvVERSION_MAJOR >= 4
    g_Runtime.fullDFB     = gcoHAL_IsFeatureAvailable(g_Runtime.hal, gcvFEATURE_FULL_DIRECTFB);
#else
    g_Runtime.fullDFB     = gcvFALSE;
#endif

    // log chip info
   
    GalTest2D = (Test2D*)malloc(sizeof(Test2D));
    if(!GalTest2D){
	sysOutput("Error: Failed to allocate Test2D\n");
	return gcvFALSE;
    }

    GalTest2D->runtime    = &g_Runtime;
    GalTest2D->dstSurf    = g_Runtime.target;
    GalTest2D->dstFormat = g_Runtime.format;
    GalTest2D->dstWidth = 0;
    GalTest2D->dstHeight = 0;
    GalTest2D->dstStride = 0;
    GalTest2D->dstPhyAddr = 0;
    GalTest2D->dstLgcAddr = 0;

    GalTest2D->src1Surf    = gcvNULL;
    GalTest2D->src1Width = 0;
    GalTest2D->src1Height = 0;
    GalTest2D->src1Stride[0] = 0;
    GalTest2D->src1Stride[1] = 0;
    GalTest2D->src1Stride[2] = 0;
    GalTest2D->src1PhyAddr[0] = 0;
    GalTest2D->src1LgcAddr[0] = 0;
    GalTest2D->src1PhyAddr[1] = 0;
    GalTest2D->src1LgcAddr[1] = 0;
    GalTest2D->src1PhyAddr[2] = 0;
    GalTest2D->src1LgcAddr[2] = 0;
    GalTest2D->src1Format = gcvSURF_UNKNOWN;

    GalTest2D->src2Surf    = gcvNULL;
    GalTest2D->src2Width = 0;
    GalTest2D->src2Height = 0;
    GalTest2D->src2Stride[0] = 0;
    GalTest2D->src2Stride[1] = 0;
    GalTest2D->src2Stride[2] = 0;
    GalTest2D->src2PhyAddr[0] = 0;
    GalTest2D->src2LgcAddr[0] = 0;
    GalTest2D->src2PhyAddr[1] = 0;
    GalTest2D->src2LgcAddr[1] = 0;
    GalTest2D->src2PhyAddr[2] = 0;
    GalTest2D->src2LgcAddr[2] = 0;
    GalTest2D->src2Format = gcvSURF_UNKNOWN;

    gcoSURF_GetAlignedSize(GalTest2D->dstSurf,
			&GalTest2D->dstWidth,
			&GalTest2D->dstHeight,
			&GalTest2D->dstStride);

    gcoSURF_Lock(GalTest2D->dstSurf, &GalTest2D->dstPhyAddr, &GalTest2D->dstLgcAddr);

    return gcvTRUE;
}

/*
 *  Name:       Finalize()
 *  Returns:    None.
 *  Parameters: None.
 *  Description:Free all resource that the framework has used. These may include the memory resource it used, and the egl system resource and so on.
 *              Here it includes "finalize the test object(case)", "free egl resource", "free library resource", "finalize output file resource" and
 *              "destroy win32 resource".
*/
static void Finalize()
{

    if (g_Runtime.hal != gcvNULL)
    {
        gcoHAL_Commit(g_Runtime.hal, gcvTRUE);
    }

    if(GalTest2D->src1Surf != NULL)
    {
	if(GalTest2D->src1LgcAddr)
	  gcoSURF_Unlock(GalTest2D->src1Surf, GalTest2D->src1LgcAddr);

	GalTest2D->src1LgcAddr[0] = NULL;	
	GalTest2D->src1LgcAddr[1] = NULL;	
	GalTest2D->src1LgcAddr[2] = NULL;	

	gcoSURF_Destroy(GalTest2D->src1Surf);

    }
    if(GalTest2D->src2Surf != NULL)
    {
	if(GalTest2D->src2LgcAddr)
	  gcoSURF_Unlock(GalTest2D->src2Surf, GalTest2D->src2LgcAddr);

	GalTest2D->src2LgcAddr[0] = NULL;	
	GalTest2D->src2LgcAddr[1] = NULL;	
	GalTest2D->src2LgcAddr[2] = NULL;	

	gcoSURF_Destroy(GalTest2D->src2Surf);

    }
    if (g_Runtime.target != gcvNULL)
    {
        gceHARDWARE_TYPE type;

        gcoHAL_GetHardwareType(gcvNULL, &type);
        //set_default_hardware_type();

        gcmVERIFY_OK(gcoSURF_Unlock(g_Runtime.target, gcvNULL));
        gcmVERIFY_OK(gcoSURF_Destroy(g_Runtime.target));
        gcmVERIFY_OK(gcoHAL_Commit(gcvNULL, gcvTRUE));

        gcoHAL_SetHardwareType(gcvNULL, type);

    }

    
    close_fd();

#if defined(LINUX) || defined(ANDROID)
    if (g_Internal != gcvNULL)
    {
        /* Unmap the local internal memory. */
        gcmVERIFY_OK(gcoHAL_UnmapMemory(g_Runtime.hal,
                                        g_InternalPhysical, g_InternalSize,
                                        g_Internal));
    }

    if (g_External != gcvNULL)
    {
        /* Unmap the local external memory. */
        gcmVERIFY_OK(gcoHAL_UnmapMemory(g_Runtime.hal,
                                        g_ExternalPhysical, g_ExternalSize,
                                        g_External));
    }

    if (g_Contiguous != gcvNULL)
    {
        /* Unmap the contiguous memory. */
        gcmVERIFY_OK(gcoHAL_UnmapMemory(g_Runtime.hal,
                                        g_ContiguousPhysical, g_ContiguousSize,
                                        g_Contiguous));
    }
#endif

    if (g_Runtime.hal != gcvNULL)
    {
        gcoHAL_Commit(g_Runtime.hal, gcvTRUE);
        gcoHAL_Destroy(g_Runtime.hal);
    }

    if (g_Runtime.os != gcvNULL)
    {
        gcoOS_Destroy(g_Runtime.os);
    }

    GalFinalizeOutput();

    free(GalTest2D);
}


static gctBOOL Render(Test2D *t2d)
{
	gctUINT8 horKernel = 1, verKernel = 1;
	gcsRECT srcRect;
	gco2D egn2D = t2d->runtime->engine2d;
	gceSTATUS status;
	//size_t stride;
	//void* VirtAddr;
        //gceHARDWARE_TYPE type;
        //unsigned long phys = ~0U;
	gcsRECT srcRect2 ;
    gctUINT numPlanes;
    gctINT temp;
	gceSURF_ROTATION srcRotation = gcvSURF_0_DEGREE, dstRotation = gcvSURF_0_DEGREE;
	gcsRECT dstRect = {0, 0, t2d->dstWidth, t2d->dstHeight};
	gcsRECT clipRect = {0, 0, t2d->dstWidth , t2d->dstHeight };
	Gal2DCleanSurface(g_Runtime.hal, g_Runtime.target, COLOR_ARGB8(0x00, 0x00, 0x00, 0x00));

	if ( LoadFileToSurface("src1", t2d) == 0 ) 
	{	
        
        printf("DBG : src1 processing. at %s\n", __FUNCTION__);
        
        gcmONERROR(gcoSURF_GetAlignedSize(t2d->src1Surf,
	  				gcvNULL,
					gcvNULL,
					t2d->src1Stride));

		gcmONERROR(gcoSURF_GetSize(t2d->src1Surf,
					&t2d->src1Width,
					&t2d->src1Height,
					gcvNULL));

		gcmONERROR(gcoSURF_GetFormat(t2d->src1Surf, gcvNULL, &t2d->src1Format));

		gcmONERROR(gcoSURF_Lock(t2d->src1Surf, t2d->src1PhyAddr, t2d->src1LgcAddr));

		if(GOOD_RECT(src1rect))
		{

		srcRect.left = src1rect.left;
		srcRect.top = src1rect.top;
		srcRect.right = src1rect.right;
		srcRect.bottom = src1rect.bottom;

		}
		else 
		{
			srcRect.left = 0;
			srcRect.top = 0;
			srcRect.right = t2d->src1Width;
			srcRect.bottom = t2d->src1Height;
		}

		if(GOOD_RECT(dstrect)){
	
			dstRect.left = dstrect.left;
			dstRect.top = dstrect.top;
			dstRect.right = dstrect.right;
			dstRect.bottom = dstrect.bottom;
		}
		// set clippint rect
		gcmONERROR(gco2D_SetClipping(egn2D, &dstRect));
	
		// set kernel size
		gcmONERROR(gco2D_SetKernelSize(egn2D, horKernel, verKernel));
		
		//printf("DBG : before gcoSURF_FilterBlit. at %s\n", __FUNCTION__);
		gcmONERROR(gcoSURF_FilterBlit(t2d->src1Surf, t2d->dstSurf, &srcRect, &dstRect, &dstRect));
		//printf("DBG : after gcoSURF_FilterBlit. at %s\n", __FUNCTION__);
		
		//DPRINTF("after gcoSURF_FilterBlit.\n"); 	
	}	
	
	if(src2File) {

		printf("src2 load.\n");
		LoadFileToSurface("src2", t2d);
		
		
#ifdef TIMEDEBUG
		gettimeofday(&timeStart, NULL);
#endif		
		DPRINTF("before alphablending.\n");
		
		gcmONERROR(gcoSURF_GetAlignedSize(t2d->src2Surf,
					gcvNULL,
					gcvNULL,
					t2d->src2Stride));

		gcmONERROR(gcoSURF_GetSize(t2d->src2Surf,
					&t2d->src2Width,
					&t2d->src2Height,
					gcvNULL));
		
		printf("DBG : src2Width(%d) src2Height(%d) at %s\n", t2d->src2Width, t2d->src2Height, __FUNCTION__);
		
		gcmONERROR(gcoSURF_GetFormat(t2d->src2Surf, gcvNULL, &t2d->src2Format));
		gcmONERROR(gcoSURF_Lock(t2d->src2Surf, t2d->src2PhyAddr, t2d->src2LgcAddr));

		if(src2Angle != 0 && src2Angle != 90 && src2Angle != 180 && src2Angle != 270)
			src2Angle = 0;
		if(GOOD_RECT(src2rect)){

			srcRect2.left = src2rect.left;
			srcRect2.top = src2rect.top;
			srcRect2.right = src2rect.right;
			srcRect2.bottom = src2rect.bottom;

		}
		else {
			srcRect2.left = 0;
			srcRect2.top = 0;
			srcRect2.right = t2d->src2Width;
			srcRect2.bottom = t2d->src2Height;
		}

		if(GOOD_RECT(src2auxdstrect)){

			dstRect.left = src2auxdstrect.left;
			dstRect.top = src2auxdstrect.top;
			dstRect.right = src2auxdstrect.right;
			dstRect.bottom = src2auxdstrect.bottom;

		}
		else {
			dstRect.left= 0;
			dstRect.top = 0;
#if 1	
			dstRect.right = g_Runtime.width;
			dstRect.bottom = g_Runtime.height;
#else	 /* ���ĺ���������� �۰� �ϰ� ���� */		
			dstRect.right = 256;
			dstRect.bottom = 256;
#endif			
		}
		if(src2Angle == 180) {
			gcmONERROR(gco2D_SetBitBlitMirror(egn2D, gcvTRUE, gcvTRUE));
		}
		else if(src2Angle == 90) {
		     dstRotation = gcvSURF_90_DEGREE;
		     temp = clipRect.right;
		     clipRect.right = clipRect.bottom;
		     clipRect.bottom = temp;
		}
		else if(src2Angle == 270) {
		     srcRotation = gcvSURF_90_DEGREE;
		     temp = srcRect2.right;
		     srcRect2.right = srcRect2.bottom;
		     srcRect2.bottom = temp;

		}
		numPlanes = GetAddressStrideNum(t2d->src2Format);
		gcmONERROR(gco2D_SetGenericSource(egn2D,
        			t2d->src2PhyAddr, numPlanes,
        			(gctUINT*)t2d->src2Stride, numPlanes,
        			gcvLINEAR,
        			t2d->src2Format,
        			srcRotation,
        			t2d->src2Width,
        			t2d->src2Height));
		gcmONERROR(gco2D_SetSource(egn2D, &srcRect2));

		gcmONERROR(gco2D_SetTarget(egn2D, t2d->dstPhyAddr, t2d->dstStride, dstRotation, t2d->dstWidth));

		gcmONERROR(gco2D_SetClipping(egn2D, &clipRect));
#if 1
		gcmONERROR(gco2D_SetSourceGlobalColorAdvanced(egn2D, 0x80 << 24));

		gcmONERROR(gco2D_SetTargetGlobalColorAdvanced(egn2D, 0x80 << 24));

		gcmONERROR(gco2D_EnableAlphaBlendAdvanced(egn2D,
					gcvSURF_PIXEL_ALPHA_STRAIGHT, gcvSURF_PIXEL_ALPHA_STRAIGHT,
					gcvSURF_GLOBAL_ALPHA_ON, gcvSURF_GLOBAL_ALPHA_ON,
					gcvSURF_BLEND_STRAIGHT, gcvSURF_BLEND_STRAIGHT));
#endif
		// gcmONERROR(gco2D_Blit(egn2D, 1, &dstRect, 0xCC, 0xCC, t2d->dstFormat));
		 //stretch and blit 
		gcmONERROR(gco2D_SetStretchRectFactors(
					egn2D,
					&srcRect2,
					&dstRect
					));

		gcmONERROR(gco2D_StretchBlit(
					egn2D,
					1,
					&dstRect,
					0xCC, 0xCC,
					t2d->dstFormat
					));
		gcmONERROR(gco2D_SetBitBlitMirror(egn2D, gcvFALSE, gcvFALSE));
		gcmONERROR(gco2D_DisableAlphaBlend(egn2D));
	}

#if 0
	gcmONERROR(gco2D_Flush(egn2D));
#else
	printf("DBG : skip flush.. at %s\n", __FUNCTION__);
#endif

	gcmONERROR(gcoHAL_Commit(t2d->runtime->hal, gcvTRUE));
	DPRINTF("after gcoHAL_Commit.\n"); 	
	
	printf("DBG : width(%d) height(%d) stride(%d) at %s\n", GalTest2D->dstWidth, GalTest2D->dstHeight, GalTest2D->dstStride, __FUNCTION__);
	GalSaveDIB(GalTest2D->dstLgcAddr, gcvSURF_A8R8G8B8, GalTest2D->dstStride, GalTest2D->dstWidth, GalTest2D->dstHeight, "/tmp/test.bmp");
    return gcvTRUE;

OnError:

    GalOutput(GalOutputType_Error | GalOutputType_Console,
        "%s(%d) failed:%s\n",__FUNCTION__, __LINE__, gcoOS_DebugStatus2Name(status));

    return gcvFALSE;
}
/*
 *  Name:       Run()
 *  Returns:    None.
 *  Parameters: None.
*/

/*******************************************************************************
**
**  main
**
**  Test entry point.
**
**  INPUT:
**
**      Command lines.
**
**  OUTPUT:
**
**      Nothing.
**
**  RETURN:
**
**      int
**          Exit value.
*/
int main(int argc, const char *argv[])
{
    /* Assume failure. */
    int result = -1;
    const char* bmpFileName ;

    if(argc < 2){
        Usage(argv[0]);
	return 1;
    }

    if(!ParseCmdLine(argc, argv))
	return 1;

    bmpFileName = dstFile;
    /* default arguements */
    memset(&g_Runtime, 0, sizeof(g_Runtime));

    /* target surface. */
    g_Runtime.target    = gcvNULL;
    if(dstGeom.width != 0 && dstGeom.height != 0){
    	g_Runtime.width   = dstGeom.width;
    	g_Runtime.height  = dstGeom.height;
    }
    else {
    	g_Runtime.width   = 800;
    	g_Runtime.height  = 480;
    }
    
    printf("DBG : width(%d) height(%d)  at %s\n", g_Runtime.width, g_Runtime.height, __FUNCTION__);
    
    g_Runtime.format  = gcvSURF_A8R8G8B8;
    //g_Runtime.format  = gcvSURF_NV12;    
    //printf("DBG : force gcvSURF_NV12. at %s\n", __FUNCTION__);
    
    g_Runtime.pool  = gcvPOOL_DEFAULT;

    g_Runtime.argc      = 0;
    g_Runtime.argv      = g_Argv2Test;
    g_Runtime.saveTarget    = gcvTRUE;
    g_Runtime.noSaveTargetNew = gcvFALSE;
    // error
    //g_Runtime.cleanTarget = gcvTRUE;
    g_Runtime.createTarget = gcvTRUE;
    // error
    //g_Runtime.notSupport = gcvFALSE;
	


    do
    {
        /* Initialize the test. */
        if (!Initialize())
        {
            /*
            if (!g_Runtime.notSupport)
                sysOutput("Initialize test failed\n");
            else
                sysOutput("Cannot initialize test because hw feature is not supported.\n");
            */
                
            result = 0;
            break;
        }
	
	//sleep(1);
	//DPRINTF("before Render.\n"); 	
	Render(GalTest2D);
	//DPRINTF("after Render.\n"); 	
	
	if(bmpFileName)
        	GalSaveSurface2DIB(g_Runtime.target, bmpFileName);

        result = 0;
    }
    while (gcvFALSE);

    /* Cleanup the test environment. */
    Finalize();
    sysOutput("Successfully completed\n");	
    return result;
}

/*
 * Copyright (c) 2012 Arvin Schnell <arvin.schnell@gmail.com>
 * Copyright (c) 2012 Rob Clark <rob@ti.com>
 * Copyright (c) 2013 Anand Balagopalakrishnan <anandb@ti.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sub license,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

/* Based on a egl cube test app originally written by Arvin Schnell */

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

#include <xf86drm.h>
#include <xf86drmMode.h>
#include <gbm.h>

#include "esUtil.h"

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

/* #define PBUFFDEF */
#define FBODEF
#define MAX_DISPLAYS 	(4)
uint8_t DISP_ID = 0;
uint8_t all_display = 0;
int8_t connector_id = -1;
static struct {
	EGLDisplay display;
	EGLConfig config;
	EGLContext context;
	EGLSurface surface;
	GLuint program;
	GLuint program_texture;
	GLint samplerLoc;
	GLint modelviewmatrix, modelviewprojectionmatrix, normalmatrix;
	GLuint vbo;
	GLuint positionsoffset, colorsoffset, normalsoffset;
	GLuint vertex_shader, fragment_shader;
} gl;

static struct {
	struct gbm_device *dev;
	struct gbm_surface *surface;
} gbm;

static struct {
	int fd;
	uint32_t ndisp;
	uint32_t crtc_id[MAX_DISPLAYS];
	uint32_t connector_id[MAX_DISPLAYS];
	uint32_t resource_id;
	uint32_t encoder[MAX_DISPLAYS];
	drmModeModeInfo *mode[MAX_DISPLAYS];
	drmModeConnector *connectors[MAX_DISPLAYS];
} drm;

struct drm_fb {
	struct gbm_bo *bo;
	uint32_t fb_id;
};

struct omap_bo* bo_test;
unsigned char* lpOut;

static unsigned long long GetUSTime()
{
	struct timeval now;
	unsigned long long ustime;
	gettimeofday( &now, NULL );
	ustime =  (unsigned long long)now.tv_sec * 1000000LL + (unsigned long long)now.tv_usec;
	return ustime;
}
static int init_drm(void)
{
	static const char *modules[] = {
			"omapdrm", "i915", "radeon", "nouveau", "vmwgfx", "exynos"
	};
	drmModeRes *resources;
	drmModeConnector *connector = NULL;
	drmModeEncoder *encoder = NULL;
	int i, j;
	uint32_t maxRes, curRes;

	printf("[dbg:%s] Load module()\n", __func__);
	for (i = 0; i < ARRAY_SIZE(modules); i++) 
	{
		printf("trying to load module %s...", modules[i]);
//		drm.fd = drmOpen(modules[i], NULL);
    	drm.fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);

		if (drm.fd < 0) 
		{
			printf("failed.\n");
		} 
		else 
		{
			printf("success.\n");
			break;
		}
	}

	if (drm.fd < 0) 
	{
		printf("could not open drm device\n");
		return -1;
	}

	resources = drmModeGetResources(drm.fd);
	if (!resources) 
	{
		printf("drmModeGetResources failed: %s\n", strerror(errno));
		return -1;
	}
	drm.resource_id = (uint32_t) resources;

	/* find a connected connector: */
	for (i = 0; i < resources->count_connectors; i++) 
	{
		connector = drmModeGetConnector(drm.fd, resources->connectors[i]);
		if (connector->connection == DRM_MODE_CONNECTED) 
		{
			/* choose the first supported mode */
			drm.mode[drm.ndisp] = &connector->modes[0];
			drm.connector_id[drm.ndisp] = connector->connector_id;

			for (j=0; j<resources->count_encoders; j++) 
			{
				encoder = drmModeGetEncoder(drm.fd, resources->encoders[j]);
				if (encoder->encoder_id == connector->encoder_id)
					break;

				drmModeFreeEncoder(encoder);
				encoder = NULL;
			}

			if (!encoder) 
			{
				printf("no encoder!\n");
				return -1;
			}

			drm.encoder[drm.ndisp]  = (uint32_t) encoder;
			drm.crtc_id[drm.ndisp] = encoder->crtc_id;
			drm.connectors[drm.ndisp] = connector;

			printf("### Display [%d]: CRTC = %d, Connector = %d\n", drm.ndisp, drm.crtc_id[drm.ndisp], drm.connector_id[drm.ndisp]);
			printf("\tMode chosen [%s] : Clock => %d, Vertical refresh => %d, Type => %d\n", drm.mode[drm.ndisp]->name, drm.mode[drm.ndisp]->clock, drm.mode[drm.ndisp]->vrefresh, drm.mode[drm.ndisp]->type);
			printf("\tHorizontal => %d, %d, %d, %d, %d\n", drm.mode[drm.ndisp]->hdisplay, drm.mode[drm.ndisp]->hsync_start, drm.mode[drm.ndisp]->hsync_end, drm.mode[drm.ndisp]->htotal, drm.mode[drm.ndisp]->hskew);
			printf("\tVertical => %d, %d, %d, %d, %d\n", drm.mode[drm.ndisp]->vdisplay, drm.mode[drm.ndisp]->vsync_start, drm.mode[drm.ndisp]->vsync_end, drm.mode[drm.ndisp]->vtotal, drm.mode[drm.ndisp]->vscan);

			/* If a connector_id is specified, use the corresponding display */
			if ((connector_id != -1) && (connector_id == drm.connector_id[drm.ndisp]))
			{
				DISP_ID = drm.ndisp;
			}

			/* If all displays are enabled, choose the connector with maximum
			* resolution as the primary display */
			if (all_display) 
			{
				maxRes = drm.mode[DISP_ID]->vdisplay * drm.mode[DISP_ID]->hdisplay;
				curRes = drm.mode[drm.ndisp]->vdisplay * drm.mode[drm.ndisp]->hdisplay;

				if (curRes > maxRes)
				{
					DISP_ID = drm.ndisp;
				}
			}

			drm.ndisp++;
		} else {
			drmModeFreeConnector(connector);
		}
	}

	if (drm.ndisp == 0) {
		/* we could be fancy and listen for hotplug events and wait for
		 * a connector..
		 */
		printf("no connected connector!\n");
		return -1;
	}

	return 0;
}

static int init_gbm(void)
{
	printf("[dbg:%s] gbm_create_device()\n", __func__);
	gbm.dev = gbm_create_device(drm.fd);

	gbm.surface = gbm_surface_create(gbm.dev, drm.mode[DISP_ID]->hdisplay, drm.mode[DISP_ID]->vdisplay,	GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING );
	if (!gbm.surface) {
		printf("failed to create gbm surface\n");
		return -1;
	}

	return 0;
}


static int init_gl(void)
{
	GLchar* vShaderStr =
		"attribute vec4 vPosition;          \n"
		"attribute vec4 aColor;             \n"
		"varying vec4 vColor;               \n"
		"void main()                        \n"
		"{                                  \n"
		"    vColor = aColor;               \n"
		"    gl_Position = vPosition;		\n"
		"}                                  \n"; 

	GLchar* fShaderStr =
		"precision mediump float;  	        \n"
		"varying vec4 vColor;               \n"
		"void main()                        \n"
		"{                                  \n"
		"    gl_FragColor = vColor;			\n"
		"}                                  \n";
//=======================================================

	GLchar* vShaderStr_1 =
		"attribute vec4 vPosition;          \n"
		"attribute vec2 a_texCoord;			\n"
		"varying   vec2 v_texCoord;         \n"
		"void main()                        \n"
		"{                                  \n"
		"	 v_texCoord  = a_texCoord;      \n"
		"    gl_Position = vPosition;		\n"
		"}                                  \n";
	
	GLchar* fShaderStr_1 =
		"precision mediump float;  	        \n"
		"uniform sampler2D s_texture;		\n"
		"varying      vec2 v_texCoord;	    \n"

		"void main()                        \n"
		"{                                  \n"
		"    gl_FragColor = texture2D(s_texture, v_texCoord);\n"
		"}                                  \n";

	static const EGLint context_attribs[] = {
		EGL_CONTEXT_CLIENT_VERSION, 2,
		EGL_NONE
	};
	
#ifdef PBUFFDEF
	static const EGLint config_attribs[] = {
		EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
		EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
		EGL_RED_SIZE, 5,
		EGL_GREEN_SIZE, 6,
		EGL_BLUE_SIZE, 5,
		EGL_DEPTH_SIZE, 1,
		EGL_NONE,
	};
	static const EGLint config_pb_attribs[] = {
		EGL_WIDTH, 1920,
		EGL_HEIGHT, 1080,
		EGL_LARGEST_PBUFFER, EGL_TRUE,
		EGL_NONE,
	};
#else
	static const EGLint config_attribs[] = {
		EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
		EGL_BUFFER_SIZE, 32,
		EGL_RED_SIZE, 8,
		EGL_GREEN_SIZE, 8,
		EGL_BLUE_SIZE, 8,
		EGL_ALPHA_SIZE, 8,
		EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
		EGL_NONE
	};
#endif
	int major, minor, n;
	gl.display = eglGetDisplay(gbm.dev);
	if (!eglInitialize(gl.display, &major, &minor)) {
		printf("failed to initialize\n");
		return -1;
	}
	
	if (!eglBindAPI(EGL_OPENGL_ES_API)) {
		printf("failed to bind api EGL_OPENGL_ES_API\n");
		return -1;
	}

	if (!eglChooseConfig(gl.display, config_attribs, &gl.config, 1, &n) || n != 1) {
		printf("failed to choose config: %d\n", n);
		return -1;
	}
	
	gl.context = eglCreateContext(gl.display, gl.config, EGL_NO_CONTEXT, context_attribs);
	if (gl.context == NULL) {
		printf("failed to create context\n");
		return -1;
	}
#ifdef PBUFFDEF
	gl.surface = eglCreatePbufferSurface(gl.display, gl.config, config_pb_attribs);
	if (gl.surface == EGL_NO_SURFACE) {
		printf("failed to create egl surface\n");
		return -1;
	}
#else
	gl.surface = eglCreateWindowSurface(gl.display, gl.config, gbm.surface, NULL);
	if (gl.surface == EGL_NO_SURFACE) {
		printf("failed to create egl surface\n");
		return -1;
	}
#endif
	eglMakeCurrent(gl.display, gl.surface, gl.surface, gl.context);

	GLint compiled = 0;
	GLint logLen = 0;
	GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vertexShader, 1, &vShaderStr, NULL);
	glCompileShader(vertexShader);
	glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &compiled);
	if (!compiled)
	{
		printf("[dbg:%s] vertex compilation failed.\n", __func__);
		glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &logLen);
		if (logLen > 1) {
			GLchar* log = malloc(sizeof(char) * logLen);
			glGetProgramInfoLog(vertexShader, logLen, NULL, log);
			printf("[err:%s] %s\n", __func__, log+8);
			free(log);
		}
		glDeleteShader(vertexShader);
		return -1;
	}
	GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fragShader, 1, &fShaderStr, NULL);
	glCompileShader(fragShader);
	glGetShaderiv(fragShader, GL_COMPILE_STATUS, &compiled);
	if (!compiled) 
	{
		printf("[dbg:%s] frag compilation failed.\n", __func__);
		glDeleteShader(fragShader);
		return -1;
	}

	GLuint program = glCreateProgram();
	if (!program) 
	{
		printf("[dbg:%s] create program failed.\n", __func__);
		return -1;
	}

	glAttachShader(program, vertexShader);
	glAttachShader(program, fragShader);

	glBindAttribLocation(program, 0, "aColor");
	glBindAttribLocation(program, 1, "vPosition");

	glLinkProgram(program);

	GLint linked;
	glGetProgramiv(program, GL_LINK_STATUS, &linked);
	if (!linked) 
	{
		GLint infoLen = 0;
		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
		if (infoLen > 1) 
		{
			char* infoLog = malloc(sizeof(char) * infoLen);
			glGetProgramInfoLog(program, infoLen, NULL, infoLog);
			printf("[dbg:%s] linking program failed:\n%s\n", __func__, infoLog);
			free(infoLog);
		}
		glDeleteProgram(program);
		return -1;
	}
	gl.program = program;

	GLuint vertexShader_1 = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vertexShader_1, 1, &vShaderStr_1, NULL);
	glCompileShader(vertexShader_1);
	glGetShaderiv(vertexShader_1, GL_COMPILE_STATUS, &compiled);
	if (!compiled) 
	{
		printf("[dbg:%s] vertex compilation failed.\n", __func__);
		glGetShaderiv(vertexShader_1, GL_INFO_LOG_LENGTH, &logLen);
		if (logLen > 1) {
			GLchar* log = malloc(sizeof(char) * logLen);
			glGetProgramInfoLog(vertexShader_1, logLen, NULL, log);
			printf("[err:%s] %s\n", __func__, log+8);
			free(log);
		}
		glDeleteShader(vertexShader_1);
		return -1;
	}
	GLuint fragShader_1 = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fragShader_1, 1, &fShaderStr_1, NULL);
	glCompileShader(fragShader_1);
	glGetShaderiv(fragShader_1, GL_COMPILE_STATUS, &compiled);
	if (!compiled) 
	{
		printf("[dbg:%s] frag compilation failed.\n", __func__);
		glDeleteShader(fragShader_1);
		return -1;
	}
	program = glCreateProgram();
	if (!program) 
	{
		printf("[dbg:%s] create program failed.\n", __func__);
		return -1;
	}

	glAttachShader(program, vertexShader_1);
	glAttachShader(program, fragShader_1);

	glBindAttribLocation(program, 0, "vPosition");
	glBindAttribLocation(program, 1, "a_texCoord");

	glLinkProgram(program);

	glGetProgramiv(program, GL_LINK_STATUS, &linked);
	if (!linked) 
	{
		GLint infoLen = 0;
		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
		if (infoLen > 1) 
		{
			char* infoLog = malloc(sizeof(char) * infoLen);
			glGetProgramInfoLog(program, infoLen, NULL, infoLog);
			printf("[dbg:%s] linking program failed:\n%s\n", __func__, infoLog);
			free(infoLog);
		}
		glDeleteProgram(program);
		return -1;
	}
	gl.program_texture = program;
	gl.samplerLoc = glGetUniformLocation(gl.program_texture, "s_texture");

	glEnable(GL_BLEND);
	glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
	glClearColor(1.0, 1.0, 1.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	return 0;
}

static void exit_gbm(void)
{
    gbm_surface_destroy(gbm.surface);
    gbm_device_destroy(gbm.dev);
    return;
}

static void exit_gl(void)
{
    glDeleteProgram(gl.program);
    glDeleteBuffers(1, &gl.vbo);
    glDeleteShader(gl.fragment_shader);
    glDeleteShader(gl.vertex_shader);
    eglDestroySurface(gl.display, gl.surface);
    eglDestroyContext(gl.display, gl.context);
    eglTerminate(gl.display);
    return;
}

static void exit_drm(void)
{
    drmModeRes *resources;
    int i;

    resources = (drmModeRes *)drm.resource_id;
    for (i = 0; i < resources->count_connectors; i++) 
	{
            drmModeFreeEncoder(drm.encoder[i]);
            drmModeFreeConnector(drm.connectors[i]);
    }
    drmModeFreeResources(drm.resource_id);
    drmClose(drm.fd);
    return;
}

void cleanup_kmscube(void)
{
	exit_gl();
	exit_gbm();
	exit_drm();
	printf("Cleanup of GL, GBM and DRM completed\n");
	return;
}

static void draw(uint32_t i)
{
	static int cnt = 0;
	// vPosition
	static const GLfloat vVertices_2[] = { -1.0, -1.0, 0,
										   1.0, -1.0, 0,
										   -1.0, 1.0, 0,
										   1.0, 1.0, 0};
	// a_texCoord
	static const GLfloat vVertices_3[] = { 0.0, 0.0,
										   1.0, 0.0,
										   0.0, 1.0,
										   1.0, 1.0};
	
	static const GLfloat vVertices_4[] = { 0.0, 1.0,
										   1.0, 1.0,
										   0.0, 0.0,
										   1.0, 0.0 };

	int width  = drm.mode[DISP_ID]->hdisplay;
	int height = drm.mode[DISP_ID]->vdisplay;

	glDisable(GL_DEPTH_TEST);
#ifdef FBODEF
	GLuint tb[10];

	for (int i = 0; i < 2; i++) 
	{
		glGenTextures(1, &tb[i]);
		//http://blog.naver.com/mywjdp/220802452130
		glBindTexture(GL_TEXTURE_2D, tb[i]);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		
		unsigned long long prev = GetUSTime();
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8_OES, 1280, 720, 0, GL_RGBA, GL_UNSIGNED_BYTE, lpOut);
		printf("[dbg:%s] glTexImage2D elapsed time[%d]: %llu\n", __func__, i, GetUSTime() - prev);
	}

	glBindTexture(GL_TEXTURE_2D, 0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glUseProgram(gl.program_texture);

	for (int i = 0; i < 2; i++) 
	{
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vVertices_2);
		glEnableVertexAttribArray(0);
		if ( i & 1 ) 
		{
			glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vVertices_3);
			glEnableVertexAttribArray(1);
		}
		else 
		{
			glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vVertices_4);
			glEnableVertexAttribArray(1);
		}
		/* printf("[dbg:%s] glActiveTexture(%d) \n", __func__, i); */

		glActiveTexture(GL_TEXTURE0 + i);
		glBindTexture(GL_TEXTURE_2D, tb[i]);
		glUniform1i(gl.samplerLoc, i);

		unsigned long long prev = GetUSTime();
		glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
		printf("[dbg:%s] Blend elapsed time[%d]: %llu\n", __func__, i, GetUSTime() - prev);
	}

#endif

}

static void
drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
{
	struct drm_fb *fb = data;
	struct gbm_device *gbm = gbm_bo_get_device(bo);

	if (fb->fb_id)
	{
		drmModeRmFB(drm.fd, fb->fb_id);
	}

	free(fb);
}

static struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo)
{
	struct drm_fb *fb = gbm_bo_get_user_data(bo);
	uint32_t width, height, stride, handle;
	int ret;

	if (fb)
	{
		return fb;
	}

	fb = calloc(1, sizeof *fb);
	fb->bo = bo;

	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(drm.fd, width, height, 24, 32, stride, handle, &fb->fb_id);
	if (ret) 
	{
		printf("failed to create fb: %s\n", strerror(errno));
		free(fb);
		return NULL;
	}

	gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);

	return fb;
}

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

void print_usage()
{
	printf("Usage : kmscube <options>\n");
	printf("\t-h : Help\n");
	printf("\t-a : Enable all displays\n");
	printf("\t-c <id> : Display using connector_id [if not specified, use the first connected connector]\n");
	printf("\t-n <number> (optional): Number of frames to render\n");
}

int kms_signalhandler(int signum)
{
	switch(signum) 
	{
		case SIGINT:
	        case SIGTERM:
	                /* Allow the pending page flip requests to be completed before
	                 * the teardown sequence */
	                sleep(1);
	                printf("Handling signal number = %d\n", signum);
			cleanup_kmscube();
			break;
		default:
			printf("Unknown signal\n");
			break;
	}
	exit(1);
}

#pragma pack(1)
typedef struct tagBMP_FILEHEADER {
	unsigned short	bmpType;		//0x00
	unsigned long	dwSize;			//0x02
	unsigned long	dwRev0;			//0x06
	unsigned long	dwDataOffset;	//0x0a

} BMP_FILEHEADER, *LPBMP_FILEHEADER;

typedef struct tagBMP_INFOHEADER {
	unsigned long	headerSize;			//0x0e
	signed long		width;				//0x12
	signed long		height;				//0x16
	unsigned short	planes;				//0x1a
	unsigned short	bitsPerPixel;		//0x1c
	unsigned long	compression;		//0x1e
	unsigned long	sizeOfBitmap;		//0x22
	signed long		horzResolution;		//0x26
	signed long		vertResolution;		//0x2a
	unsigned long	colorsUsed;			//0x2e	
	unsigned long	colorsImportant;	//0x32
	
} BMP_INFOHEADER, *LPBMP_INFOHEADER;
#pragma pack()
int LoadSpriteData( unsigned char* lpBuf, unsigned char* lpOutAdd )
{
	printf("[dbg:%s] lpIn: %p, lpOut: %p\n", __func__, lpBuf, lpOutAdd);
	printf("[dbg:%s] clorsImportant: %p\n", __func__, (unsigned char*)&((LPBMP_INFOHEADER)0x00)->colorsImportant + 0x0e);

	int Ret = 0;
	LPBMP_FILEHEADER lpFileHeader = (LPBMP_FILEHEADER)lpBuf;
	LPBMP_INFOHEADER lpInfoHeader = (LPBMP_INFOHEADER)(lpBuf + sizeof(BMP_FILEHEADER));
	unsigned char* lpTmp = lpBuf + lpFileHeader->dwDataOffset;
	printf("[dbg:%s] Data: %p\n", __func__, lpTmp);
	printf("[dbg:%s] [%d x %d]\n", __func__, lpFileHeader->dwSize, lpInfoHeader->bitsPerPixel);

	int xcou;
	int Width = 1280;
	int Height = 720;

	unsigned char* lpDataAdd = lpTmp + Width * (Height - 1) * 4;
	printf("[dbg:%s] lpDataAdd: %p\n", __func__, lpDataAdd);

	while ( Height-- )
	{
		for ( xcou = 0; xcou < Width; xcou++ )
		{
			*lpOutAdd++ = lpDataAdd[3]; // Red
			*lpOutAdd++ = lpDataAdd[2]; // Green
			*lpOutAdd++ = lpDataAdd[1]; // Blue
			*lpOutAdd++ = lpDataAdd[0]; // Alpha

			lpDataAdd += 4;
		}
		lpDataAdd -= (Width * 2) * 4;
	}
	Ret = 1;

	return Ret;
}

int main(int argc, char *argv[])
{
	fd_set fds;
	drmEventContext evctx = {
			.version = DRM_EVENT_CONTEXT_VERSION,
			.page_flip_handler = page_flip_handler,
	};
	struct gbm_bo *bo;
	struct drm_fb *fb;
	uint32_t i = 0;
	int ret;
	int opt;
	int frame_count = -1;

	printf("[dbg:%s] init_drm()\n", __func__);
	ret = init_drm();
	printf("### Primary display => ConnectorId = %d, Resolution = %dx%d\n",	drm.connector_id[DISP_ID], drm.mode[DISP_ID]->hdisplay,	drm.mode[DISP_ID]->vdisplay);

	FD_ZERO(&fds);
	FD_SET(drm.fd, &fds);

	printf("[dbg:%s] init_gbm()\n", __func__);
	ret = init_gbm();
	if (ret) 
	{
		printf("failed to initialize GBM\n");
		return ret;
	}

	printf("[dbg:%s] init_gl()\n", __func__);
	ret = init_gl();
	if (ret) 
	{
		printf("failed to initialize EGL\n");
		return ret;
	}

	printf("[dbg:%s] Init screen\n", __func__ );
	/* clear the color buffer */
	/* glClearColor(0.0, 0.0, 0.0, 0.0); */
	/* glClear(GL_COLOR_BUFFER_BIT); */
	eglSwapBuffers(gl.display, gl.surface);
	bo = gbm_surface_lock_front_buffer(gbm.surface);
	fb = drm_fb_get_from_bo(bo);

	printf("[dbg:%s] drmModeSetCrtc()\n", __func__);
	/* set mode: */
	if (all_display) 
	{
		for (i=0; i<drm.ndisp; i++) 
		{
			ret = drmModeSetCrtc(drm.fd, drm.crtc_id[i], fb->fb_id, 0, 0, &drm.connector_id[i], 1, drm.mode[i]);
			if (ret) 
			{
				printf("display %d failed to set mode: %s\n", i, strerror(errno));
				return ret;
			}
		}
	} 
	else 
	{
		ret = drmModeSetCrtc(drm.fd, drm.crtc_id[DISP_ID], fb->fb_id, 0, 0, &drm.connector_id[DISP_ID], 1, drm.mode[DISP_ID]);
		if (ret) 
		{
			printf("display %d failed to set mode: %s\n", DISP_ID, strerror(errno));
			return ret;
		}
	}

	// bmp load
	bo_test = omap_bo_new(omap_device_new(drm.fd), 1280 * 720 * 4, 3);
	printf("omap_bo_new\n");

	lpOut = omap_bo_map(bo_test);
	printf("lpOut: %p\n", lpOut);
	printf("omap_bo_map\n");

	FILE* lpFile;
	lpFile = fopen("/home/root/bmp720.bmp", "r");
	fseek(lpFile, 0, SEEK_END);
	int FileSize = ftell(lpFile);
	unsigned char* lpBuf = (unsigned char*)malloc(FileSize);
	fseek(lpFile, 0, SEEK_SET);
	fread(lpBuf, FileSize, 1, lpFile);
	printf("lpIn: %p\n", lpBuf);
	fclose(lpFile);

	LoadSpriteData(lpBuf, lpOut);
	printf("LoadSpriteData\n");

	int done = 0;
	while (frame_count != 0) 
	{
		struct gbm_bo *next_bo;
		int waiting_for_flip = 1;

		if (done == 1)
		{
			printf("Exit Sleep 10 sec \n");
			sleep(10);
			break;
		}

		printf("[dbg:%s] draw()\n", __func__);
		draw(i++);
		done = 1;

		eglSwapBuffers(gl.display, gl.surface);
		next_bo = gbm_surface_lock_front_buffer(gbm.surface);
		fb = drm_fb_get_from_bo(next_bo);
		/* Here you could also update drm plane layers if you want hw composition */

		ret = drmModePageFlip(drm.fd, drm.crtc_id[DISP_ID], fb->fb_id, DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
		if (ret) 
		{
			printf("failed to queue page flip: %s\n", strerror(errno));
			return -1;
		}

		while (waiting_for_flip) 
		{
			ret = select(drm.fd + 1, &fds, NULL, NULL, NULL);
			if (ret < 0) {
				printf("select err: %s\n", strerror(errno));
				return ret;
			} else if (ret == 0) {
				printf("select timeout!\n");
				return -1;
			} else if (FD_ISSET(0, &fds)) {
				continue;
			}
			drmHandleEvent(drm.fd, &evctx);
		}

		/* release last buffer to render on again: */
		gbm_surface_release_buffer(gbm.surface, bo);
		bo = next_bo;

		if(frame_count >= 0)
		{
			frame_count--;
		}
	}

	cleanup_kmscube();
	printf("\n Exiting kmscube \n");

	omap_bo_del(bo_test);
	free(lpBuf);

	return ret;
}