/* $Id: VBoxFBQGL.cpp 22675 2009-09-01 14:50:32Z vboxsync $ */
/** @file
 * VBoxFBQGL Opengl-based FrameBuffer implementation
 */

/*
 * Copyright (C) 2009 Sun Microsystems, Inc.
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 USA or visit http://www.sun.com if you need
 * additional information or have any questions.
 */
#if defined (VBOX_GUI_USE_QGL)

#include "VBoxFrameBuffer.h"

#include "VBoxConsoleView.h"
#include "VBoxProblemReporter.h"
#include "VBoxGlobal.h"

/* Qt includes */
#include <QGLWidget>

#include <iprt/asm.h>

#ifdef VBOX_WITH_VIDEOHWACCEL
#include <VBox/VBoxVideo.h>
#include <VBox/types.h>
#include <VBox/ssm.h>
#endif
#include <iprt/semaphore.h>

#include <QFile>
#include <QTextStream>

#ifdef VBOXQGL_PROF_BASE
# ifdef VBOXQGL_DBG_SURF
#  define VBOXQGL_PROF_WIDTH 1400
#  define VBOXQGL_PROF_HEIGHT 1050
# else
# define VBOXQGL_PROF_WIDTH 1400
# define VBOXQGL_PROF_HEIGHT 1050
//#define VBOXQGL_PROF_WIDTH 720
//#define VBOXQGL_PROF_HEIGHT 480
# endif
#endif

#define VBOXQGL_STATE_NAMEBASE "QGLVHWAData"
#define VBOXQGL_STATE_VERSION 1
#ifdef DEBUG_misha
# define VBOXQGL_STATE_DEBUG
#endif

#ifdef VBOXQGL_STATE_DEBUG
#define VBOXQGL_STATE_START_MAGIC        0x12345678
#define VBOXQGL_STATE_STOP_MAGIC         0x87654321

#define VBOXQGL_STATE_SURFSTART_MAGIC    0x9abcdef1
#define VBOXQGL_STATE_SURFSTOP_MAGIC     0x1fedcba9

#define VBOXQGL_STATE_OVERLAYSTART_MAGIC 0x13579bdf
#define VBOXQGL_STATE_OVERLAYSTOP_MAGIC  0xfdb97531

#define VBOXQGL_SAVE_START(_pSSM) do{ int rc = SSMR3PutU32(_pSSM, VBOXQGL_STATE_START_MAGIC); AssertRC(rc);}while(0)
#define VBOXQGL_SAVE_STOP(_pSSM) do{ int rc = SSMR3PutU32(_pSSM, VBOXQGL_STATE_STOP_MAGIC); AssertRC(rc);}while(0)

#define VBOXQGL_SAVE_SURFSTART(_pSSM) do{ int rc = SSMR3PutU32(_pSSM, VBOXQGL_STATE_SURFSTART_MAGIC); AssertRC(rc);}while(0)
#define VBOXQGL_SAVE_SURFSTOP(_pSSM) do{ int rc = SSMR3PutU32(_pSSM, VBOXQGL_STATE_SURFSTOP_MAGIC); AssertRC(rc);}while(0)

#define VBOXQGL_SAVE_OVERLAYSTART(_pSSM) do{ int rc = SSMR3PutU32(_pSSM, VBOXQGL_STATE_OVERLAYSTART_MAGIC); AssertRC(rc);}while(0)
#define VBOXQGL_SAVE_OVERLAYSTOP(_pSSM) do{ int rc = SSMR3PutU32(_pSSM, VBOXQGL_STATE_OVERLAYSTOP_MAGIC); AssertRC(rc);}while(0)

#define VBOXQGL_LOAD_CHECK(_pSSM, _v) \
    do{ \
        uint32_t _u32; \
        int rc = SSMR3GetU32(_pSSM, &_u32); AssertRC(rc); \
        if(_u32 != (_v)) \
        { \
            VBOXQGLLOG(("load error: expected magic (0x%x), but was (0x%x)\n", (_v), _u32));\
        }\
        Assert(_u32 == (_v)); \
    }while(0)

#define VBOXQGL_LOAD_START(_pSSM) VBOXQGL_LOAD_CHECK(_pSSM, VBOXQGL_STATE_START_MAGIC)
#define VBOXQGL_LOAD_STOP(_pSSM) VBOXQGL_LOAD_CHECK(_pSSM, VBOXQGL_STATE_STOP_MAGIC)

#define VBOXQGL_LOAD_SURFSTART(_pSSM) VBOXQGL_LOAD_CHECK(_pSSM, VBOXQGL_STATE_SURFSTART_MAGIC)
#define VBOXQGL_LOAD_SURFSTOP(_pSSM) VBOXQGL_LOAD_CHECK(_pSSM, VBOXQGL_STATE_SURFSTOP_MAGIC)

#define VBOXQGL_LOAD_OVERLAYSTART(_pSSM) VBOXQGL_LOAD_CHECK(_pSSM, VBOXQGL_STATE_OVERLAYSTART_MAGIC)
#define VBOXQGL_LOAD_OVERLAYSTOP(_pSSM) VBOXQGL_LOAD_CHECK(_pSSM, VBOXQGL_STATE_OVERLAYSTOP_MAGIC)

#else

#define VBOXQGL_SAVE_START(_pSSM) do{}while(0)
#define VBOXQGL_SAVE_STOP(_pSSM) do{}while(0)

#define VBOXQGL_SAVE_SURFSTART(_pSSM) do{}while(0)
#define VBOXQGL_SAVE_SURFSTOP(_pSSM) do{}while(0)

#define VBOXQGL_SAVE_OVERLAYSTART(_pSSM) do{}while(0)
#define VBOXQGL_SAVE_OVERLAYSTOP(_pSSM) do{}while(0)

#define VBOXQGL_LOAD_START(_pSSM) do{}while(0)
#define VBOXQGL_LOAD_STOP(_pSSM) do{}while(0)

#define VBOXQGL_LOAD_SURFSTART(_pSSM) do{}while(0)
#define VBOXQGL_LOAD_SURFSTOP(_pSSM) do{}while(0)

#define VBOXQGL_LOAD_OVERLAYSTART(_pSSM) do{}while(0)
#define VBOXQGL_LOAD_OVERLAYSTOP(_pSSM) do{}while(0)

#endif

#define VBOXQGL_MAKEFOURCC(ch0, ch1, ch2, ch3)                              \
                ((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) |       \
                ((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24 ))

#define FOURCC_AYUV VBOXQGL_MAKEFOURCC('A', 'Y', 'U', 'V')
#define FOURCC_UYVY VBOXQGL_MAKEFOURCC('U', 'Y', 'V', 'Y')
#define FOURCC_YUY2 VBOXQGL_MAKEFOURCC('Y', 'U', 'Y', '2')
#define FOURCC_YV12 VBOXQGL_MAKEFOURCC('Y', 'V', '1', '2')
#define VBOXVHWA_NUMFOURCC 4

typedef char GLchar;

#ifndef GL_COMPILE_STATUS
# define GL_COMPILE_STATUS 0x8b81
#endif
#ifndef GL_LINK_STATUS
# define GL_LINK_STATUS    0x8b82
#endif
#ifndef GL_FRAGMENT_SHADER
# define GL_FRAGMENT_SHADER 0x8b30
#endif
#ifndef GL_VERTEX_SHADER
# define GL_VERTEX_SHADER 0x8b31
#endif

/* GL_ARB_multitexture */
#ifndef GL_TEXTURE0
# define GL_TEXTURE0                    0x84c0
#endif
#ifndef GL_TEXTURE1
# define GL_TEXTURE1                    0x84c1
#endif
#ifndef GL_MAX_TEXTURE_COORDS
# define GL_MAX_TEXTURE_COORDS          0x8871
#endif
#ifndef GL_MAX_TEXTURE_IMAGE_UNITS
# define GL_MAX_TEXTURE_IMAGE_UNITS     0x8872
#endif

#ifndef APIENTRY
# define APIENTRY
#endif

typedef GLvoid (APIENTRY *PFNVBOXVHWA_ACTIVE_TEXTURE) (GLenum texture);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_MULTI_TEX_COORD2I) (GLenum texture, GLint v0, GLint v1);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_MULTI_TEX_COORD2F) (GLenum texture, GLfloat v0, GLfloat v1);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_MULTI_TEX_COORD2D) (GLenum texture, GLdouble v0, GLdouble v1);

/* GL_ARB_texture_rectangle */
#ifndef GL_TEXTURE_RECTANGLE
# define GL_TEXTURE_RECTANGLE 0x84F5
#endif

/* GL_ARB_shader_objects */
/* GL_ARB_fragment_shader */

typedef GLuint (APIENTRY *PFNVBOXVHWA_CREATE_SHADER)  (GLenum type);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_SHADER_SOURCE)  (GLuint shader, GLsizei count, const GLchar **string, const GLint *length);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_COMPILE_SHADER) (GLuint shader);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_DELETE_SHADER)  (GLuint shader);

typedef GLuint (APIENTRY *PFNVBOXVHWA_CREATE_PROGRAM) ();
typedef GLvoid (APIENTRY *PFNVBOXVHWA_ATTACH_SHADER)  (GLuint program, GLuint shader);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_DETACH_SHADER)  (GLuint program, GLuint shader);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_LINK_PROGRAM)   (GLuint program);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_USE_PROGRAM)    (GLuint program);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_DELETE_PROGRAM) (GLuint program);

typedef GLboolean (APIENTRY *PFNVBOXVHWA_IS_SHADER)   (GLuint shader);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_GET_SHADERIV)   (GLuint shader, GLenum pname, GLint *params);
typedef GLboolean (APIENTRY *PFNVBOXVHWA_IS_PROGRAM)  (GLuint program);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_GET_PROGRAMIV)  (GLuint program, GLenum pname, GLint *params);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_GET_ATTACHED_SHADERS) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_GET_SHADER_INFO_LOG)  (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_GET_PROGRAM_INFO_LOG) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef GLint (APIENTRY *PFNVBOXVHWA_GET_UNIFORM_LOCATION) (GLint programObj, const GLchar *name);

typedef GLvoid (APIENTRY *PFNVBOXVHWA_UNIFORM1F)(GLint location, GLfloat v0);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_UNIFORM2F)(GLint location, GLfloat v0, GLfloat v1);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_UNIFORM3F)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_UNIFORM4F)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);

typedef GLvoid (APIENTRY *PFNVBOXVHWA_UNIFORM1I)(GLint location, GLint v0);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_UNIFORM2I)(GLint location, GLint v0, GLint v1);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_UNIFORM3I)(GLint location, GLint v0, GLint v1, GLint v2);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_UNIFORM4I)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3);

/* GL_ARB_pixel_buffer_object*/
#ifndef Q_WS_MAC
/* apears to be defined on mac */
typedef ptrdiff_t GLsizeiptr;
#endif

#ifndef GL_READ_ONLY
# define GL_READ_ONLY                   0x88B8
#endif
#ifndef GL_WRITE_ONLY
# define GL_WRITE_ONLY                  0x88B9
#endif
#ifndef GL_READ_WRITE
# define GL_READ_WRITE                  0x88BA
#endif
#ifndef GL_STREAM_DRAW
# define GL_STREAM_DRAW                 0x88E0
#endif
#ifndef GL_STREAM_READ
# define GL_STREAM_READ                 0x88E1
#endif
#ifndef GL_STREAM_COPY
# define GL_STREAM_COPY                 0x88E2
#endif

#ifndef GL_PIXEL_PACK_BUFFER
# define GL_PIXEL_PACK_BUFFER           0x88EB
#endif
#ifndef GL_PIXEL_UNPACK_BUFFER
# define GL_PIXEL_UNPACK_BUFFER         0x88EC
#endif
#ifndef GL_PIXEL_PACK_BUFFER_BINDING
# define GL_PIXEL_PACK_BUFFER_BINDING   0x88ED
#endif
#ifndef GL_PIXEL_UNPACK_BUFFER_BINDING
# define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF
#endif

typedef GLvoid (APIENTRY *PFNVBOXVHWA_GEN_BUFFERS)(GLsizei n, GLuint *buffers);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_DELETE_BUFFERS)(GLsizei n, const GLuint *buffers);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_BIND_BUFFER)(GLenum target, GLuint buffer);
typedef GLvoid (APIENTRY *PFNVBOXVHWA_BUFFER_DATA)(GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
typedef GLvoid* (APIENTRY *PFNVBOXVHWA_MAP_BUFFER)(GLenum target, GLenum access);
typedef GLboolean (APIENTRY *PFNVBOXVHWA_UNMAP_BUFFER)(GLenum target);

/*****************/

/* functions */

PFNVBOXVHWA_ACTIVE_TEXTURE vboxglActiveTexture = NULL;
PFNVBOXVHWA_MULTI_TEX_COORD2I vboxglMultiTexCoord2i = NULL;
PFNVBOXVHWA_MULTI_TEX_COORD2D vboxglMultiTexCoord2d = NULL;
PFNVBOXVHWA_MULTI_TEX_COORD2F vboxglMultiTexCoord2f = NULL;


PFNVBOXVHWA_CREATE_SHADER   vboxglCreateShader  = NULL;
PFNVBOXVHWA_SHADER_SOURCE   vboxglShaderSource  = NULL;
PFNVBOXVHWA_COMPILE_SHADER  vboxglCompileShader = NULL;
PFNVBOXVHWA_DELETE_SHADER   vboxglDeleteShader  = NULL;

PFNVBOXVHWA_CREATE_PROGRAM  vboxglCreateProgram = NULL;
PFNVBOXVHWA_ATTACH_SHADER   vboxglAttachShader  = NULL;
PFNVBOXVHWA_DETACH_SHADER   vboxglDetachShader  = NULL;
PFNVBOXVHWA_LINK_PROGRAM    vboxglLinkProgram   = NULL;
PFNVBOXVHWA_USE_PROGRAM     vboxglUseProgram    = NULL;
PFNVBOXVHWA_DELETE_PROGRAM  vboxglDeleteProgram = NULL;

PFNVBOXVHWA_IS_SHADER       vboxglIsShader      = NULL;
PFNVBOXVHWA_GET_SHADERIV    vboxglGetShaderiv   = NULL;
PFNVBOXVHWA_IS_PROGRAM      vboxglIsProgram     = NULL;
PFNVBOXVHWA_GET_PROGRAMIV   vboxglGetProgramiv  = NULL;
PFNVBOXVHWA_GET_ATTACHED_SHADERS vboxglGetAttachedShaders = NULL;
PFNVBOXVHWA_GET_SHADER_INFO_LOG  vboxglGetShaderInfoLog   = NULL;
PFNVBOXVHWA_GET_PROGRAM_INFO_LOG vboxglGetProgramInfoLog  = NULL;

PFNVBOXVHWA_GET_UNIFORM_LOCATION vboxglGetUniformLocation = NULL;

PFNVBOXVHWA_UNIFORM1F vboxglUniform1f;
PFNVBOXVHWA_UNIFORM2F vboxglUniform2f;
PFNVBOXVHWA_UNIFORM3F vboxglUniform3f;
PFNVBOXVHWA_UNIFORM4F vboxglUniform4f;

PFNVBOXVHWA_UNIFORM1I vboxglUniform1i;
PFNVBOXVHWA_UNIFORM2I vboxglUniform2i;
PFNVBOXVHWA_UNIFORM3I vboxglUniform3i;
PFNVBOXVHWA_UNIFORM4I vboxglUniform4i;

PFNVBOXVHWA_GEN_BUFFERS vboxglGenBuffers = NULL;
PFNVBOXVHWA_DELETE_BUFFERS vboxglDeleteBuffers = NULL;
PFNVBOXVHWA_BIND_BUFFER vboxglBindBuffer = NULL;
PFNVBOXVHWA_BUFFER_DATA vboxglBufferData = NULL;
PFNVBOXVHWA_MAP_BUFFER vboxglMapBuffer = NULL;
PFNVBOXVHWA_UNMAP_BUFFER vboxglUnmapBuffer = NULL;

#if 0
#if defined Q_WS_WIN
#define VBOXVHWA_GETPROCADDRESS(_t, _n) (_t)wglGetProcAddress(_n)
#elif defined Q_WS_X11
#include <GL/glx.h>
#define VBOXVHWA_GETPROCADDRESS(_t, _n) (_t)glXGetProcAddress((const GLubyte *)(_n))
#else
#error "Port me!!!"
#endif
#endif

#define VBOXVHWA_GETPROCADDRESS(_c, _t, _n) ((_t)(_c).getProcAddress(QString(_n)))

#define VBOXVHWA_PFNINIT_SAME(_c, _t, _v, _rc) \
    do { \
        if((vboxgl##_v = VBOXVHWA_GETPROCADDRESS(_c, _t, "gl"#_v)) == NULL) \
        { \
            VBOXQGLLOG(("ERROR: '%s' function is not found\n", "gl"#_v));\
            AssertBreakpoint(); \
            if((vboxgl##_v = VBOXVHWA_GETPROCADDRESS(_c, _t, "gl"#_v"ARB")) == NULL) \
            { \
                VBOXQGLLOG(("ERROR: '%s' function is not found\n", "gl"#_v"ARB"));\
                AssertBreakpoint(); \
                if((vboxgl##_v = VBOXVHWA_GETPROCADDRESS(_c, _t, "gl"#_v"EXT")) == NULL) \
                { \
                    VBOXQGLLOG(("ERROR: '%s' function is not found\n", "gl"#_v"EXT"));\
                    AssertBreakpoint(); \
                    (_rc)++; \
                } \
            } \
        } \
    }while(0)

#define VBOXVHWA_PFNINIT(_c, _t, _v, _f,_rc) \
    do { \
        if((vboxgl##_v = VBOXVHWA_GETPROCADDRESS(_c, _t, "gl"#_f)) == NULL) \
        { \
            VBOXQGLLOG(("ERROR: '%s' function is not found\n", "gl"#_f));\
            AssertBreakpoint(); \
            (_rc)++; \
        } \
    }while(0)

//#define VBOXVHWA_PFNINIT_OBJECT_ARB(_t, _v, _rc) VBOXVHWA_PFNINIT(_t, _v, #_v"ObjectARB" ,_rc)
#define VBOXVHWA_PFNINIT_OBJECT_ARB(_c, _t, _v, _rc) \
	    do { \
	        if((vboxgl##_v = VBOXVHWA_GETPROCADDRESS(_c, _t, "gl"#_v"ObjectARB")) == NULL) \
	        { \
	            VBOXQGLLOG(("ERROR: '%s' function is not found\n", "gl"#_v"ObjectARB"));\
	            AssertBreakpoint(); \
	            (_rc)++; \
	        } \
	    }while(0)

//#define VBOXVHWA_PFNINIT_ARB(_t, _v, _rc) VBOXVHWA_PFNINIT(_t, _v, #_v"ARB" ,_rc)
#define VBOXVHWA_PFNINIT_ARB(_c, _t, _v, _rc) \
	    do { \
	        if((vboxgl##_v = VBOXVHWA_GETPROCADDRESS(_c, _t, "gl"#_v"ARB")) == NULL) \
	        { \
                VBOXQGLLOG(("ERROR: '%s' function is not found\n", "gl"#_v"ARB"));\
	            AssertBreakpoint(); \
	            (_rc)++; \
	        } \
	    }while(0)


static bool g_vboxVHWAGlSupportInitialized = false;
/* vbox version in the format 0x00mjmnbl
 * in case of a failure contains -1 (0xffffffff) */
static int g_vboxVHWAGlVersion = 0;

static bool g_GL_ARB_multitexture        = false;
static bool g_GL_ARB_shader_objects      = false;
static bool g_GL_ARB_fragment_shader     = false;
static bool g_GL_ARB_pixel_buffer_object = false;
static bool g_GL_ARB_texture_rectangle   = false;
static bool g_GL_EXT_texture_rectangle   = false;
static bool g_GL_NV_texture_rectangle         = false;
static bool g_GL_ARB_texture_non_power_of_two = false;

/* gl features supported */
static bool g_vboxVHWAGlShaderSupported = false;
static bool g_vboxVHWAGlTextureRectangleSupported = false;
static bool g_vboxVHWAGlTextureNP2Supported = false;
static bool g_vboxVHWAGlPBOSupported = false;
static int g_vboxVHWAGlMultiTexNumSupported = 1; /* 1 would mean it is not supported */

/* vhwa features supported */
static uint32_t g_vboxVHWAFourccSupportedList[VBOXVHWA_NUMFOURCC];
static uint32_t g_vboxVHWAFourccSupportedCount = 0;


static int vboxVHWAGlParseSubver(const GLubyte * ver, const GLubyte ** pNext, bool bSpacePrefixAllowed)
{
    int val = 0;

    for(;;++ver)
    {
        if(*ver >= '0' && *ver < '9')
        {
            if(!val)
            {
                if(*ver == '0')
                    continue;
            }
            else
            {
                val *= 10;
            }
            val += *ver - '0';
        }
        else if(*ver == '.')
        {
            *pNext = ver+1;
            break;
        }
        else if(*ver == '\0')
        {
            *pNext = NULL;
            break;
        }
        else if(*ver == ' ' || *ver == '\t' ||  *ver == 0x0d || *ver == 0x0a)
        {
        	if(bSpacePrefixAllowed)
        	{
        		if(!val)
        		{
        			continue;
        		}
        	}

        	/* treat this as the end ov version string */
        	*pNext = NULL;
            break;
        }
        else
        {
            Assert(0);
            val = -1;
            break;
        }
    }

    return val;
}

static int vboxVHWAGlParseVersion(const GLubyte * ver)
{
    int iVer = vboxVHWAGlParseSubver(ver, &ver, true);
    if(iVer)
    {
        iVer <<= 16;
        if(ver)
        {
            int tmp = vboxVHWAGlParseSubver(ver, &ver, false);
            if(tmp >= 0)
            {
                iVer |= tmp << 8;
                if(ver)
                {
                    tmp = vboxVHWAGlParseSubver(ver, &ver, false);
                    if(tmp >= 0)
                    {
                        iVer |= tmp;
                    }
                    else
                    {
                        Assert(0);
                        iVer = -1;
                    }
                }
            }
            else
            {
                Assert(0);
                iVer = -1;
            }
        }
    }
    return iVer;
}

static void vboxVHWAGlInitExtSupport(const QGLContext & context)
{
    int rc = 0;
    do
    {
    	rc = 0;
    	g_vboxVHWAGlMultiTexNumSupported = 1; /* default, 1 means not supported */
		if(g_vboxVHWAGlVersion >= 0x010201) /* ogl >= 1.2.1 */
		{
			VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_ACTIVE_TEXTURE, ActiveTexture, rc);
			VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_MULTI_TEX_COORD2I, MultiTexCoord2i, rc);
			VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_MULTI_TEX_COORD2D, MultiTexCoord2d, rc);
			VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_MULTI_TEX_COORD2F, MultiTexCoord2f, rc);
		}
		else if(g_GL_ARB_multitexture)
		{
			VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_ACTIVE_TEXTURE, ActiveTexture, rc);
			VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_MULTI_TEX_COORD2I, MultiTexCoord2i, rc);
			VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_MULTI_TEX_COORD2D, MultiTexCoord2d, rc);
			VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_MULTI_TEX_COORD2F, MultiTexCoord2f, rc);
		}
		else
		{
			break;
		}

		if(RT_FAILURE(rc))
		    break;

        GLint maxCoords, maxUnits;
        glGetIntegerv(GL_MAX_TEXTURE_COORDS, &maxCoords);
        glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxUnits);

        VBOXQGLLOG(("Max Tex Coords (%d), Img Units (%d)\n", maxCoords, maxUnits));
        /* take the minimum of those */
        if(maxUnits < maxCoords)
        	maxCoords = maxUnits;
        if(maxUnits < 2)
        {
            VBOXQGLLOG(("Max Tex Coord or Img Units < 2 disabling MultiTex support\n"));
        	break;
        }

        g_vboxVHWAGlMultiTexNumSupported = maxUnits;
    }while(0);


    do
    {
    	rc = 0;
    	g_vboxVHWAGlPBOSupported = false;

		if(g_GL_ARB_pixel_buffer_object)
		{
			VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_GEN_BUFFERS, GenBuffers, rc);
			VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_DELETE_BUFFERS, DeleteBuffers, rc);
			VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_BIND_BUFFER, BindBuffer, rc);
			VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_BUFFER_DATA, BufferData, rc);
			VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_MAP_BUFFER, MapBuffer, rc);
			VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_UNMAP_BUFFER, UnmapBuffer, rc);
		}
		else
		{
		    break;
		}

		if(RT_FAILURE(rc))
		    break;

		g_vboxVHWAGlPBOSupported = true;
    } while(0);

    do
    {
        rc = 0;
        g_vboxVHWAGlShaderSupported = false;

        if(g_vboxVHWAGlVersion >= 0x020000)  /* if ogl >= 2.0*/
        {
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_CREATE_SHADER, CreateShader, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_SHADER_SOURCE, ShaderSource, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_COMPILE_SHADER, CompileShader, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_DELETE_SHADER, DeleteShader, rc);

            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_CREATE_PROGRAM, CreateProgram, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_ATTACH_SHADER, AttachShader, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_DETACH_SHADER, DetachShader, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_LINK_PROGRAM, LinkProgram, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_USE_PROGRAM, UseProgram, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_DELETE_PROGRAM, DeleteProgram, rc);

            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_IS_SHADER, IsShader, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_GET_SHADERIV, GetShaderiv, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_IS_PROGRAM, IsProgram, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_GET_PROGRAMIV, GetProgramiv, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_GET_ATTACHED_SHADERS, GetAttachedShaders,  rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_GET_SHADER_INFO_LOG, GetShaderInfoLog, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_GET_PROGRAM_INFO_LOG, GetProgramInfoLog, rc);

            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_GET_UNIFORM_LOCATION, GetUniformLocation, rc);

            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_UNIFORM1F, Uniform1f, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_UNIFORM2F, Uniform2f, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_UNIFORM3F, Uniform3f, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_UNIFORM4F, Uniform4f, rc);

            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_UNIFORM1I, Uniform1i, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_UNIFORM2I, Uniform2i, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_UNIFORM3I, Uniform3i, rc);
            VBOXVHWA_PFNINIT_SAME(context, PFNVBOXVHWA_UNIFORM4I, Uniform4i, rc);
        }
        else if(g_GL_ARB_shader_objects && g_GL_ARB_fragment_shader)
        {
            VBOXVHWA_PFNINIT_OBJECT_ARB(context, PFNVBOXVHWA_CREATE_SHADER, CreateShader, rc);
            VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_SHADER_SOURCE, ShaderSource, rc);
            VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_COMPILE_SHADER, CompileShader, rc);
            VBOXVHWA_PFNINIT(context, PFNVBOXVHWA_DELETE_SHADER, DeleteShader, DeleteObjectARB, rc);

            VBOXVHWA_PFNINIT_OBJECT_ARB(context, PFNVBOXVHWA_CREATE_PROGRAM, CreateProgram, rc);
            VBOXVHWA_PFNINIT(context, PFNVBOXVHWA_ATTACH_SHADER, AttachShader, AttachObjectARB, rc);
            VBOXVHWA_PFNINIT(context, PFNVBOXVHWA_DETACH_SHADER, DetachShader, DetachObjectARB, rc);
            VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_LINK_PROGRAM, LinkProgram, rc);
            VBOXVHWA_PFNINIT_OBJECT_ARB(context, PFNVBOXVHWA_USE_PROGRAM, UseProgram, rc);
            VBOXVHWA_PFNINIT(context, PFNVBOXVHWA_DELETE_PROGRAM, DeleteProgram, DeleteObjectARB, rc);

        //TODO:    VBOXVHWA_PFNINIT(PFNVBOXVHWA_IS_SHADER, IsShader, rc);
            VBOXVHWA_PFNINIT(context, PFNVBOXVHWA_GET_SHADERIV, GetShaderiv, GetObjectParameterivARB, rc);
        //TODO:    VBOXVHWA_PFNINIT(PFNVBOXVHWA_IS_PROGRAM, IsProgram, rc);
            VBOXVHWA_PFNINIT(context, PFNVBOXVHWA_GET_PROGRAMIV, GetProgramiv, GetObjectParameterivARB, rc);
            VBOXVHWA_PFNINIT(context, PFNVBOXVHWA_GET_ATTACHED_SHADERS, GetAttachedShaders, GetAttachedObjectsARB, rc);
            VBOXVHWA_PFNINIT(context, PFNVBOXVHWA_GET_SHADER_INFO_LOG, GetShaderInfoLog, GetInfoLogARB, rc);
            VBOXVHWA_PFNINIT(context, PFNVBOXVHWA_GET_PROGRAM_INFO_LOG, GetProgramInfoLog, GetInfoLogARB, rc);

            VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_GET_UNIFORM_LOCATION, GetUniformLocation, rc);

            VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_UNIFORM1F, Uniform1f, rc);
            VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_UNIFORM2F, Uniform2f, rc);
            VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_UNIFORM3F, Uniform3f, rc);
            VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_UNIFORM4F, Uniform4f, rc);

            VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_UNIFORM1I, Uniform1i, rc);
            VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_UNIFORM2I, Uniform2i, rc);
            VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_UNIFORM3I, Uniform3i, rc);
            VBOXVHWA_PFNINIT_ARB(context, PFNVBOXVHWA_UNIFORM4I, Uniform4i, rc);
        }
        else
        {
            break;
        }

        if(RT_FAILURE(rc))
            break;

        g_vboxVHWAGlShaderSupported = true;
    } while(0);

    if(g_GL_ARB_texture_rectangle || g_GL_EXT_texture_rectangle || g_GL_NV_texture_rectangle)
    {
        g_vboxVHWAGlTextureRectangleSupported = true;
    }
    else
    {
        g_vboxVHWAGlTextureRectangleSupported = false;
    }

    g_vboxVHWAGlTextureNP2Supported = g_GL_ARB_texture_non_power_of_two;
}

static void vboxVHWAGlInitFeatureSupport()
{
    if(g_vboxVHWAGlShaderSupported && g_vboxVHWAGlTextureRectangleSupported)
    {
        uint32_t num = 0;
        g_vboxVHWAFourccSupportedList[num++] = FOURCC_AYUV;
        g_vboxVHWAFourccSupportedList[num++] = FOURCC_UYVY;
        g_vboxVHWAFourccSupportedList[num++] = FOURCC_YUY2;
        if(g_vboxVHWAGlMultiTexNumSupported >= 4)
        {
            /* YV12 currently requires 3 units (for each color component)
             * + 1 unit for dst texture for color-keying + 3 units for each color component
             * TODO: we could store YV12 data in one texture to eliminate this requirement*/
            g_vboxVHWAFourccSupportedList[num++] = FOURCC_YV12;
        }

        Assert(num <= VBOXVHWA_NUMFOURCC);
        g_vboxVHWAFourccSupportedCount = num;
    }
    else
    {
        g_vboxVHWAFourccSupportedCount = 0;
    }
}

static void vboxVHWAGlInit(const QGLContext * pContext)
{
    if(g_vboxVHWAGlSupportInitialized)
        return;

    static QGLWidget *pTmpContextHolder = NULL;
    const bool bHasGlContext = (pContext != NULL);

    if(!pContext)
    {
        if(!pTmpContextHolder)
        {
            QGLWidget *pWidget = new QGLWidget();
            pWidget->makeCurrent();
            pContext = pWidget->context();
            pTmpContextHolder = pWidget;
        }

//        QGLContext * pTmpContext = new QGLContext(QGLFormat::defaultFormat());
//        bool bCreated = pTmpContext->create();
//        Assert(pTmpContext);
//        Assert(pTmpContext->isValid());
//
//        pTmpContext->makeCurrent();
//        pTmpContextHolder = pTmpContext;
    }

    const GLubyte * str;
    VBOXQGL_CHECKERR(
            str = glGetString(GL_VERSION);
            );

    VBOXQGLLOG (("gl version string: 0%s\n", str));

    g_vboxVHWAGlVersion = vboxVHWAGlParseVersion(str);
    Assert(g_vboxVHWAGlVersion > 0);
    if(g_vboxVHWAGlVersion < 0)
    {
        g_vboxVHWAGlVersion = 0;
    }
    else
    {
        VBOXQGLLOG (("gl version: 0x%x\n", g_vboxVHWAGlVersion));
        VBOXQGL_CHECKERR(
                str = glGetString(GL_EXTENSIONS);
                );

        const char * pos = strstr((const char *)str, "GL_ARB_multitexture");
        g_GL_ARB_multitexture = pos != NULL;
        VBOXQGLLOG (("GL_ARB_multitexture: %d\n", g_GL_ARB_multitexture));

        pos = strstr((const char *)str, "GL_ARB_shader_objects");
        g_GL_ARB_shader_objects = pos != NULL;
        VBOXQGLLOG (("GL_ARB_shader_objects: %d\n", g_GL_ARB_shader_objects));

        pos = strstr((const char *)str, "GL_ARB_fragment_shader");
        g_GL_ARB_fragment_shader = pos != NULL;
        VBOXQGLLOG (("GL_ARB_fragment_shader: %d\n", g_GL_ARB_fragment_shader));

        pos = strstr((const char *)str, "GL_ARB_pixel_buffer_object");
        g_GL_ARB_pixel_buffer_object = pos != NULL;
        VBOXQGLLOG (("GL_ARB_pixel_buffer_object: %d\n", g_GL_ARB_pixel_buffer_object));

        pos = strstr((const char *)str, "GL_ARB_texture_rectangle");
        g_GL_ARB_texture_rectangle = pos != NULL;
        VBOXQGLLOG (("GL_ARB_texture_rectangle: %d\n", g_GL_ARB_texture_rectangle));

        pos = strstr((const char *)str, "GL_EXT_texture_rectangle");
        g_GL_EXT_texture_rectangle = pos != NULL;
        VBOXQGLLOG (("GL_EXT_texture_rectangle: %d\n", g_GL_EXT_texture_rectangle));

        pos = strstr((const char *)str, "GL_NV_texture_rectangle");
        g_GL_NV_texture_rectangle = pos != NULL;
        VBOXQGLLOG (("GL_NV_texture_rectangle: %d\n", g_GL_NV_texture_rectangle));

        pos = strstr((const char *)str, "GL_ARB_texture_non_power_of_two");
        g_GL_ARB_texture_non_power_of_two = pos != NULL;
        VBOXQGLLOG (("GL_ARB_texture_non_power_of_two: %d\n", g_GL_ARB_texture_non_power_of_two));

        vboxVHWAGlInitExtSupport(*pContext);

        vboxVHWAGlInitFeatureSupport();
    }

    if(!bHasGlContext)
    {
        pTmpContextHolder->doneCurrent();
    }

    g_vboxVHWAGlSupportInitialized = true;
}

static bool vboxVHWASupportedInternal()
{
    if(g_vboxVHWAGlVersion <= 0)
    {
        /* error occurred while gl info initialization */
        return false;
    }

#ifndef DEBUG
    /* in case we do not support shaders & multitexturing we can not supprt dst colorkey,
     * no sense to report Video Acceleration supported */
    if(!g_vboxVHWAGlShaderSupported)
        return false;
#endif
    if(g_vboxVHWAGlMultiTexNumSupported < 2)
        return false;

    /* color conversion now supported only GL_TEXTURE_RECTANGLE
     * in this case only stretching is accelerated
     * report as unsupported, TODO: probably should report as supported for stretch acceleration */
    if(!g_vboxVHWAGlTextureRectangleSupported)
        return false;

    return true;
}

class VBoxVHWACommandProcessEvent : public QEvent
{
public:
    VBoxVHWACommandProcessEvent (VBoxVHWACommandElement *pEl)
        : QEvent ((QEvent::Type) VBoxDefs::VHWACommandProcessType)
    {
        mCmdPipe.put(pEl);
    }
    VBoxVHWACommandElementPipe & pipe() { return mCmdPipe; }
private:
    VBoxVHWACommandElementPipe mCmdPipe;
    VBoxVHWACommandProcessEvent *mpNext;

    friend class VBoxGLWidget;
};



VBoxVHWAHandleTable::VBoxVHWAHandleTable(uint32_t initialSize)
{
    mTable = new void*[initialSize];
    memset(mTable, 0, initialSize*sizeof(void*));
    mcSize = initialSize;
    mcUsage = 0;
    mCursor = 1; /* 0 is treated as invalid */
}

VBoxVHWAHandleTable::~VBoxVHWAHandleTable()
{
    delete[] mTable;
}

uint32_t VBoxVHWAHandleTable::put(void * data)
{
    Assert(data);
    if(!data)
        return VBOXVHWA_SURFHANDLE_INVALID;

    if(mcUsage == mcSize)
    {
        /* @todo: resize */
        Assert(0);
    }

    Assert(mcUsage < mcSize);
    if(mcUsage >= mcSize)
        return VBOXVHWA_SURFHANDLE_INVALID;

    for(int k = 0; k < 2; ++k)
    {
        Assert(mCursor != 0);
        for(uint32_t i = mCursor; i < mcSize; ++i)
        {
            if(!mTable[i])
            {
                doPut(i, data);
                mCursor = i+1;
                return i;
            }
        }
        mCursor = 1; /* 0 is treated as invalid */
    }

    Assert(0);
    return VBOXVHWA_SURFHANDLE_INVALID;
}

bool VBoxVHWAHandleTable::mapPut(uint32_t h, void * data)
{
    if(mcSize <= h)
        return false;
    if(h == 0)
        return false;
    if(mTable[h])
        return false;

    doPut(h, data);
    return true;
}

void* VBoxVHWAHandleTable::get(uint32_t h)
{
    Assert(h < mcSize);
    Assert(h > 0);
    return mTable[h];
}

void* VBoxVHWAHandleTable::remove(uint32_t h)
{
    Assert(mcUsage);
    Assert(h < mcSize);
    void* val = mTable[h];
    Assert(val);
    if(val)
    {
        doRemove(h);
    }
    return val;
}

void VBoxVHWAHandleTable::doPut(uint32_t h, void * data)
{
    ++mcUsage;
    mTable[h] = data;
}

void VBoxVHWAHandleTable::doRemove(uint32_t h)
{
    mTable[h] = 0;
    --mcUsage;
}

static VBoxVHWATexture* vboxVHWATextureCreate(const QRect & aRect, const VBoxVHWAColorFormat & aFormat, bool bVGA)
{
	if(!bVGA && g_GL_ARB_pixel_buffer_object)
	{
		VBOXQGLLOG(("VBoxVHWATextureNP2RectPBO\n"));
		return new VBoxVHWATextureNP2RectPBO(aRect, aFormat);
	}
	else if(g_vboxVHWAGlTextureRectangleSupported)
	{
		VBOXQGLLOG(("VBoxVHWATextureNP2Rect\n"));
		return new VBoxVHWATextureNP2Rect(aRect, aFormat);
	}
	else if(g_GL_ARB_texture_non_power_of_two)
	{
		VBOXQGLLOG(("VBoxVHWATextureNP2\n"));
		return new VBoxVHWATextureNP2(aRect, aFormat);
	}
	VBOXQGLLOG(("VBoxVHWATexture\n"));
	return new VBoxVHWATexture(aRect, aFormat);
}

class VBoxVHWAGlShader
{
public:
    VBoxVHWAGlShader(const char *aRcName, GLenum aType) :
        mShader(0),
        mRcName(aRcName),
        mType(aType)
    {}

//    virtual ~VBoxVHWAGlShader();

    int init();
//    virtual int initUniforms(class VBoxVHWAGlProgram * pProgram){}
    void uninit();
    bool isInitialized() { return mShader; }
    GLuint shader() {return mShader;}
private:
    GLuint mShader;
    const char *mRcName;
    GLenum mType;
};

int VBoxVHWAGlShader::init()
{
//    Assert(!isInitialized());
    if(isInitialized())
        return VINF_ALREADY_INITIALIZED;

    QFile fi(mRcName);
    if (!fi.open(QIODevice::ReadOnly))
    {
        Assert(0);
        return VERR_GENERAL_FAILURE;
    }

    QTextStream is(&fi);
    QString program = is.readAll();

    mShader = vboxglCreateShader(mType);
    Assert(mShader);
    if(!mShader)
        return VERR_GENERAL_FAILURE;

 //   int length = program.length();
    QByteArray asciiStr = program.toAscii();
    const char * contents = asciiStr.constData();
    GLint length = (GLint)strlen(contents) + 1;

    VBOXQGL_CHECKERR(
            vboxglShaderSource(mShader, 1, &contents, &length);
            );

    VBOXQGL_CHECKERR(
            vboxglCompileShader(mShader);
            );

    GLint compiled;
    VBOXQGL_CHECKERR(
            vboxglGetShaderiv(mShader, GL_COMPILE_STATUS, &compiled);
            );

#ifdef DEBUG
    GLchar * pBuf = new GLchar[16300];
    vboxglGetShaderInfoLog(mShader, 16300, NULL, pBuf);
    VBOXQGLLOG(("compile log for shader:\n-----------\n%s\n---------\n", contents));
    VBOXQGLLOG(("%s\n**********\n", pBuf));
    delete pBuf;
#endif

    Assert(compiled);
    if(compiled)
    {
        return VINF_SUCCESS;
    }



    VBOXQGL_CHECKERR(
            vboxglDeleteShader(mShader);
            );
    mShader = 0;
    return VERR_GENERAL_FAILURE;
}

void VBoxVHWAGlShader::uninit()
{
    if(!isInitialized())
        return;

    VBOXQGL_CHECKERR(
            vboxglDeleteShader(mShader);
            );
    mShader = 0;
}

class VBoxVHWAGlProgram
{
public:
    VBoxVHWAGlProgram(VBoxVHWAGlShader ** apShaders, int acShaders);

    ~VBoxVHWAGlProgram();

    virtual int init();
    virtual void uninit();
    virtual int start();
    virtual int stop();
    bool isInitialized() { return mProgram; }
    GLuint program() {return mProgram;}
private:
    GLuint mProgram;
    VBoxVHWAGlShader ** mpShaders;
    int mcShaders;
};

VBoxVHWAGlProgram::VBoxVHWAGlProgram(VBoxVHWAGlShader ** apShaders, int acShaders) :
       mProgram(0),
       mpShaders(NULL),
       mcShaders(0)
{
    Assert(acShaders);
    if(acShaders)
    {
        mpShaders = (VBoxVHWAGlShader **)malloc(sizeof(VBoxVHWAGlShader *) * acShaders);
        memcpy(mpShaders, apShaders, sizeof(VBoxVHWAGlShader *) * acShaders);
        mcShaders = acShaders;
    }
}

VBoxVHWAGlProgram::~VBoxVHWAGlProgram()
{
    uninit();

    if(mpShaders)
    {
        free(mpShaders);
    }
}

int VBoxVHWAGlProgram::init()
{
    Assert(!isInitialized());
    if(isInitialized())
        return VINF_ALREADY_INITIALIZED;

    Assert(mcShaders);
    if(!mcShaders)
        return VERR_GENERAL_FAILURE;

    int rc = VINF_SUCCESS;
    for(int i = 0; i < mcShaders; i++)
    {
        int rc = mpShaders[i]->init();
        Assert(RT_SUCCESS(rc));
        if(RT_FAILURE(rc))
        {
            break;
        }
    }
    if(RT_FAILURE(rc))
    {
        return rc;
    }

    mProgram = vboxglCreateProgram();
    Assert(mProgram);
    if(mProgram)
    {
        for(int i = 0; i < mcShaders; i++)
        {
            VBOXQGL_CHECKERR(
                    vboxglAttachShader(mProgram, mpShaders[i]->shader());
                    );
        }

        VBOXQGL_CHECKERR(
                vboxglLinkProgram(mProgram);
                );


        GLint linked;
        vboxglGetProgramiv(mProgram, GL_LINK_STATUS, &linked);

#ifdef DEBUG
        GLchar * pBuf = new GLchar[16300];
        vboxglGetProgramInfoLog(mProgram, 16300, NULL, pBuf);
        VBOXQGLLOG(("link log: %s\n", pBuf));
        Assert(linked);
        delete pBuf;
#endif

        if(linked)
        {
            return VINF_SUCCESS;
        }

        VBOXQGL_CHECKERR(
                vboxglDeleteProgram(mProgram);
                );
        mProgram = 0;
    }
    return VERR_GENERAL_FAILURE;
}

void VBoxVHWAGlProgram::uninit()
{
    if(!isInitialized())
        return;

    VBOXQGL_CHECKERR(
            vboxglDeleteProgram(mProgram);
            );
    mProgram = 0;
}

int VBoxVHWAGlProgram::start()
{
    VBOXQGL_CHECKERR(
            vboxglUseProgram(mProgram);
            );
    return VINF_SUCCESS;
}

int VBoxVHWAGlProgram::stop()
{
    VBOXQGL_CHECKERR(
            vboxglUseProgram(0);
            );
    return VINF_SUCCESS;
}

#define VBOXVHWA_PROGRAM_DSTCOLORKEY  0x00000001
#define VBOXVHWA_PROGRAM_SRCCOLORKEY  0x00000002
#define VBOXVHWA_PROGRAM_COLORCONV    0x00000004

class VBoxVHWAGlProgramVHWA : public VBoxVHWAGlProgram
{
public:
    VBoxVHWAGlProgramVHWA(/*class VBoxVHWAGlProgramMngr *aMngr, */uint32_t type, uint32_t fourcc, VBoxVHWAGlShader ** apShaders, int acShaders);

    uint32_t type() const {return mType;}
    uint32_t fourcc() const {return mFourcc;}

    int setDstCKeyUpperRange(GLfloat r, GLfloat g, GLfloat b);

    int setDstCKeyLowerRange(GLfloat r, GLfloat g, GLfloat b);

    int setSrcCKeyUpperRange(GLfloat r, GLfloat g, GLfloat b);

    int setSrcCKeyLowerRange(GLfloat r, GLfloat g, GLfloat b);


    virtual int init();

    bool matches(uint32_t type, uint32_t fourcc)
    {
        return mType == type && mFourcc == fourcc;
    }

    bool equals(const VBoxVHWAGlProgramVHWA & other)
    {
        return matches(other.mType, other.mFourcc);
    }

private:
    uint32_t mType;
    uint32_t mFourcc;

    GLfloat mDstUpperR, mDstUpperG, mDstUpperB;
    GLint mUniDstUpperColor;

    GLfloat mDstLowerR, mDstLowerG, mDstLowerB;
    GLint mUniDstLowerColor;

    GLfloat mSrcUpperR, mSrcUpperG, mSrcUpperB;
    GLint mUniSrcUpperColor;

    GLfloat mSrcLowerR, mSrcLowerG, mSrcLowerB;
    GLint mUniSrcLowerColor;

    GLint mDstTex;
    GLint mUniDstTex;

    GLint mSrcTex;
    GLint mUniSrcTex;

    GLint mVTex;
    GLint mUniVTex;

    GLint mUTex;
    GLint mUniUTex;

//    VBoxVHWAGlProgram *mpProgram;
//
//    class VBoxVHWAGlProgramMngr *mpMngr;
};

VBoxVHWAGlProgramVHWA::VBoxVHWAGlProgramVHWA(/*VBoxVHWAGlProgramMngr *aMngr, */uint32_t type, uint32_t fourcc, VBoxVHWAGlShader ** apShaders, int acShaders) :
    VBoxVHWAGlProgram(apShaders, acShaders),
    mType(type),
    mFourcc(fourcc),
    mDstUpperR(0.0), mDstUpperG(0.0), mDstUpperB(0.0),
    mUniDstUpperColor(-1),
    mDstLowerR(0.0), mDstLowerG(0.0), mDstLowerB(0.0),
    mUniDstLowerColor(-1),
    mSrcUpperR(0.0), mSrcUpperG(0.0), mSrcUpperB(0.0),
    mUniSrcUpperColor(-1),
    mSrcLowerR(0.0), mSrcLowerG(0.0), mSrcLowerB(0.0),
    mUniSrcLowerColor(-1),
//    mpMngr(aMngr),
    mDstTex(-1),
    mUniDstTex(-1),
    mSrcTex(-1),
    mUniSrcTex(-1),
    mVTex(-1),
    mUniVTex(-1),
    mUTex(-1),
    mUniUTex(-1)
{}

int VBoxVHWAGlProgramVHWA::init()
{
    int rc = VBoxVHWAGlProgram::init();
    if(RT_FAILURE(rc))
        return rc;
    if(rc == VINF_ALREADY_INITIALIZED)
        return rc;

    start();

    rc = VERR_GENERAL_FAILURE;

    do
    {
        mUniSrcTex = vboxglGetUniformLocation(program(), "uSrcTex");
        Assert(mUniSrcTex != -1);
        if(mUniSrcTex == -1)
            break;
        if(type() & VBOXVHWA_PROGRAM_DSTCOLORKEY)
        {
            VBOXQGL_CHECKERR(
                    vboxglUniform1i(mUniSrcTex, 1);
                    );
            mSrcTex = 1;

            mUniDstTex = vboxglGetUniformLocation(program(), "uDstTex");
            Assert(mUniDstTex != -1);
            if(mUniDstTex == -1)
                break;
            VBOXQGL_CHECKERR(
                    vboxglUniform1i(mUniDstTex, 0);
                    );
            mDstTex = 0;
            mUniDstLowerColor = vboxglGetUniformLocation(program(), "uDstClr");
            Assert(mUniDstLowerColor != -1);
            if(mUniDstLowerColor == -1)
                break;

            mDstLowerR = 0.0; mDstLowerG = 0.0; mDstLowerB = 0.0;

            VBOXQGL_CHECKERR(
                    vboxglUniform4f(mUniDstLowerColor, 0.0, 0.0, 0.0, 0.0);
                    );
        }
        else
        {
            VBOXQGL_CHECKERR(
                    vboxglUniform1i(mUniSrcTex, 1);
                    );
            mSrcTex = 0;
        }

        if(type() & VBOXVHWA_PROGRAM_SRCCOLORKEY)
        {
            mUniSrcLowerColor = vboxglGetUniformLocation(program(), "uSrcClr");
            Assert(mUniSrcLowerColor != -1);
            if(mUniSrcLowerColor == -1)
                break;

            mSrcLowerR = 0.0; mSrcLowerG = 0.0; mSrcLowerB = 0.0;
            VBOXQGL_CHECKERR(
                    vboxglUniform4f(mUniSrcLowerColor, 0.0, 0.0, 0.0, 0.0);
                    );
        }

        if(type() & VBOXVHWA_PROGRAM_COLORCONV)
        {
            switch(fourcc())
            {
                case FOURCC_YV12:
                {
                    GLint tex = mSrcTex + 1;

                    mUniVTex = vboxglGetUniformLocation(program(), "uVTex");

                    Assert(mUniVTex != -1);
                    if(mUniVTex == -1)
                        break;

                    VBOXQGL_CHECKERR(
                            vboxglUniform1i(mUniVTex, tex);
                            );
                    mVTex = tex;

                    tex++;

                    mUniUTex = vboxglGetUniformLocation(program(), "uUTex");
                    Assert(mUniUTex != -1);
                    if(mUniUTex == -1)
                        break;

                    VBOXQGL_CHECKERR(
                            vboxglUniform1i(mUniUTex, tex);
                            );
                    mUTex = tex;

                    break;
                }
                case FOURCC_UYVY:
                case FOURCC_YUY2:
                case FOURCC_AYUV:
                    break;
                default:
                    Assert(0);
                    break;
            }
        }

        rc = VINF_SUCCESS;
    } while(0);


    stop();
    if(rc == VINF_SUCCESS)
        return VINF_SUCCESS;

    Assert(0);
    VBoxVHWAGlProgram::uninit();
    return VERR_GENERAL_FAILURE;
}

int VBoxVHWAGlProgramVHWA::setDstCKeyUpperRange(GLfloat r, GLfloat g, GLfloat b)
{
    Assert(isInitialized());
    if(!isInitialized())
        return VERR_GENERAL_FAILURE;
    if(mDstUpperR == r && mDstUpperG == g && mDstUpperB == b)
        return VINF_ALREADY_INITIALIZED;
    vboxglUniform4f(mUniDstUpperColor, r, g, b, 0.0);
    mDstUpperR = r;
    mDstUpperG = g;
    mDstUpperB = b;
    return VINF_SUCCESS;
}

int VBoxVHWAGlProgramVHWA::setDstCKeyLowerRange(GLfloat r, GLfloat g, GLfloat b)
{
    Assert(isInitialized());
    if(!isInitialized())
        return VERR_GENERAL_FAILURE;
    if(mDstLowerR == r && mDstLowerG == g && mDstLowerB == b)
        return VINF_ALREADY_INITIALIZED;

//    VBOXQGLLOG(("setDstCKeyLowerRange: r(%f), g(%f), b(%f)\n", r, g, b));
    VBOXQGL_CHECKERR(
            vboxglUniform4f(mUniDstLowerColor, r, g, b, 0.0);
            );

    mDstLowerR = r;
    mDstLowerG = g;
    mDstLowerB = b;
    return VINF_SUCCESS;
}

int VBoxVHWAGlProgramVHWA::setSrcCKeyUpperRange(GLfloat r, GLfloat g, GLfloat b)
{
    Assert(isInitialized());
    if(!isInitialized())
        return VERR_GENERAL_FAILURE;
    if(mSrcUpperR == r && mSrcUpperG == g && mSrcUpperB == b)
        return VINF_ALREADY_INITIALIZED;
    vboxglUniform4f(mUniSrcUpperColor, r, g, b, 0.0);
    mSrcUpperR = r;
    mSrcUpperG = g;
    mSrcUpperB = b;
    return VINF_SUCCESS;
}

int VBoxVHWAGlProgramVHWA::setSrcCKeyLowerRange(GLfloat r, GLfloat g, GLfloat b)
{
    Assert(isInitialized());
    if(!isInitialized())
        return VERR_GENERAL_FAILURE;
    if(mSrcLowerR == r && mSrcLowerG == g && mSrcLowerB == b)
        return VINF_ALREADY_INITIALIZED;
    VBOXQGL_CHECKERR(
            vboxglUniform4f(mUniSrcLowerColor, r, g, b, 0.0);
            );
    mSrcLowerR = r;
    mSrcLowerG = g;
    mSrcLowerB = b;
    return VINF_SUCCESS;
}

class VBoxVHWAGlProgramMngr
{
public:
    VBoxVHWAGlProgramMngr() :
        mShaderCConvApplyAYUV(":/cconvApplyAYUV.c", GL_FRAGMENT_SHADER),
        mShaderCConvAYUV(":/cconvAYUV.c", GL_FRAGMENT_SHADER),
        mShaderCConvAYUVVoid(":/cconvAYUV_void.c", GL_FRAGMENT_SHADER),
        mShaderCConvBGR(":/cconvBGR.c", GL_FRAGMENT_SHADER),
        mShaderCConvBGRVoid(":/cconvBGR_void.c", GL_FRAGMENT_SHADER),
        mShaderCConvUYVY(":/cconvUYVY.c", GL_FRAGMENT_SHADER),
        mShaderCConvUYVYVoid(":/cconvUYVY_void.c", GL_FRAGMENT_SHADER),
        mShaderCConvYUY2(":/cconvYUY2.c", GL_FRAGMENT_SHADER),
        mShaderCConvYUY2Void(":/cconvYUY2_void.c", GL_FRAGMENT_SHADER),
        mShaderCConvYV12(":/cconvYV12.c", GL_FRAGMENT_SHADER),
        mShaderCConvYV12Void(":/cconvYV12_void.c", GL_FRAGMENT_SHADER),
        mShaderSplitBGRA(":/splitBGRA.c", GL_FRAGMENT_SHADER),
        mShaderCKeyDst(":/ckeyDst.c", GL_FRAGMENT_SHADER),
        mShaderCKeyDstVoid(":/ckeyDst_void.c", GL_FRAGMENT_SHADER),
    //  mShaderCKeySrc;
    //  mShaderCKeySrcVoid;
        mShaderMainOverlay(":/mainOverlay.c", GL_FRAGMENT_SHADER)
    {}

    VBoxVHWAGlProgramVHWA * getProgram(bool bDstCKey, bool bSrcCKey, const VBoxVHWAColorFormat * pFrom, const VBoxVHWAColorFormat * pTo);

    void stopCurrentProgram()
    {
        VBOXQGL_CHECKERR(
            vboxglUseProgram(0);
            );
    }
private:
    VBoxVHWAGlProgramVHWA * searchProgram(uint32_t type, uint32_t fourcc, bool bCreate);

    VBoxVHWAGlProgramVHWA * createProgram(uint32_t type, uint32_t fourcc);

//    int startProgram(VBoxVHWAGlProgramVHWA * pProgram) {mCurrentProgram = pProgram; return pProgram->start();}

    typedef std::list <VBoxVHWAGlProgramVHWA*> ProgramList;

//    VBoxVHWAGlProgramVHWA * mCurrentProgram;
    ProgramList mPrograms;

    VBoxVHWAGlShader mShaderCConvApplyAYUV;

    VBoxVHWAGlShader mShaderCConvAYUV;
    VBoxVHWAGlShader mShaderCConvAYUVVoid;
    VBoxVHWAGlShader mShaderCConvBGR;
    VBoxVHWAGlShader mShaderCConvBGRVoid;
    VBoxVHWAGlShader mShaderCConvUYVY;
    VBoxVHWAGlShader mShaderCConvUYVYVoid;
    VBoxVHWAGlShader mShaderCConvYUY2;
    VBoxVHWAGlShader mShaderCConvYUY2Void;
    VBoxVHWAGlShader mShaderCConvYV12;
    VBoxVHWAGlShader mShaderCConvYV12Void;
    VBoxVHWAGlShader mShaderSplitBGRA;

    VBoxVHWAGlShader mShaderCKeyDst;
    VBoxVHWAGlShader mShaderCKeyDstVoid;
//    VBoxVHWAGlShader mShaderCKeySrc;
//    VBoxVHWAGlShader mShaderCKeySrcVoid;

    VBoxVHWAGlShader mShaderMainOverlay;

    friend class VBoxVHWAGlProgramVHWA;
};

VBoxVHWAGlProgramVHWA * VBoxVHWAGlProgramMngr::createProgram(uint32_t type, uint32_t fourcc)
{
    VBoxVHWAGlShader * apShaders[16];
    uint32_t cShaders = 0;

    /* workaround for NVIDIA driver bug: ensure we attach the shader before those it is used in */
    /* reserve a slot for the mShaderCConvApplyAYUV,
     * in case it is not used the slot will be occupied by mShaderCConvBGR , which is ok */
    cShaders++;

    if(type &  VBOXVHWA_PROGRAM_DSTCOLORKEY)
    {
        apShaders[cShaders++] = &mShaderCKeyDst;
    }
    else
    {
        apShaders[cShaders++] = &mShaderCKeyDstVoid;
    }

    if(type & VBOXVHWA_PROGRAM_SRCCOLORKEY)
    {
        Assert(0);
        /* disabled for now, not really necessary for video overlaying */
    }

    bool bFound = false;

//    if(type & VBOXVHWA_PROGRAM_COLORCONV)
    {
        if(fourcc == FOURCC_UYVY)
        {
            apShaders[cShaders++] = &mShaderCConvUYVY;
            bFound = true;
        }
        else
        {
            apShaders[cShaders++] = &mShaderCConvUYVYVoid;
        }

        if(fourcc == FOURCC_YUY2)
        {
            apShaders[cShaders++] = &mShaderCConvYUY2;
            bFound = true;
        }
        else
        {
            apShaders[cShaders++] = &mShaderCConvYUY2Void;
        }

        if(fourcc == FOURCC_YV12)
        {
            apShaders[cShaders++] = &mShaderSplitBGRA;
            apShaders[cShaders++] = &mShaderCConvYV12;
            bFound = true;
        }
        else
        {
            apShaders[cShaders++] = &mShaderCConvYV12Void;
        }

        if(fourcc == FOURCC_AYUV)
        {
            apShaders[cShaders++] = &mShaderCConvAYUV;
            bFound = true;
        }
        else
        {
            apShaders[cShaders++] = &mShaderCConvAYUVVoid;
        }
    }

    if(bFound)
    {
        type |= VBOXVHWA_PROGRAM_COLORCONV;
        apShaders[0] = &mShaderCConvApplyAYUV;
        apShaders[cShaders++] = &mShaderCConvBGRVoid;
    }
    else
    {
        type &= (~VBOXVHWA_PROGRAM_COLORCONV);
        apShaders[0] = &mShaderCConvBGR;
    }

    apShaders[cShaders++] = &mShaderMainOverlay;

    Assert(cShaders <= RT_ELEMENTS(apShaders));

    VBoxVHWAGlProgramVHWA *pProgram =  new VBoxVHWAGlProgramVHWA(/*this, */type, fourcc, apShaders, cShaders);
    pProgram->init();

    return pProgram;
}

VBoxVHWAGlProgramVHWA * VBoxVHWAGlProgramMngr::getProgram(bool bDstCKey, bool bSrcCKey, const VBoxVHWAColorFormat * pFrom, const VBoxVHWAColorFormat * pTo)
{
	Q_UNUSED(pTo);
    uint32_t type = 0;
    uint32_t fourcc = 0;
    if(bDstCKey)
    {
        type |= VBOXVHWA_PROGRAM_DSTCOLORKEY;
    }
    if(bSrcCKey)
    {
        type |= VBOXVHWA_PROGRAM_SRCCOLORKEY;
    }
    if(pFrom && pFrom->fourcc())
    {
        fourcc = pFrom->fourcc();
        type |= VBOXVHWA_PROGRAM_COLORCONV;
    }
    if(type)
        return searchProgram(type, fourcc, true);
    return NULL;
}

VBoxVHWAGlProgramVHWA * VBoxVHWAGlProgramMngr::searchProgram(uint32_t type, uint32_t fourcc, bool bCreate)
{
//    if(mCurrentProgram && mCurrentProgram->matches(type))
//        return mCurrentProgram;

    for (ProgramList::const_iterator it = mPrograms.begin();
         it != mPrograms.end(); ++ it)
    {
        if (!(*it)->matches(type, fourcc))
        {
            continue;
        }
        return *it;
    }
    if(bCreate)
    {
        VBoxVHWAGlProgramVHWA *pProgram = createProgram(type, fourcc);
        if(pProgram)
        {
            mPrograms.push_back(pProgram);
            return pProgram;
        }
    }
    return NULL;
}

int VBoxVHWASurfaceBase::setCKey(VBoxVHWAGlProgramVHWA * pProgram, const VBoxVHWAColorFormat * pFormat, const VBoxVHWAColorKey * pCKey, bool bDst)
{
    float r,g,b;
//    pProgram->start();
//    pFormat->pixel2Normalized(pCKey->upper(), &r, &g, &b);
//    int rcU = pProgram->setCKeyUpperRange(r, g, b);
//    Assert(RT_SUCCESS(rcU));
    pFormat->pixel2Normalized(pCKey->lower(), &r, &g, &b);
    int rcL = bDst ? pProgram->setDstCKeyLowerRange(r, g, b) : pProgram->setSrcCKeyLowerRange(r, g, b);
    Assert(RT_SUCCESS(rcL));
//    pProgram->stop();

    return RT_SUCCESS(rcL) /*&& RT_SUCCESS(rcU)*/ ? VINF_SUCCESS: VERR_GENERAL_FAILURE;
}



void VBoxVHWASurfaceBase::setAddress(uchar * addr)
{
    Assert(addr);
    if(!addr) return;
    if(addr == mAddress) return;

    if(mFreeAddress)
    {
        free(mAddress);
    }

    mAddress = addr;
    mFreeAddress = false;

    mpTex[0]->setAddress(mAddress);
    if(fourcc() == FOURCC_YV12)
    {
        uchar *pTexAddr = mAddress+mpTex[0]->memSize();
        mpTex[1]->setAddress(pTexAddr);
        pTexAddr = pTexAddr+mpTex[1]->memSize();
        mpTex[2]->setAddress(pTexAddr);
    }

//    makeCurrent();
//    updateTexture(&mRect);
    mUpdateMem2TexRect.set(mRect);
    Assert(!mUpdateMem2TexRect.isClear());
    Assert(mRect.contains(mUpdateMem2TexRect.rect()));
//    mUpdateTex2FBRect.clear();
//    Assert(mUpdateTex2FBRect.isClear());
}

void VBoxVHWASurfaceBase::globalInit()
{
    VBOXQGLLOG(("globalInit\n"));

//    glEnable(GL_TEXTURE_2D);
    glEnable(GL_TEXTURE_RECTANGLE);

    VBOXQGL_CHECKERR(
            glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
            );
    VBOXQGL_CHECKERR(
            glPixelStorei(GL_PACK_ALIGNMENT, 1);
            );
//
//    VBOXQGL_CHECKERR(
//            vboxglActiveTexture(GL_TEXTURE1);
//        );
//    VBOXQGL_CHECKERR(
//            glEnable(GL_TEXTURE_2D);
//            );
//    VBOXQGL_CHECKERR(
//            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//            );
//    VBOXQGL_CHECKERR(
//            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//            );
//    VBOXQGL_CHECKERR(
//            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
//            );
//    VBOXQGL_CHECKERR(
//            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
//            );
//
//    VBOXQGL_CHECKERR(
//            vboxglActiveTexture(GL_TEXTURE0);
//        );
//    VBOXQGL_CHECKERR(
//            glEnable(GL_TEXTURE_2D);
//            );
//    VBOXQGL_CHECKERR(
//            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//            );
//    VBOXQGL_CHECKERR(
//            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//            );
//    VBOXQGL_CHECKERR(
//            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
//            );
//    VBOXQGL_CHECKERR(
//            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
//            );
}

VBoxVHWASurfaceBase::VBoxVHWASurfaceBase(class VBoxGLWidget *aWidget, const QSize * aSize, const QSize * aTargSize,
        VBoxVHWAColorFormat & aColorFormat,
        VBoxVHWAColorKey * pSrcBltCKey, VBoxVHWAColorKey * pDstBltCKey,
                    VBoxVHWAColorKey * pSrcOverlayCKey, VBoxVHWAColorKey * pDstOverlayCKey,
                    bool bVGA) :
                mRect(0,0,aSize->width(),aSize->height()),
                mVisibleDisplayInitialized(false),
                mAddress(NULL),
                mColorFormat(aColorFormat),
                mpSrcBltCKey(NULL),
                mpDstBltCKey(NULL),
                mpSrcOverlayCKey(NULL),
                mpDstOverlayCKey(NULL),
                mpDefaultDstOverlayCKey(NULL),
                mpDefaultSrcOverlayCKey(NULL),
                mLockCount(0),
                mFreeAddress(false),
                mComplexList(NULL),
                mWidget(aWidget),
                mHGHandle(VBOXVHWA_SURFHANDLE_INVALID)
#ifdef DEBUG
                ,
                cFlipsCurr(0),
                cFlipsTarg(0)
#endif
{
    setDstBltCKey(pDstBltCKey);
    setSrcBltCKey(pSrcBltCKey);

    setDefaultDstOverlayCKey(pDstOverlayCKey);
    resetDefaultDstOverlayCKey();

    setDefaultSrcOverlayCKey(pSrcOverlayCKey);
    resetDefaultSrcOverlayCKey();

    mpTex[0] = vboxVHWATextureCreate(QRect(0,0,aSize->width(),aSize->height()), mColorFormat, bVGA);
    if(mColorFormat.fourcc() == FOURCC_YV12)
    {
        QRect rect(0,0,aSize->width()/2,aSize->height()/2);
        mpTex[1] = vboxVHWATextureCreate(rect, mColorFormat, bVGA);
        mpTex[2] = vboxVHWATextureCreate(rect, mColorFormat, bVGA);
    }

    mSrcRect = mRect;
    mTargRect = mRect; /* == Vis FB size */
    mTargSize = QRect(0, 0, aTargSize->width(), aTargSize->height());

//    mBytesPerPixel = calcBytesPerPixel(mColorFormat.format(), mColorFormat.type());
//    mBytesPerLine = mRect.width() * mBytesPerPixel;
}

VBoxVHWASurfaceBase::~VBoxVHWASurfaceBase()
{
    uninit();
}

GLsizei VBoxVHWASurfaceBase::makePowerOf2(GLsizei val)
{
    int last = ASMBitLastSetS32(val);
    if(last>1)
    {
        last--;
        if((1 << last) != val)
        {
            Assert((1 << last) < val);
            val = (1 << (last+1));
        }
    }
    return val;
}

ulong VBoxVHWASurfaceBase::calcBytesPerPixel(GLenum format, GLenum type)
{
    /* we now support only common byte-aligned data */
    int numComponents = 0;
    switch(format)
    {
    case GL_COLOR_INDEX:
    case GL_RED:
    case GL_GREEN:
    case GL_BLUE:
    case GL_ALPHA:
    case GL_LUMINANCE:
        numComponents = 1;
        break;
    case GL_RGB:
    case GL_BGR_EXT:
        numComponents = 3;
        break;
    case GL_RGBA:
    case GL_BGRA_EXT:
        numComponents = 4;
        break;
    case GL_LUMINANCE_ALPHA:
        numComponents = 2;
        break;
    default:
        Assert(0);
        break;
    }

    int componentSize = 0;
    switch(type)
    {
    case GL_UNSIGNED_BYTE:
    case GL_BYTE:
        componentSize = 1;
        break;
    //case GL_BITMAP:
    case  GL_UNSIGNED_SHORT:
    case GL_SHORT:
        componentSize = 2;
        break;
    case GL_UNSIGNED_INT:
    case GL_INT:
    case GL_FLOAT:
        componentSize = 4;
        break;
    default:
        Assert(0);
        break;
    }
    return numComponents * componentSize;
}

void VBoxVHWASurfaceBase::uninit()
{
//    mState->makeCurrent(this);

    deleteDisplay();

    delete mpTex[0];
    if(fourcc() == FOURCC_YV12)
    {
        delete mpTex[1];
        delete mpTex[2];
    }

    if(mAddress && mFreeAddress)
    {
        free(mAddress);
        mAddress = NULL;
    }
}

ulong VBoxVHWASurfaceBase::memSize()
{
    ulong size = mpTex[0]->memSize();
    if(fourcc() == FOURCC_YV12)
    {
        size += mpTex[1]->memSize() + mpTex[2]->memSize();
    }
    return size;
}

void VBoxVHWASurfaceBase::init(VBoxVHWASurfaceBase * pPrimary, uchar *pvMem)
{
    if(pPrimary)
    {
        VBOXQGL_CHECKERR(
                vboxglActiveTexture(GL_TEXTURE1);
            );
    }

    int size = memSize();
    uchar * address = (uchar *)malloc(size);
#ifdef DEBUG_misha
    int tex0Size = mpTex[0]->memSize();
    if(pPrimary)
    {
        memset(address, 0xff, tex0Size);
        Assert(size >= tex0Size);
        if(size > tex0Size)
        {
        	memset(address + tex0Size, 0x0, size - tex0Size);
        }
    }
    else
    {
        memset(address, 0x0f, tex0Size);
        Assert(size >= tex0Size);
        if(size > tex0Size)
        {
        	memset(address + tex0Size, 0x3f, size - tex0Size);
        }
    }
#else
    memset(address, 0, size);
#endif

    mpTex[0]->init(address);
    if(fourcc() == FOURCC_YV12)
    {
        mpTex[1]->init(address);
        mpTex[2]->init(address);
    }


    if(pvMem)
    {
        mAddress = pvMem;
        free(address);
        mFreeAddress = false;

    }
    else
    {
        mAddress = address;
        mFreeAddress = true;
    }

    mpTex[0]->setAddress(mAddress);
    if(fourcc() == FOURCC_YV12)
    {
        uchar *pTexAddr = mAddress+mpTex[0]->memSize();
        mpTex[1]->setAddress(pTexAddr);
        pTexAddr = pTexAddr+mpTex[1]->memSize();
        mpTex[2]->setAddress(pTexAddr);
    }

    initDisplay(pPrimary);

    mUpdateMem2TexRect.set(mRect);
    Assert(!mUpdateMem2TexRect.isClear());
    Assert(mRect.contains(mUpdateMem2TexRect.rect()));

    if(pPrimary)
    {
        VBOXQGLLOG(("restoring to tex 0"));
        VBOXQGL_CHECKERR(
                vboxglActiveTexture(GL_TEXTURE0);
            );
    }

}

#ifdef DEBUG_misha
bool g_DbgTest = false;
#endif

void VBoxVHWATexture::doUpdate(uchar * pAddress, const QRect * pRect)
{
#ifdef DEBUG_misha
	if(g_DbgTest)
	{
		pAddress = (uchar*)malloc(memSize());
		uchar val = 0;
		for(uint32_t i = 0; i < memSize(); i++)
		{
			pAddress[i] = val;
			val+=64;
		}
	}
#endif

    GLenum tt = texTarget();
    if(pRect)
    {
        Assert(mRect.contains(*pRect));
    }
    else
    {
        pRect = &mRect;
    }

    Assert(glIsTexture(mTexture));
    VBOXQGL_CHECKERR(
    		glBindTexture(tt, mTexture);
    		);

    int x = pRect->x()/mColorFormat.widthCompression();
    int y = pRect->y()/mColorFormat.heightCompression();
    int width = pRect->width()/mColorFormat.widthCompression();
    int height = pRect->height()/mColorFormat.heightCompression();

    uchar * address = pAddress + pointOffsetTex(x, y);

    VBOXQGL_CHECKERR(
            glPixelStorei(GL_UNPACK_ROW_LENGTH, mRect.width()/mColorFormat.widthCompression());
            );

    VBOXQGL_CHECKERR(
            glTexSubImage2D(tt,
                0,
                x, y, width, height,
                mColorFormat.format(),
                mColorFormat.type(),
                address);
            );

#ifdef DEBUG_misha
	if(g_DbgTest)
	{
		free(pAddress);
	}
#endif
}

void VBoxVHWATexture::texCoord(int x, int y)
{
    glTexCoord2f(((float)x)/mTexRect.width()/mColorFormat.widthCompression(), ((float)y)/mTexRect.height()/mColorFormat.heightCompression());
}

void VBoxVHWATexture::multiTexCoord(GLenum texUnit, int x, int y)
{
    vboxglMultiTexCoord2f(texUnit, ((float)x)/mTexRect.width()/mColorFormat.widthCompression(), ((float)y)/mTexRect.height()/mColorFormat.heightCompression());
}

void VBoxVHWATexture::uninit()
{
    if(mTexture)
    {
        glDeleteTextures(1,&mTexture);
    }
}

VBoxVHWATexture::VBoxVHWATexture(const QRect & aRect, const VBoxVHWAColorFormat &aFormat)
		: mAddress(0),
          mTexture(0)
{
    mColorFormat = aFormat;
    mRect = aRect;
    mBytesPerPixel = mColorFormat.bitsPerPixel()/8;
    mBytesPerPixelTex = mColorFormat.bitsPerPixelTex()/8;
    mBytesPerLine = mBytesPerPixel * mRect.width();
    GLsizei wdt = VBoxVHWASurfaceBase::makePowerOf2(mRect.width()/mColorFormat.widthCompression());
    GLsizei hgt = VBoxVHWASurfaceBase::makePowerOf2(mRect.height()/mColorFormat.heightCompression());
    mTexRect = QRect(0,0,wdt,hgt);
}

void VBoxVHWATexture::initParams()
{
    GLenum tt = texTarget();

    glTexParameteri(tt, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    VBOXQGL_ASSERTNOERR();
    glTexParameteri(tt, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    VBOXQGL_ASSERTNOERR();
    glTexParameteri(tt, GL_TEXTURE_WRAP_S, GL_CLAMP);
    VBOXQGL_ASSERTNOERR();

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    VBOXQGL_ASSERTNOERR();
    glPixelStorei(GL_PACK_ALIGNMENT, 1);
    VBOXQGL_ASSERTNOERR();

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    VBOXQGL_ASSERTNOERR();
}

void VBoxVHWATexture::load()
{
    VBOXQGL_CHECKERR(
            glPixelStorei(GL_UNPACK_ROW_LENGTH, mTexRect.width());
            );

    VBOXQGL_CHECKERR(
        glTexImage2D(texTarget(),
                0,
                  mColorFormat.internalFormat(),
                  mTexRect.width(),
                  mTexRect.height(),
                  0,
                  mColorFormat.format(),
                  mColorFormat.type(),
                  (GLvoid *)mAddress);
        );
}

void VBoxVHWATexture::init(uchar *pvMem)
{
//    GLsizei wdt = mTexRect.width();
//    GLsizei hgt = mTexRect.height();

    VBOXQGL_CHECKERR(
            glGenTextures(1, &mTexture);
        );

    VBOXQGLLOG(("tex: %d", mTexture));

    bind();

    initParams();

    setAddress(pvMem);

    load();
}

VBoxVHWATexture::~VBoxVHWATexture()
{
	uninit();
}

void VBoxVHWATextureNP2Rect::texCoord(int x, int y)
{
    glTexCoord2f(((float)x)/mColorFormat.widthCompression(), ((float)y)/mColorFormat.heightCompression());
}

void VBoxVHWATextureNP2Rect::multiTexCoord(GLenum texUnit, int x, int y)
{
    vboxglMultiTexCoord2f(texUnit, x/mColorFormat.widthCompression(), y/mColorFormat.heightCompression());
}

GLenum VBoxVHWATextureNP2Rect::texTarget() {return GL_TEXTURE_RECTANGLE; }

void VBoxVHWASurfaceBase::synchTexMem(const QRect * pRect)
{
    if(pRect)
    {
        Assert(mRect.contains(*pRect));
    }

    if(mUpdateMem2TexRect.isClear())
        return;

    if(pRect && !mUpdateMem2TexRect.rect().intersects(*pRect))
        return;

    mpTex[0]->update(&mUpdateMem2TexRect.rect());
    if(fourcc() == FOURCC_YV12)
    {
        QRect rect(mUpdateMem2TexRect.rect().x()/2, mUpdateMem2TexRect.rect().y()/2,
                mUpdateMem2TexRect.rect().width()/2, mUpdateMem2TexRect.rect().height()/2);
        mpTex[1]->update(&rect);
        mpTex[2]->update(&rect);
    }

#if 0
    mUpdateTex2FBRect.add(mUpdateMem2TexRect);
    Assert(!mUpdateTex2FBRect.isClear());
    Assert(mRect.contains(mUpdateTex2FBRect.rect()));
#endif
    mUpdateMem2TexRect.clear();
    Assert(mUpdateMem2TexRect.isClear());
//#ifdef DEBUG
//    VBOXPRINTDIF(dbgTime, ("texMem:"));
//#endif
}

void VBoxVHWATextureNP2RectPBO::init(uchar *pvMem)
{
	VBOXQGL_CHECKERR(
			vboxglGenBuffers(1, &mPBO);
			);
	VBoxVHWATextureNP2Rect::init(pvMem);
}

void VBoxVHWATextureNP2RectPBO::doUpdate(uchar * pAddress, const QRect * pRect)
{
	Q_UNUSED(pAddress);
	Q_UNUSED(pRect);

	vboxglBindBuffer(GL_PIXEL_UNPACK_BUFFER, mPBO);

    GLvoid *buf = vboxglMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);

//    updateBuffer((uchar*)buf, pRect);
	memcpy(buf, mAddress, memSize());

	bool unmapped = vboxglUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
	Assert(unmapped);

    VBoxVHWATextureNP2Rect::doUpdate(0, &mRect);

	vboxglBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}

VBoxVHWATextureNP2RectPBO::~VBoxVHWATextureNP2RectPBO()
{
	VBOXQGL_CHECKERR(
			vboxglDeleteBuffers(1, &mPBO);
			);
}


void VBoxVHWATextureNP2RectPBO::load()
{
	VBoxVHWATextureNP2Rect::load();

	vboxglBindBuffer(GL_PIXEL_UNPACK_BUFFER, mPBO);

	vboxglBufferData(GL_PIXEL_UNPACK_BUFFER, memSize(), NULL, GL_STREAM_DRAW);

	GLvoid *buf = vboxglMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);

//	updateBuffer((uchar*)buf, &mRect);
	memcpy(buf, mAddress, memSize());

	bool unmapped = vboxglUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
	Assert(unmapped);

	vboxglBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}

#if 0
void VBoxVHWASurfaceBase::synch(const QRect * aRect)
{
    synchFB(aRect);
    synchTex(aRect);
    synchMem(aRect);
}

void VBoxVHWASurfaceBase::synchFB(const QRect * pRect)
{
    Assert(isYInverted());

    if(pRect)
    {
        Assert(mRect.contains(*pRect));
    }

    synchTexMem(pRect);

    if(mUpdateTex2FBRect.isClear())
        return;

    if(pRect && !mUpdateTex2FBRect.rect().intersects(*pRect))
        return;

    mState->makeCurrent(this);

    VBOXQGL_CHECKERR(
            glBindTexture(GL_TEXTURE_2D, mTexture);
            );

    VBoxVHWAGlProgramMngr * pMngr = getGlProgramMngr();
    pMngr->stopCurrentProgram();

    doTex2FB(&mUpdateTex2FBRect.rect(), &mUpdateTex2FBRect.rect());

    mUpdateTex2FBRect.clear();
    Assert(mUpdateTex2FBRect.isClear());
}

void VBoxVHWASurfaceBase::synchMem(const QRect * pRect)
{
    if(pRect)
    {
        Assert(mRect.contains(*pRect));
    }

    if(mUpdateFB2MemRect.isClear())
        return;

    if(pRect && !mUpdateFB2MemRect.rect().intersects(*pRect))
        return;

    mState->makeYInvertedCurrent(this);
//    mState->makeCurrent(this);

    uchar * address = pointAddress(mUpdateFB2MemRect.rect().x(), mUpdateFB2MemRect.rect().y());

    VBOXQGL_CHECKERR(
            glPixelStorei(GL_PACK_ROW_LENGTH, mRect.width());
            );
    VBOXQGL_CHECKERR(
            glReadPixels(
                mUpdateFB2MemRect.rect().x(),
                mUpdateFB2MemRect.rect().y(),
                mUpdateFB2MemRect.rect().width(),
                mUpdateFB2MemRect.rect().height(),
                mColorFormat.format(),
                mColorFormat.type(),
                address);
            );

    mUpdateFB2MemRect.clear();
    Assert(mUpdateFB2TexRect.isClear());
}

int VBoxVHWASurfaceBase::performBlt(const QRect * pDstRect, VBoxVHWASurfaceBase * pSrcSurface, const QRect * pSrcRect, const VBoxVHWAColorKey * pDstCKey, const VBoxVHWAColorKey * pSrcCKey, bool blt)
{
//    pDstCKey = NULL;
//    pSrcCKey = NULL;

    GLuint tex = pSrcSurface->textureSynched(pSrcRect);

    if(pDstCKey)
    {
        synchTex(pDstRect);
    }

    mState->makeCurrent(this, blt);

    VBoxVHWAGlProgramMngr * pMngr = getGlProgramMngr();
    VBoxVHWAGlProgramVHWA * pProgram = pMngr->getProgram(pSrcCKey != NULL, &pSrcSurface->colorFormat(), &colorFormat());
    if(pProgram)
    {
        if(pSrcCKey != NULL)
        {
            pProgram->start();
            setCKey(pProgram, &pSrcSurface->colorFormat(), pSrcCKey);

            vboxglActiveTexture(GL_TEXTURE0);
        }
    }
    else
    {
        pMngr->stopCurrentProgram();
    }

//    if(blt)
    {
        VBOXQGL_CHECKERR(
                glBindTexture(GL_TEXTURE_2D, tex);
                );

        //TODO: setup strething params
        GLsizei wdt = pSrcSurface->mTexRect.width();
        GLsizei hgt = pSrcSurface->mTexRect.height();

        VBOXQGL_CHECKERR(
                glMatrixMode(GL_TEXTURE);
                );
        VBOXQGL_CHECKERR(
                glPushMatrix();
            );

        VBoxGLWidget::doSetupMatrix(QSize(wdt, hgt), true);
        VBOXQGL_CHECKERR(
                glMatrixMode(GL_MODELVIEW);
                );

        doTex2FB(pDstRect, pSrcRect);

        VBOXQGL_CHECKERR(
                glMatrixMode(GL_TEXTURE);
                );
        VBOXQGL_CHECKERR(
                glPopMatrix();
                );
        VBOXQGL_CHECKERR(
                glMatrixMode(GL_MODELVIEW);
                );
    }
//    else
//    {
//
//    }

    /* if dst color key */
    /* setup ckey shader */
    if(pDstCKey)
    {
        VBOXQGL_CHECKERR(
                glBindTexture(GL_TEXTURE_2D, mTexture);
                );
        pProgram = pMngr->getProgram(true, NULL, NULL);
        /* setup ckey values*/
        setCKey(pProgram, &colorFormat(), pDstCKey);
        pProgram->start();
        doTex2FB(pDstRect, pDstRect);
    }

    return VINF_SUCCESS;
}

int VBoxVHWASurfaceBase::overlay(VBoxVHWASurfaceBase * pOverlaySurface)
{
    VBOXQGLLOG(("overlay src(0x%x) ", pOverlaySurface));
    VBOXQGLLOG_QRECT("dst: ", &pOverlaySurface->mTargRect, "\n");
    VBOXQGLLOG_QRECT("src: ", &pOverlaySurface->mSrcRect,  "\n");
    VBOXQGLLOG_METHODTIME("time:");

    Assert(!pOverlaySurface->isHidden());

    if(pOverlaySurface->isHidden())
    {
        VBOXQGLLOG(("!!!hidden!!!\n"));
        return VINF_SUCCESS;
    }

    const QRect * pSrcRect = &pOverlaySurface->mSrcRect;
    const QRect * pDstRect = &pOverlaySurface->mTargRect;
    const VBoxVHWAColorKey * pSrcCKey = pOverlaySurface->srcOverlayCKey();
    /* we use src (overlay) surface to maintain overridden dst ckey info
     * to allow multiple overlays have different overridden dst keys for one primary surface */
    /* non-null dstOverlayCKey for overlay would mean the overlay surface contains the overridden
     * dst ckey value in defaultDstOverlayCKey
     * this allows the NULL to be a valid overridden value as well */
    const VBoxVHWAColorKey * pDstCKey = pOverlaySurface->dstOverlayCKey() ? pOverlaySurface->defaultDstOverlayCKey() : dstOverlayCKey();

    return performBlt(pDstRect, pOverlaySurface, pSrcRect, pDstCKey, pSrcCKey, false);
}

int VBoxVHWASurfaceBase::blt(const QRect * pDstRect, VBoxVHWASurfaceBase * pSrcSurface, const QRect * pSrcRect, const VBoxVHWAColorKey * pDstCKey, const VBoxVHWAColorKey * pSrcCKey)
{
    if(pDstRect)
    {
        Assert(mRect.contains(*pDstRect));
    }
    else
    {
        pDstRect = &mRect;
    }

    if(pSrcRect)
    {
        Assert(pSrcSurface->mRect.contains(*pSrcRect));
    }
    else
    {
        pSrcRect = &pSrcSurface->mRect;
    }

    if(!pSrcCKey)
        pSrcCKey = pSrcSurface->srcBltCKey();
    if(!pDstCKey)
        pDstCKey = dstBltCKey();

    VBOXQGLLOG(("blt dst(0x%x), src(0x%x)", this, pSrcSurface));
    VBOXQGLLOG_QRECT("dst: ", pDstRect, "\n");
    VBOXQGLLOG_QRECT("src: ", pSrcRect, "\n");
    VBOXQGLLOG_METHODTIME("time:");
    int rc = performBlt(pDstRect, pSrcSurface, pSrcRect, pDstCKey, pSrcCKey, true);

    mUpdateFB2TexRect.add(*pDstRect);
    Assert(!mUpdateFB2TexRect.isClear());
    Assert(mRect.contains(mUpdateFB2TexRect.rect()));
//    synchTexture(pDstRect);
    mUpdateFB2MemRect.add(*pDstRect);
    Assert(!mUpdateFB2MemRect.isClear());
    Assert(mRect.contains(mUpdateFB2MemRect.rect()));

    return rc;
}
#endif
void VBoxVHWASurfaceBase::doTex2FB(const QRect * pDstRect, const QRect * pSrcRect)
{
    int tx1, ty1, tx2, ty2;
    pSrcRect->getCoords(&tx1, &ty1, &tx2, &ty2);
    int bx1, by1, bx2, by2;
    pDstRect->getCoords(&bx1, &by1, &bx2, &by2);
    tx2++; ty2++;bx2++; by2++;

#if 1
//    VBOXQGL_CHECKERR(
            VBOXQGLLOG_QRECT("texRect: ", &mpTex[0]->texRect(), "\n");
            glBegin(GL_QUADS);
//            glTexCoord2d(((double)tx1)/mpTex[0]->texRect().width(), ((double)ty1)/mpTex[0]->texRect().height());
//            glVertex2i(bx1, by1);
//            glTexCoord2d(((double)tx1)/mpTex[0]->texRect().width(), ((double)ty2)/mpTex[0]->texRect().height());
//            glVertex2i(bx1, by2);
//            glTexCoord2d(((double)tx2)/mpTex[0]->texRect().width(), ((double)ty2)/mpTex[0]->texRect().height());
//            glVertex2i(bx2, by2);
//            glTexCoord2d(((double)tx2)/mpTex[0]->texRect().width(), ((double)ty1)/mpTex[0]->texRect().height());
//            glVertex2i(bx2, by1);
            mpTex[0]->texCoord(tx1, ty1);
            glVertex2i(bx1, by1);
            mpTex[0]->texCoord(tx1, ty2);
            glVertex2i(bx1, by2);
            mpTex[0]->texCoord(tx2, ty2);
            glVertex2i(bx2, by2);
            mpTex[0]->texCoord(tx2, ty1);
            glVertex2i(bx2, by1);

            glEnd();
//            );
#else
        glBegin(GL_QUADS);
        glTexCoord2d(0.0, 0.0);
        glVertex2i(0, 0);
        glTexCoord2d(0.0, 1.0);
        glVertex2i(0, mRect.height());
        glTexCoord2d(1.0, 1.0);
        glVertex2i(mRect.width(), mRect.height());
        glTexCoord2d(1.0, 0.0);
        glVertex2i(mRect.width(), 0);
        glEnd();
#endif
}


void VBoxVHWASurfaceBase::doMultiTex2FB(const QRect * pDstRect, const QRect * pSrcRect, int cSrcTex)
{
    int tx1, ty1, tx2, ty2;
    pSrcRect->getCoords(&tx1, &ty1, &tx2, &ty2);
    int bx1, by1, bx2, by2;
    pDstRect->getCoords(&bx1, &by1, &bx2, &by2);
    tx2++; ty2++;bx2++; by2++;
    uint32_t t0width = mpTex[0]->rect().width();
    uint32_t t0height = mpTex[0]->rect().height();

//    VBOXQGL_CHECKERR(
            glBegin(GL_QUADS);
            for(int i = 0; i < cSrcTex; i++)
            {
//                vboxglMultiTexCoord2d(GL_TEXTURE0 + i, ((double)tx1)/mpTex[i]->texRect().width()/(width()/mpTex[i]->rect().width()),
//                        ((double)ty1)/mpTex[i]->texRect().height()/(height()/mpTex[i]->rect().height()));
            	mpTex[i]->multiTexCoord(GL_TEXTURE0 + i, tx1/(t0width/mpTex[i]->texRect().width()), ty1/(t0height/mpTex[i]->rect().height()));
            }
            glVertex2i(bx1, by1);
            for(int i = 0; i < cSrcTex; i++)
            {
//                vboxglMultiTexCoord2d(GL_TEXTURE0 + i, ((double)tx1)/mpTex[i]->texRect().width()/(width()/mpTex[i]->rect().width()),
//                        ((double)ty2)/mpTex[i]->texRect().height()/(height()/mpTex[i]->rect().height()));
            	mpTex[i]->multiTexCoord(GL_TEXTURE0 + i, tx1/(t0width/mpTex[i]->texRect().width()), ty2/(t0height/mpTex[i]->rect().height()));
            }
            glVertex2i(bx1, by2);
            for(int i = 0; i < cSrcTex; i++)
            {
//                vboxglMultiTexCoord2d(GL_TEXTURE0 + i, ((double)tx2)/mpTex[i]->texRect().width()/(width()/mpTex[i]->rect().width()),
//                        ((double)ty2)/mpTex[i]->texRect().height()/(height()/mpTex[i]->rect().height()));
            	mpTex[i]->multiTexCoord(GL_TEXTURE0 + i, tx2/(t0width/mpTex[i]->texRect().width()), ty2/(t0height/mpTex[i]->rect().height()));

            }
            glVertex2i(bx2, by2);
            for(int i = 0; i < cSrcTex; i++)
            {
//                vboxglMultiTexCoord2d(GL_TEXTURE0 + i, ((double)tx2)/mpTex[i]->texRect().width()/(width()/mpTex[i]->rect().width()),
//                        ((double)ty1)/mpTex[i]->texRect().height()/(height()/mpTex[i]->rect().height()));
            	mpTex[i]->multiTexCoord(GL_TEXTURE0 + i, tx2/(t0width/mpTex[i]->texRect().width()), ty1/(t0height/mpTex[i]->rect().height()));
            }
            glVertex2i(bx2, by1);
            glEnd();
//            );
}

void VBoxVHWASurfaceBase::doMultiTex2FB(const QRect * pDstRect, VBoxVHWATexture * pDstTex, const QRect * pSrcRect, int cSrcTex)
{
    int tx1, ty1, tx2, ty2;
    pSrcRect->getCoords(&tx1, &ty1, &tx2, &ty2);
    int bx1, by1, bx2, by2;
    pDstRect->getCoords(&bx1, &by1, &bx2, &by2);
    tx2++; ty2++;bx2++; by2++;
    uint32_t t0width = mpTex[0]->rect().width();
    uint32_t t0height = mpTex[0]->rect().height();

//    VBOXQGL_CHECKERR(
            glBegin(GL_QUADS);
            pDstTex->multiTexCoord(GL_TEXTURE0, bx1, by1);
            for(int i = 0; i < cSrcTex; i++)
            {
//                vboxglMultiTexCoord2d(GL_TEXTURE1 + i, ((double)tx1)/mpTex[i]->texRect().width()/(width()/mpTex[i]->rect().width()),
//                        ((double)ty1)/mpTex[i]->texRect().height()/(height()/mpTex[i]->rect().height()));
            	mpTex[i]->multiTexCoord(GL_TEXTURE1 + i, tx1/(t0width/mpTex[i]->rect().width()), ty1/(t0height/mpTex[i]->rect().height()));

            }
            glVertex2i(bx1, by1);
            pDstTex->multiTexCoord(GL_TEXTURE0, bx1, by2);
            for(int i = 0; i < cSrcTex; i++)
            {
//                vboxglMultiTexCoord2d(GL_TEXTURE1 + i, ((double)tx1)/mpTex[i]->texRect().width()/(width()/mpTex[i]->rect().width()),
//                        ((double)ty2)/mpTex[i]->texRect().height()/(height()/mpTex[i]->rect().height()));
            	mpTex[i]->multiTexCoord(GL_TEXTURE1 + i, tx1/(t0width/mpTex[i]->rect().width()), ty2/(t0height/mpTex[i]->rect().height()));

            }
            glVertex2i(bx1, by2);
            pDstTex->multiTexCoord(GL_TEXTURE0, bx2, by2);
            for(int i = 0; i < cSrcTex; i++)
            {
//                vboxglMultiTexCoord2d(GL_TEXTURE1 + i, ((double)tx2)/mpTex[i]->texRect().width()/(width()/mpTex[i]->rect().width()),
//                        ((double)ty2)/mpTex[i]->texRect().height()/(height()/mpTex[i]->rect().height()));
            	mpTex[i]->multiTexCoord(GL_TEXTURE1 + i, tx2/(t0width/mpTex[i]->rect().width()), ty2/(t0height/mpTex[i]->rect().height()));

            }
            glVertex2i(bx2, by2);
            pDstTex->multiTexCoord(GL_TEXTURE0, bx2, by1);
            for(int i = 0; i < cSrcTex; i++)
            {
//                vboxglMultiTexCoord2d(GL_TEXTURE1 + i, ((double)tx2)/mpTex[i]->texRect().width()/(width()/mpTex[i]->rect().width()),
//                        ((double)ty1)/mpTex[i]->texRect().height()/(height()/mpTex[i]->rect().height()));
            	mpTex[i]->multiTexCoord(GL_TEXTURE1 + i, tx2/(t0width/mpTex[i]->rect().width()), ty1/(t0height/mpTex[i]->rect().height()));

            }
            glVertex2i(bx2, by1);
            glEnd();
//            );

}

int VBoxVHWASurfaceBase::lock(const QRect * pRect, uint32_t flags)
{
	Q_UNUSED(flags);

    if(pRect)
    {
        Assert(mRect.contains(*pRect));
    }

    Assert(mLockCount >= 0);
    if(pRect && pRect->isEmpty())
        return VERR_GENERAL_FAILURE;
    if(mLockCount < 0)
        return VERR_GENERAL_FAILURE;

    VBOXQGLLOG(("lock (0x%x)", this));
    VBOXQGLLOG_QRECT("rect: ", pRect ? pRect : &mRect, "\n");
    VBOXQGLLOG_METHODTIME("time ");
//    if(!(flags & VBOXVHWA_LOCK_DISCARDCONTENTS))
//    {
//        synchMem(pRect);
//    }

    mUpdateMem2TexRect.add(pRect ? *pRect : mRect);

    Assert(!mUpdateMem2TexRect.isClear());
    Assert(mRect.contains(mUpdateMem2TexRect.rect()));
    return VINF_SUCCESS;
}

int VBoxVHWASurfaceBase::unlock()
{
    VBOXQGLLOG(("unlock (0x%x)\n", this));
    mLockCount = 0;
    return VINF_SUCCESS;
}

void VBoxVHWASurfaceBase::setRects(VBoxVHWASurfaceBase *pPrimary, const QRect * aTargRect, const QRect * aSrcRect)
{
    if(mTargRect == *aTargRect && mSrcRect == *aSrcRect)
        return;

    mTargRect = *aTargRect;
    mSrcRect = *aSrcRect;

    initDisplay(pPrimary);
}

void VBoxVHWASurfaceBase::setTargetRectPosition(VBoxVHWASurfaceBase *pPrimary, const QPoint * aPoint)
{
    if(mTargRect.topLeft() == *aPoint)
        return;

    mTargRect = QRect(aPoint->x(), aPoint->y(), mTargRect.width(), mTargRect.height());

    initDisplay(pPrimary
//            false
            );
}

void VBoxVHWASurfaceBase::deleteDisplay(
//        bool bInverted
        )
{
    {
        if(mVisibleDisplayInitialized)
        {
            glDeleteLists(mVisibleDisplay, 1);
            mVisibleDisplayInitialized = false;
        }
    }
}

void VBoxVHWASurfaceBase::doDisplay(VBoxVHWASurfaceBase *pPrimary, VBoxVHWAGlProgramVHWA * pProgram, bool bBindDst)
{
    bool bInvokeMultiTex2 = false;

    if(pProgram)
    {
        pProgram->start();

//            if(pSrcCKey != NULL)
//            {
//                pProgram->start();
//                setCKey(pProgram, &pSrcSurface->colorFormat(), pSrcCKey);

//                vboxglActiveTexture(GL_TEXTURE0);
//            }

        if(bBindDst)
        {
            vboxglActiveTexture(GL_TEXTURE1);
            mpTex[0]->bind();
            if(fourcc() == FOURCC_YV12)
            {
                vboxglActiveTexture(GL_TEXTURE1+1);
                mpTex[1]->bind();
                vboxglActiveTexture(GL_TEXTURE1 + 2);
                mpTex[2]->bind();
            }
            vboxglActiveTexture(GL_TEXTURE0);
            pPrimary->mpTex[0]->bind();
            bInvokeMultiTex2 = true;
        }
        else
        {
            if(fourcc() == FOURCC_YV12)
            {
                vboxglActiveTexture(GL_TEXTURE1);
                mpTex[1]->bind();
                vboxglActiveTexture(GL_TEXTURE1 + 1);
                mpTex[2]->bind();
                vboxglActiveTexture(GL_TEXTURE0);
            }
            mpTex[0]->bind();
        }
    }
    else
    {
//        vboxglActiveTexture(GL_TEXTURE0);
        mpTex[0]->bind();
//        VBOXQGLLOG(("binding (primary??) texture: %d\n", mpTex[0]->texture()));
    }

    if(bInvokeMultiTex2)
    {
        doMultiTex2FB(&mTargRect, pPrimary->mpTex[0], &mSrcRect,
                (fourcc() == FOURCC_YV12) ? 3 : 1);
    }
    else
    {
        if(fourcc() == FOURCC_YV12)
        {
            doMultiTex2FB(&mTargRect, &mSrcRect, 3 );
        }
        else
        {
            VBOXQGLLOG_QRECT("mTargRect: ", &mTargRect, "\n");
            VBOXQGLLOG_QRECT("mSrcRect: ", &mSrcRect, "\n");
            doTex2FB(&mTargRect, &mSrcRect);
        }
    }

    if(pProgram)
    {
        pProgram->stop();
    }
}

GLuint VBoxVHWASurfaceBase::createDisplay(VBoxVHWASurfaceBase *pPrimary)
{
    VBoxVHWAGlProgramVHWA * pProgram = NULL;
    const VBoxVHWAColorKey * pSrcCKey = NULL, *pDstCKey = NULL;
    if(pPrimary)
    {
        pSrcCKey = getActiveSrcOverlayCKey();
        /* we use src (overlay) surface to maintain overridden dst ckey info
         * to allow multiple overlays have different overridden dst keys for one primary surface */
        /* non-null dstOverlayCKey for overlay would mean the overlay surface contains the overridden
         * dst ckey value in defaultDstOverlayCKey
         * this allows the NULL to be a valid overridden value as well */
        pDstCKey = getActiveDstOverlayCKey(pPrimary);
//        pSrcCKey = NULL;
//        pDstCKey = NULL;

        pProgram = mWidget->vboxVHWAGetGlProgramMngr()->getProgram(pDstCKey != NULL, pSrcCKey != NULL, &colorFormat(), &pPrimary->colorFormat());
    }

    GLuint display = glGenLists(1);
    VBOXQGL_ASSERTNOERR();
    glNewList(display, GL_COMPILE);

    doDisplay(pPrimary, pProgram, pDstCKey != NULL);

    glEndList();
    VBOXQGL_ASSERTNOERR();

    return display;
}

void VBoxVHWASurfaceBase::initDisplay(VBoxVHWASurfaceBase *pPrimary)
{
    deleteDisplay();


    {
        mVisibleDisplay = createDisplay(pPrimary);
        mVisibleDisplayInitialized = true;
    }
}

void VBoxVHWASurfaceBase::updatedMem(const QRect *rec)
{
    if(rec)
    {
        Assert(mRect.contains(*rec));
    }
    mUpdateMem2TexRect.add(*rec);
}

void VBoxVHWASurfaceBase::performDisplay(VBoxVHWASurfaceBase *pPrimary)
{
    Assert(mVisibleDisplayInitialized);

    synchTexMem(&mSrcRect);
    if(pPrimary && getActiveDstOverlayCKey(pPrimary))
    {
        pPrimary->synchTexMem(&mTargRect);
    }

#ifdef DEBUG_misha
    if(0)
    {
        VBoxVHWAGlProgramVHWA * pProgram = NULL;
        const VBoxVHWAColorKey * pSrcCKey = NULL, *pDstCKey = NULL;
        if(pPrimary)
        {
            pSrcCKey = getActiveSrcOverlayCKey();
            /* we use src (overlay) surface to maintain overridden dst ckey info
             * to allow multiple overlays have different overridden dst keys for one primary surface */
            /* non-null dstOverlayCKey for overlay would mean the overlay surface contains the overridden
             * dst ckey value in defaultDstOverlayCKey
             * this allows the NULL to be a valid overridden value as well */
            pDstCKey = getActiveDstOverlayCKey(pPrimary);
    //        pSrcCKey = NULL;
    //        pDstCKey = NULL;

            pProgram = mWidget->vboxVHWAGetGlProgramMngr()->getProgram(pDstCKey != NULL, pSrcCKey != NULL, &colorFormat(), &pPrimary->colorFormat());
        }

        doDisplay(pPrimary, pProgram, pDstCKey != NULL);

//        doDisplay(pPrimary, NULL, false);
    }
    else
#endif
    {
        VBOXQGL_CHECKERR(
                glCallList(mVisibleDisplay);
                );
    }
}

/** @class VBoxQGLFrameBuffer
 *
 *  The VBoxQImageFrameBuffer class is a class that implements the IFrameBuffer
 *  interface and uses QImage as the direct storage for VM display data. QImage
 *  is then converted to QPixmap and blitted to the console view widget.
 */

VBoxQGLFrameBuffer::VBoxQGLFrameBuffer (VBoxConsoleView *aView) :
    VBoxFrameBuffer (aView)
{
//    mWidget = new GLWidget(aView->viewport());
#ifndef VBOXQGL_PROF_BASE
    resizeEvent (new VBoxResizeEvent (FramebufferPixelFormat_Opaque,
                                      NULL, 0, 0, 640, 480));
#else
    resizeEvent (new VBoxResizeEvent (FramebufferPixelFormat_Opaque,
                                      NULL, 0, 0, VBOXQGL_PROF_WIDTH, VBOXQGL_PROF_HEIGHT));
#endif
}

/** @note This method is called on EMT from under this object's lock */
STDMETHODIMP VBoxQGLFrameBuffer::NotifyUpdate (ULONG aX, ULONG aY,
                                                  ULONG aW, ULONG aH)
{
//    /* We're not on the GUI thread and update() isn't thread safe in
//     * Qt 4.3.x on the Win, Qt 3.3.x on the Mac (4.2.x is),
//     * on Linux (didn't check Qt 4.x there) and probably on other
//     * non-DOS platforms, so post the event instead. */
#ifdef VBOXQGL_PROF_BASE
    QApplication::postEvent (mView,
                             new VBoxRepaintEvent (aX, aY, aW, aH));
#else
    QRect r(aX, aY, aW, aH);
    vboxWidget()->postCmd(VBOXVHWA_PIPECMD_PAINT, &r);
#endif
    return S_OK;
}

#ifdef VBOXQGL_PROF_BASE
STDMETHODIMP VBoxQGLFrameBuffer::RequestResize (ULONG aScreenId, ULONG aPixelFormat,
                              BYTE *aVRAM, ULONG aBitsPerPixel, ULONG aBytesPerLine,
                              ULONG aWidth, ULONG aHeight,
                              BOOL *aFinished)
{
	aWidth = VBOXQGL_PROF_WIDTH;
	aHeight = VBOXQGL_PROF_HEIGHT;
    VBoxFrameBuffer::RequestResize (aScreenId, aPixelFormat,
            aVRAM, aBitsPerPixel, aBytesPerLine,
            aWidth, aHeight,
            aFinished);

//    if(aVRAM)
    {
        for(;;)
        {
            ULONG aX = 0;
            ULONG aY = 0;
            ULONG aW = aWidth;
            ULONG aH = aHeight;
            NotifyUpdate (aX, aY, aW, aH);
            RTThreadSleep(40);
        }
    }
    return S_OK;
}
#endif

VBoxGLWidget* VBoxQGLFrameBuffer::vboxWidget()
{
    return (VBoxGLWidget*)mView->viewport();
}

void VBoxQGLFrameBuffer::paintEvent (QPaintEvent *pe)
{
    vboxWidget()->vboxPaintEvent(pe);
}

void VBoxQGLFrameBuffer::resizeEvent (VBoxResizeEvent *re)
{
    mWdt = re->width();
    mHgt = re->height();

    vboxWidget()->vboxResizeEvent(re);
}

/* processing the VHWA command, called from the GUI thread */
void VBoxQGLFrameBuffer::doProcessVHWACommand(QEvent * pEvent)
{
    vboxWidget()->vboxProcessVHWACommands((VBoxVHWACommandProcessEvent*)pEvent);
}

VBoxGLWidget::VBoxGLWidget (VBoxConsoleView *aView, QWidget *aParent)
    : QGLWidget (VBoxGLWidget::vboxGLFormat(), aParent),
    mSurfHandleTable(128), /* 128 should be enough */
    mpfnOp(NULL),
    mOpContext(NULL),
    mPixelFormat(0),
    mUsesGuestVRAM(false),
    mbVGASurfCreated(false),
    mView(aView),
    mConstructingList(NULL),
    mcRemaining2Contruct(0)
{
    cmdPipeInit();
    mpMngr = new VBoxVHWAGlProgramMngr();
//        /* No need for background drawing */
//        setAttribute (Qt::WA_OpaquePaintEvent);
}

const QGLFormat & VBoxGLWidget::vboxGLFormat()
{
    static QGLFormat vboxFormat = QGLFormat();
    vboxFormat.setAlpha(true);
    Assert(vboxFormat.alpha());
    vboxFormat.setSwapInterval(0);
    Assert(vboxFormat.swapInterval() == 0);
    vboxFormat.setAccum(false);
    Assert(!vboxFormat.accum());
    vboxFormat.setDepth(false);
    Assert(!vboxFormat.depth());
    return vboxFormat;
}

VBoxGLWidget::~VBoxGLWidget()
{
    delete mpMngr;
    cmdPipeDelete();
}

void VBoxGLWidget::cmdPipeInit()
{
    int rc = RTCritSectInit(&mCritSect);
    AssertRC(rc);

    mpFirstEvent = NULL;
    mpLastEvent = NULL;
    mbNewEvent = false;
    for(int i = RT_ELEMENTS(mElementsBuffer) - 1; i >= 0; i--)
    {
        mFreeElements.push(&mElementsBuffer[i]);
    }
}

void VBoxGLWidget::cmdPipeDelete()
{
    RTCritSectDelete(&mCritSect);
}

void VBoxGLWidget::doSetupMatrix(const QSize & aSize, bool bInverted)
{
    VBOXQGL_CHECKERR(
            glLoadIdentity();
            );
    if(bInverted)
    {
        VBOXQGL_CHECKERR(
                glScalef(1.0f/aSize.width(), 1.0f/aSize.height(), 1.0f);
                );
    }
    else
    {
        /* make display coordinates be scaled to pixel coordinates */
        VBOXQGL_CHECKERR(
                glTranslatef(0.0f, 1.0f, 0.0f);
                );
        VBOXQGL_CHECKERR(
                glScalef(1.0f/aSize.width(), 1.0f/aSize.height(), 1.0f);
                );
        VBOXQGL_CHECKERR(
                glRotatef(180.0f, 1.0f, 0.0f, 0.0f);
                );
    }
}
void VBoxGLWidget::adjustViewport(const QSize &display, const QRect &viewport)
{
    /* viewport:  (viewport.x;viewport.y) (viewport.width;viewport.height)*/
    glViewport(-((int)display.width() + viewport.x()),
                -((int)display.height() - viewport.y() + display.height() - viewport.height()),
                2*display.width(),
                2*display.height());
}

void VBoxGLWidget::setupMatricies(const QSize &display)
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(0., (GLdouble)display.width(), 0., (GLdouble)display.height(), 0., 0.);

    glMatrixMode(GL_MODELVIEW);
    //    doSetupMatrix(bInverted ? &mRect.size() : &mTargSize.size(), bInverted);
    doSetupMatrix(display, false);
}

void VBoxGLWidget::postCmd(VBOXVHWA_PIPECMD_TYPE aType, void * pvData)
{
    /* 1. lock*/
    RTCritSectEnter(&mCritSect);
    VBoxVHWACommandElement * pCmd = mFreeElements.pop();
    if(!pCmd)
    {
        VBOXQGLLOG(("!!!no more free elements!!!\n"));
#ifdef VBOXQGL_PROF_BASE
        RTCritSectLeave(&mCritSect);
        return;
#else
    //TODO:
#endif
    }
    pCmd->setData(aType, pvData);
    /* 2. if can add to current*/
    if(!mbNewEvent)
    {
        /* 3. if event is being processed (event != 0) */
        if(mpLastEvent)
        {
            /* 3.a add cmd to event */
            mpLastEvent->pipe().put(pCmd);
            /* 3.b unlock and return */
            RTCritSectLeave(&mCritSect);
            return;
        }
    }

    /* we're here because the cmd was NOT be added to the current event queue */
    /* 4. unlock*/
    RTCritSectLeave(&mCritSect);
    /* 5. create & initialize new Event */
    VBoxVHWACommandProcessEvent *pCurrentEvent = new VBoxVHWACommandProcessEvent(pCmd);
    /* 6. lock */
    RTCritSectEnter(&mCritSect);
    /* 7. if no current event set event as current */
    if(!mpLastEvent)
    {
        Assert(!mpFirstEvent);
        mpFirstEvent = pCurrentEvent;
        mpLastEvent = pCurrentEvent;
        pCurrentEvent->mpNext = NULL;
    }
    else
    {
        mpLastEvent->mpNext = pCurrentEvent;
        mpLastEvent = pCurrentEvent;
    }
    /* 8. reset blocking events counter */
    mbNewEvent = false;
    /* 9. unlock */
    RTCritSectLeave(&mCritSect);
    /* 10. post event */
    QApplication::postEvent (mView, pCurrentEvent);
}

VBoxVHWACommandElement * VBoxGLWidget::detachCmdList(VBoxVHWACommandElement * pFirst2Free, VBoxVHWACommandElement * pLast2Free)
{
    VBoxVHWACommandElement * pList = NULL;
    RTCritSectEnter(&mCritSect);
    if(pFirst2Free)
    {
        mFreeElements.pusha(pFirst2Free, pLast2Free);
    }
    if(mpFirstEvent)
    {
        pList = mpFirstEvent->pipe().detachList();
        if(!pList)
        {
            VBoxVHWACommandProcessEvent *pNext = mpFirstEvent->mpNext;
            if(pNext)
            {
                mpFirstEvent = pNext;
            }
            else
            {
                mpFirstEvent = NULL;
                mpLastEvent = NULL;
            }
        }
    }
    RTCritSectLeave(&mCritSect);

    return pList;
}

VBoxVHWACommandElement * VBoxGLWidget::processCmdList(VBoxVHWACommandElement * pCmd)
{
    VBoxVHWACommandElement * pCur;
    do
    {
        pCur = pCmd;
        switch(pCmd->type())
        {
        case VBOXVHWA_PIPECMD_PAINT:
            vboxDoUpdateRect(&pCmd->rect());
            break;
#ifdef VBOX_WITH_VIDEOHWACCEL
        case VBOXVHWA_PIPECMD_VHWA:
            vboxDoVHWACmd(pCmd->vhwaCmd());
            break;
        case VBOXVHWA_PIPECMD_OP:
        {
            const VBOXVHWACALLBACKINFO & info = pCmd->op();
            (info.pThis->*(info.pfnCallback))(info.pContext);
            break;
        }
#endif
        default:
            Assert(0);
        }
        pCmd = pCmd->mpNext;
    } while(pCmd);

    return pCur;
}

void VBoxGLWidget::vboxDoProcessVHWACommands(void *pContext)
{
    Q_UNUSED(pContext);
    VBoxVHWACommandElement * pFirst = detachCmdList(NULL, NULL);
    do
    {
        VBoxVHWACommandElement * pLast = processCmdList(pFirst);

        pFirst = detachCmdList(pFirst, pLast);
    } while(pFirst);

    mDisplay.performDisplay();
}

#ifdef VBOX_WITH_VIDEOHWACCEL
/* static */
bool VBoxQGLFrameBuffer::isAcceleration2DVideoAvailable()
{
    vboxVHWAGlInit(NULL);
    return vboxVHWASupportedInternal();
}

STDMETHODIMP VBoxQGLFrameBuffer::ProcessVHWACommand(BYTE *pCommand)
{
    VBOXVHWACMD * pCmd = (VBOXVHWACMD*)pCommand;
//    Assert(0);
    /* indicate that we process and complete the command asynchronously */
    pCmd->Flags |= VBOXVHWACMD_FLAG_HG_ASYNCH;
    /* post the command to the GUI thread for processing */
//    QApplication::postEvent (mView,
//                             new VBoxVHWACommandProcessEvent (pCmd));
    vboxWidget()->postCmd(VBOXVHWA_PIPECMD_VHWA, pCmd);
    return S_OK;
}

void VBoxGLWidget::vboxDoVHWACmd(void *cmd)
{
    vboxDoVHWACmdExec(cmd);

    CDisplay display = mView->console().GetDisplay();
    Assert (!display.isNull());

    display.CompleteVHWACommand((BYTE*)cmd);
}

void VBoxGLWidget::vboxDoVHWACmdAndFree(void *cmd)
{
    vboxDoVHWACmdExec(cmd);

    free(cmd);
}

void VBoxGLWidget::vboxDoVHWACmdExec(void *cmd)
{
    struct _VBOXVHWACMD * pCmd = (struct _VBOXVHWACMD *)cmd;
    switch(pCmd->enmCmd)
    {
        case VBOXVHWACMD_TYPE_SURF_CANCREATE:
        {
            VBOXVHWACMD_SURF_CANCREATE * pBody = VBOXVHWACMD_BODY(pCmd, VBOXVHWACMD_SURF_CANCREATE);
            pCmd->rc = vhwaSurfaceCanCreate(pBody);
        } break;
        case VBOXVHWACMD_TYPE_SURF_CREATE:
        {
            VBOXVHWACMD_SURF_CREATE * pBody = VBOXVHWACMD_BODY(pCmd, VBOXVHWACMD_SURF_CREATE);
            pCmd->rc = vhwaSurfaceCreate(pBody);
        } break;
        case VBOXVHWACMD_TYPE_SURF_DESTROY:
        {
            VBOXVHWACMD_SURF_DESTROY * pBody = VBOXVHWACMD_BODY(pCmd, VBOXVHWACMD_SURF_DESTROY);
            pCmd->rc = vhwaSurfaceDestroy(pBody);
        } break;
        case VBOXVHWACMD_TYPE_SURF_LOCK:
        {
            VBOXVHWACMD_SURF_LOCK * pBody = VBOXVHWACMD_BODY(pCmd, VBOXVHWACMD_SURF_LOCK);
            pCmd->rc = vhwaSurfaceLock(pBody);
        } break;
        case VBOXVHWACMD_TYPE_SURF_UNLOCK:
        {
            VBOXVHWACMD_SURF_UNLOCK * pBody = VBOXVHWACMD_BODY(pCmd, VBOXVHWACMD_SURF_UNLOCK);
            pCmd->rc = vhwaSurfaceUnlock(pBody);
        } break;
        case VBOXVHWACMD_TYPE_SURF_BLT:
        {
            VBOXVHWACMD_SURF_BLT * pBody = VBOXVHWACMD_BODY(pCmd, VBOXVHWACMD_SURF_BLT);
            pCmd->rc = vhwaSurfaceBlt(pBody);
        } break;
        case VBOXVHWACMD_TYPE_SURF_FLIP:
        {
            VBOXVHWACMD_SURF_FLIP * pBody = VBOXVHWACMD_BODY(pCmd, VBOXVHWACMD_SURF_FLIP);
            pCmd->rc = vhwaSurfaceFlip(pBody);
        } break;
        case VBOXVHWACMD_TYPE_SURF_OVERLAY_UPDATE:
        {
            VBOXVHWACMD_SURF_OVERLAY_UPDATE * pBody = VBOXVHWACMD_BODY(pCmd, VBOXVHWACMD_SURF_OVERLAY_UPDATE);
            pCmd->rc = vhwaSurfaceOverlayUpdate(pBody);
        } break;
        case VBOXVHWACMD_TYPE_SURF_OVERLAY_SETPOSITION:
        {
            VBOXVHWACMD_SURF_OVERLAY_SETPOSITION * pBody = VBOXVHWACMD_BODY(pCmd, VBOXVHWACMD_SURF_OVERLAY_SETPOSITION);
            pCmd->rc = vhwaSurfaceOverlaySetPosition(pBody);
        } break;
        case VBOXVHWACMD_TYPE_SURF_COLORKEY_SET:
        {
            VBOXVHWACMD_SURF_COLORKEY_SET * pBody = VBOXVHWACMD_BODY(pCmd, VBOXVHWACMD_SURF_COLORKEY_SET);
            pCmd->rc = vhwaSurfaceColorkeySet(pBody);
        } break;
        case VBOXVHWACMD_TYPE_QUERY_INFO1:
        {
            VBOXVHWACMD_QUERYINFO1 * pBody = VBOXVHWACMD_BODY(pCmd, VBOXVHWACMD_QUERYINFO1);
            pCmd->rc = vhwaQueryInfo1(pBody);
        } break;
        case VBOXVHWACMD_TYPE_QUERY_INFO2:
        {
            VBOXVHWACMD_QUERYINFO2 * pBody = VBOXVHWACMD_BODY(pCmd, VBOXVHWACMD_QUERYINFO2);
            pCmd->rc = vhwaQueryInfo2(pBody);
        } break;
        case VBOXVHWACMD_TYPE_ENABLE:
        case VBOXVHWACMD_TYPE_DISABLE:
            pCmd->rc = VINF_SUCCESS;
            break;
        case VBOXVHWACMD_TYPE_HH_CONSTRUCT:
        {
            VBOXVHWACMD_HH_CONSTRUCT * pBody = VBOXVHWACMD_BODY(pCmd, VBOXVHWACMD_HH_CONSTRUCT);
            pCmd->rc = vhwaConstruct(pBody);
        } break;
        default:
            Assert(0);
            pCmd->rc = VERR_NOT_IMPLEMENTED;
            break;
    }
}

int VBoxGLWidget::vhwaSurfaceCanCreate(struct _VBOXVHWACMD_SURF_CANCREATE *pCmd)
{
    VBOXQGLLOG_ENTER(("\n"));

    if(!(pCmd->SurfInfo.flags & VBOXVHWA_SD_CAPS))
    {
        Assert(0);
        pCmd->u.out.ErrInfo = 1;
        return VINF_SUCCESS;
    }

    if(pCmd->SurfInfo.surfCaps & VBOXVHWA_SCAPS_PRIMARYSURFACE)
    {
        pCmd->u.out.ErrInfo = 0;
        return VINF_SUCCESS;
    }

    if(pCmd->SurfInfo.surfCaps & VBOXVHWA_SCAPS_OFFSCREENPLAIN)
    {
        pCmd->u.out.ErrInfo = 1;
        return VINF_SUCCESS;
    }

    Assert(pCmd->SurfInfo.surfCaps & VBOXVHWA_SCAPS_OFFSCREENPLAIN
            || pCmd->SurfInfo.surfCaps & VBOXVHWA_SCAPS_OVERLAY);

    if(pCmd->u.in.bIsDifferentPixelFormat)
    {
        if(!(pCmd->SurfInfo.flags & VBOXVHWA_SD_PIXELFORMAT))
        {
            Assert(0);
            pCmd->u.out.ErrInfo = 1;
            return VINF_SUCCESS;
        }

        if(pCmd->SurfInfo.PixelFormat.flags & VBOXVHWA_PF_RGB)
        {
            if(pCmd->SurfInfo.PixelFormat.c.rgbBitCount != 32
                    || pCmd->SurfInfo.PixelFormat.c.rgbBitCount != 24)
            {
                Assert(0);
                pCmd->u.out.ErrInfo = 1;
                return VINF_SUCCESS;
            }
        }
        else if(pCmd->SurfInfo.PixelFormat.flags & VBOXVHWA_PF_FOURCC)
        {
            /* detect whether we support this format */
            bool bFound = false;
            for(uint32_t i = 0; i < g_vboxVHWAFourccSupportedCount; i++)
            {
                if(g_vboxVHWAFourccSupportedList[i] == pCmd->SurfInfo.PixelFormat.fourCC)
                {
                    bFound = true;
                    break;
                }
            }
            Assert(bFound);
            if(!bFound)
            {
                Assert(0);
                pCmd->u.out.ErrInfo = 1;
                return VINF_SUCCESS;
            }
        }
        else
        {
            Assert(0);
            pCmd->u.out.ErrInfo = 1;
            return VINF_SUCCESS;
        }
    }

    pCmd->u.out.ErrInfo = 0;
    return VINF_SUCCESS;
}

int VBoxGLWidget::vhwaSurfaceCreate(struct _VBOXVHWACMD_SURF_CREATE *pCmd)
{
    VBOXQGLLOG_ENTER(("\n"));

    uint32_t handle = VBOXVHWA_SURFHANDLE_INVALID;
    if(pCmd->SurfInfo.hSurf != VBOXVHWA_SURFHANDLE_INVALID)
    {
        handle = pCmd->SurfInfo.hSurf;
        if(mSurfHandleTable.get(handle))
        {
//            do
//            {
//                if(!mcVGASurfCreated)
//                {
//                    /* check if it is a primary surface that needs handle adjusting*/
//                    if((pCmd->SurfInfo.surfCaps & VBOXVHWA_SCAPS_PRIMARYSURFACE)
//                            && handle2Surface(handle) == mDisplay.getVGA())
//                    {
//                        /* remove, the primary surface will be assigned to a new handle assumed by the guest */
//                        mSurfHandleTable.remove(handle);
//                        break;
//                    }
//                }
                Assert(0);
                return VERR_GENERAL_FAILURE;
//            }while(0);
        }
    }

    VBoxVHWASurfaceBase *surf = NULL;
//    VBoxVHWAColorFormat *pFormat = NULL, Format;
    bool bPrimary = false;

    VBoxVHWAColorKey *pDstBltCKey = NULL, DstBltCKey;
    VBoxVHWAColorKey *pSrcBltCKey = NULL, SrcBltCKey;
    VBoxVHWAColorKey *pDstOverlayCKey = NULL, DstOverlayCKey;
    VBoxVHWAColorKey *pSrcOverlayCKey = NULL, SrcOverlayCKey;
    if(pCmd->SurfInfo.flags & VBOXVHWA_SD_CKDESTBLT)
    {
        DstBltCKey = VBoxVHWAColorKey(pCmd->SurfInfo.DstBltCK.high, pCmd->SurfInfo.DstBltCK.low);
        pDstBltCKey = &DstBltCKey;
    }
    if(pCmd->SurfInfo.flags & VBOXVHWA_SD_CKSRCBLT)
    {
        SrcBltCKey = VBoxVHWAColorKey(pCmd->SurfInfo.SrcBltCK.high, pCmd->SurfInfo.SrcBltCK.low);
        pSrcBltCKey = &SrcBltCKey;
    }
    if(pCmd->SurfInfo.flags & VBOXVHWA_SD_CKDESTOVERLAY)
    {
        DstOverlayCKey = VBoxVHWAColorKey(pCmd->SurfInfo.DstOverlayCK.high, pCmd->SurfInfo.DstOverlayCK.low);
        pDstOverlayCKey = &DstOverlayCKey;
    }
    if(pCmd->SurfInfo.flags & VBOXVHWA_SD_CKSRCOVERLAY)
    {
        SrcOverlayCKey = VBoxVHWAColorKey(pCmd->SurfInfo.SrcOverlayCK.high, pCmd->SurfInfo.SrcOverlayCK.low);
        pSrcOverlayCKey = &SrcOverlayCKey;
    }

    if(pCmd->SurfInfo.surfCaps & VBOXVHWA_SCAPS_PRIMARYSURFACE)
    {
        bPrimary = true;
        if(!mbVGASurfCreated)
        {
            VBoxVHWASurfaceBase * pVga = vboxGetVGASurface();
            if(pCmd->SurfInfo.PixelFormat.flags & VBOXVHWA_PF_RGB)
            {
                if(pCmd->SurfInfo.width == pVga->width()
                        && pCmd->SurfInfo.height == pVga->height())
                {
                    VBoxVHWAColorFormat format(pCmd->SurfInfo.PixelFormat.c.rgbBitCount,
                                                pCmd->SurfInfo.PixelFormat.m1.rgbRBitMask,
                                                pCmd->SurfInfo.PixelFormat.m2.rgbGBitMask,
                                                pCmd->SurfInfo.PixelFormat.m3.rgbBBitMask);
                    if(pVga->colorFormat().equals(format))
                    {
                        surf = pVga;

                        surf->setDstBltCKey(pDstBltCKey);
                        surf->setSrcBltCKey(pSrcBltCKey);

                        surf->setDefaultDstOverlayCKey(pDstOverlayCKey);
                        surf->resetDefaultDstOverlayCKey();

                        surf->setDefaultSrcOverlayCKey(pDstOverlayCKey);
                        surf->resetDefaultSrcOverlayCKey();
                        mbVGASurfCreated = true;
                    }
                }
            }
        }
    }

    if(!surf)
    {
        if(pCmd->SurfInfo.PixelFormat.flags & VBOXVHWA_PF_RGB)
        {
            VBoxVHWAColorFormat format(pCmd->SurfInfo.PixelFormat.c.rgbBitCount,
        	                                pCmd->SurfInfo.PixelFormat.m1.rgbRBitMask,
        	                                pCmd->SurfInfo.PixelFormat.m2.rgbGBitMask,
        	                                pCmd->SurfInfo.PixelFormat.m3.rgbBBitMask);
            QSize surfSize(pCmd->SurfInfo.width, pCmd->SurfInfo.height);
            QSize primarySize = mDisplay.getPrimary()->rect().size();
            surf = new VBoxVHWASurfaceBase(this, &surfSize,
//                        ((pCmd->SurfInfo.surfCaps & VBOXVHWA_SCAPS_OVERLAY) ? mDisplay.getPrimary()->rect().size() : &surfSize),
                        &primarySize,
                        format,
                        pSrcBltCKey, pDstBltCKey, pSrcOverlayCKey, pDstOverlayCKey,
                        bPrimary);
        }
        else if(pCmd->SurfInfo.PixelFormat.flags & VBOXVHWA_PF_FOURCC)
        {
            QSize surfSize(pCmd->SurfInfo.width, pCmd->SurfInfo.height);
            QSize primarySize = mDisplay.getPrimary()->rect().size();

            VBoxVHWAColorFormat format(pCmd->SurfInfo.PixelFormat.fourCC);
            surf = new VBoxVHWASurfaceBase(this, &surfSize,
            //                        ((pCmd->SurfInfo.surfCaps & VBOXVHWA_SCAPS_OVERLAY) ? mDisplay.getPrimary()->rect().size() : &QSize(pCmd->SurfInfo.width, pCmd->SurfInfo.height)),
                                    &primarySize,
                                    format,
                                    pSrcBltCKey, pDstBltCKey, pSrcOverlayCKey, pDstOverlayCKey,
                                    bPrimary);
        }
        else
        {
            Assert(0);
            VBOXQGLLOG_EXIT(("pSurf (0x%x)\n",surf));
            return VERR_GENERAL_FAILURE;
        }

        uchar * addr = vboxVRAMAddressFromOffset(pCmd->SurfInfo.offSurface);
        surf->init(mDisplay.getVGA(), addr);

        if(pCmd->SurfInfo.surfCaps & VBOXVHWA_SCAPS_OVERLAY)
        {
            Assert(!bPrimary);

            if(!mConstructingList)
            {
                mConstructingList = new VBoxVHWASurfList();
                mcRemaining2Contruct = pCmd->SurfInfo.cBackBuffers+1;
                mDisplay.addOverlay(mConstructingList);
            }

            mConstructingList->add(surf);
            mcRemaining2Contruct--;
            if(!mcRemaining2Contruct)
            {
                mConstructingList = NULL;
            }
        }
        else if(bPrimary)
        {
            Assert(mbVGASurfCreated);
            mDisplay.getVGA()->getComplexList()->add(surf);
            if(pCmd->SurfInfo.surfCaps & VBOXVHWA_SCAPS_VISIBLE)
            {
                Assert(surf->getComplexList() == mDisplay.getVGA()->getComplexList());
                surf->getComplexList()->setCurrentVisible(surf);
            }
        }
        else if(pCmd->SurfInfo.surfCaps & VBOXVHWA_SCAPS_COMPLEX)
        {
            //TODO: impl fullscreen mode support
            Assert(0);
        }
        else
        {
            Assert(0);
        }
    }

    /* tell the guest how we think the memory is organized */
    VBOXQGLLOG(("bps: %d\n", surf->bitsPerPixel()));

    pCmd->SurfInfo.pitch        = surf->bitsPerPixel() * surf->width() / 8;
    pCmd->SurfInfo.sizeX = surf->memSize();
    pCmd->SurfInfo.sizeY = 1;

    if(handle != VBOXVHWA_SURFHANDLE_INVALID)
    {
        bool bSuccess = mSurfHandleTable.mapPut(handle, surf);
        Assert(bSuccess);
        if(!bSuccess)
        {
            /* @todo: this is very bad, should not be here */
            return VERR_GENERAL_FAILURE;
        }
    }
    else
    {
        /* tell the guest our handle */
        handle = mSurfHandleTable.put(surf);
        pCmd->SurfInfo.hSurf = (VBOXVHWA_SURFHANDLE)handle;
    }

    surf->setHandle(handle);

    VBOXQGLLOG_EXIT(("pSurf (0x%x)\n",surf));

    return VINF_SUCCESS;
}

int VBoxGLWidget::vhwaSurfaceDestroy(struct _VBOXVHWACMD_SURF_DESTROY *pCmd)
{
    VBoxVHWASurfaceBase *pSurf = handle2Surface(pCmd->u.in.hSurf);
    VBOXQGLLOG_ENTER(("pSurf (0x%x)\n",pSurf));
    if(pSurf != mDisplay.getVGA())
    {
        VBoxVHWASurfList *pList = pSurf->getComplexList();
        if(pList)
        {
            pList->remove(pSurf);
            if(pList->surfaces().empty())
            {
                mDisplay.removeOverlay(pList);
//                Assert(mConstructingList != pList);
                if(pList == mConstructingList)
                {
                    mConstructingList = NULL;
                    mcRemaining2Contruct = 0;
                }
                delete pList;
            }
            else if(pList == mDisplay.getVGA()->getComplexList())
            {
                if(pList->current() == NULL)
                {
                    pList->setCurrentVisible(mDisplay.getVGA());
                }
            }
        }

        delete(pSurf);
    }
    else
    {
        Assert(mbVGASurfCreated);
        mbVGASurfCreated = false;
    }

    void * test = mSurfHandleTable.remove(pCmd->u.in.hSurf);
    Assert(test);

    return VINF_SUCCESS;
}

#define VBOXVHWA_CONSTRUCT_QRECT_FROM_RECTL_RB(_pr) \
             QRect((_pr)->left,                     \
                 (_pr)->top,                        \
                 (_pr)->right - (_pr)->left + 1,    \
                 (_pr)->bottom - (_pr)->top + 1)

#define VBOXVHWA_CONSTRUCT_QRECT_FROM_RECTL_WH(_pr) \
             QRect((_pr)->left,                     \
                 (_pr)->top,                        \
                 (_pr)->right - (_pr)->left,        \
                 (_pr)->bottom - (_pr)->top)

int VBoxGLWidget::vhwaSurfaceLock(struct _VBOXVHWACMD_SURF_LOCK *pCmd)
{
    VBoxVHWASurfaceBase *pSurf = handle2Surface(pCmd->u.in.hSurf);
    VBOXQGLLOG_ENTER(("pSurf (0x%x)\n",pSurf));
    vboxCheckUpdateAddress (pSurf, pCmd->u.in.offSurface);
    if(pCmd->u.in.rectValid)
    {
        QRect r = VBOXVHWA_CONSTRUCT_QRECT_FROM_RECTL_WH(&pCmd->u.in.rect);
        return pSurf->lock(&r, pCmd->u.in.flags);
    }
    return pSurf->lock(NULL, pCmd->u.in.flags);
}

int VBoxGLWidget::vhwaSurfaceUnlock(struct _VBOXVHWACMD_SURF_UNLOCK *pCmd)
{
    VBoxVHWASurfaceBase *pSurf = handle2Surface(pCmd->u.in.hSurf);
    VBOXQGLLOG_ENTER(("pSurf (0x%x)\n",pSurf));
    if(pCmd->u.in.xUpdatedMemValid)
    {
        QRect r = VBOXVHWA_CONSTRUCT_QRECT_FROM_RECTL_WH(&pCmd->u.in.xUpdatedMemRect);
        pSurf->updatedMem(&r);
    }
    return pSurf->unlock();
}

int VBoxGLWidget::vhwaSurfaceBlt(struct _VBOXVHWACMD_SURF_BLT *pCmd)
{
    /**/
    Q_UNUSED(pCmd);
    return VERR_NOT_IMPLEMENTED;
//    VBoxVHWASurfaceBase *pDstSurf = (VBoxVHWASurfaceBase*)pCmd->u.in.hDstSurf;
//    VBoxVHWASurfaceBase *pSrcSurf = (VBoxVHWASurfaceBase*)pCmd->u.in.hSrcSurf;
//    VBOXQGLLOG_ENTER(("pDstSurf (0x%x), pSrcSurf (0x%x)\n",pDstSurf,pSrcSurf));
//    QRect dstRect = VBOXVHWA_CONSTRUCT_QRECT_FROM_RECTL(&pCmd->u.in.dstRect);
//    QRect srcRect = VBOXVHWA_CONSTRUCT_QRECT_FROM_RECTL(&pCmd->u.in.srcRect);
//    vboxCheckUpdateAddress (pSrcSurf, pCmd->u.in.offSrcSurface);
//    vboxCheckUpdateAddress (pDstSurf, pCmd->u.in.offDstSurface);
////    pDstSurf->makeCurrent();
////    const VBoxVHWAColorKey DstCKey, *pDstCKey = NULL;
////    const VBoxVHWAColorKey SrcCKey, *pSrcCKey = NULL;
////    if(pCmd->u.in.flags & VBOXVHWA_BLT_KEYSRCOVERRIDE)
////    {
////        pSrcCKey = &
////    }
////    if(pCmd->u.in.flags & VBOXVHWA_BLT_KEYDESTOVERRIDE)
////    {
////
////    }
//    if(pCmd->u.in.xUpdatedSrcMemValid)
//    {
//        QRect r = VBOXVHWA_CONSTRUCT_QRECT_FROM_RECTL(&pCmd->u.in.xUpdatedSrcMemRect);
//        pSrcSurf->updatedMem(&r);
//    }
//
//    return pDstSurf->blt(&dstRect, pSrcSurf, &srcRect,
//            pCmd->u.in.flags & VBOXVHWA_BLT_KEYSRCOVERRIDE ? &VBoxVHWAColorKey(pCmd->u.in.desc.SrcCK.high, pCmd->u.in.desc.SrcCK.low) : NULL,
//            pCmd->u.in.flags & VBOXVHWA_BLT_KEYDESTOVERRIDE ? &VBoxVHWAColorKey(pCmd->u.in.desc.DstCK.high, pCmd->u.in.desc.DstCK.low) : NULL);
}

int VBoxGLWidget::vhwaSurfaceFlip(struct _VBOXVHWACMD_SURF_FLIP *pCmd)
{
    VBoxVHWASurfaceBase *pTargSurf = handle2Surface(pCmd->u.in.hTargSurf);
    VBoxVHWASurfaceBase *pCurrSurf = handle2Surface(pCmd->u.in.hCurrSurf);
    VBOXQGLLOG_ENTER(("pTargSurf (0x%x), pCurrSurf (0x%x)\n",pTargSurf,pCurrSurf));
    vboxCheckUpdateAddress (pCurrSurf, pCmd->u.in.offCurrSurface);
    vboxCheckUpdateAddress (pTargSurf, pCmd->u.in.offTargSurface);

//    Assert(pTargSurf->isYInverted());
//    Assert(!pCurrSurf->isYInverted());
    if(pCmd->u.in.xUpdatedTargMemValid)
    {
        QRect r = VBOXVHWA_CONSTRUCT_QRECT_FROM_RECTL_WH(&pCmd->u.in.xUpdatedTargMemRect);
        pTargSurf->updatedMem(&r);
    }
    pTargSurf->getComplexList()->setCurrentVisible(pTargSurf);

#ifdef DEBUG
    pCurrSurf->cFlipsCurr++;
    pTargSurf->cFlipsTarg++;
#endif
//    mDisplay.flip(pTargSurf, pCurrSurf);
//    Assert(!pTargSurf->isYInverted());
//    Assert(pCurrSurf->isYInverted());
    return VINF_SUCCESS;
}

void VBoxGLWidget::vhwaDoSurfaceOverlayUpdate(VBoxVHWASurfaceBase *pDstSurf, VBoxVHWASurfaceBase *pSrcSurf, struct _VBOXVHWACMD_SURF_OVERLAY_UPDATE *pCmd)
{
    if(pCmd->u.in.flags & VBOXVHWA_OVER_KEYDEST)
    {
        VBOXQGLLOG((", KEYDEST"));
        /* we use src (overlay) surface to maintain overridden dst ckey info
         * to allow multiple overlays have different overridden dst keys for one primary surface */
        /* non-null dstOverlayCKey for overlay would mean the overlay surface contains the overridden
         * dst ckey value in defaultDstOverlayCKey
         * this allows the NULL to be a valid overridden value as well
         *  i.e.
         * 1. indicate the value is NUL overridden, just set NULL*/
        pSrcSurf->setOverriddenDstOverlayCKey(NULL);
    }
    else if(pCmd->u.in.flags & VBOXVHWA_OVER_KEYDESTOVERRIDE)
    {
        VBOXQGLLOG((", KEYDESTOVERRIDE"));
        /* we use src (overlay) surface to maintain overridden dst ckey info
         * to allow multiple overlays have different overridden dst keys for one primary surface */
        /* non-null dstOverlayCKey for overlay would mean the overlay surface contains the overridden
         * dst ckey value in defaultDstOverlayCKey
         * this allows the NULL to be a valid overridden value as well
         *  i.e.
         * 1. indicate the value is overridden (no matter what we write here, bu it should be not NULL)*/
        VBoxVHWAColorKey ckey(pCmd->u.in.desc.DstCK.high, pCmd->u.in.desc.DstCK.low);
        VBOXQGLLOG_CKEY(" ckey: ",&ckey, "\n");
        pSrcSurf->setOverriddenDstOverlayCKey(&ckey);
        /* tell the ckey is enabled */
        pSrcSurf->setDefaultDstOverlayCKey(&ckey);
    }
    else
    {
        VBOXQGLLOG((", no KEYDEST"));
        /* we use src (overlay) surface to maintain overridden dst ckey info
         * to allow multiple overlays have different overridden dst keys for one primary surface */
        /* non-null dstOverlayCKey for overlay would mean the overlay surface contains the overridden
         * dst ckey value in defaultDstOverlayCKey
         * this allows the NULL to be a valid overridden value as well
         * i.e.
         * 1. indicate the value is overridden (no matter what we write here, bu it should be not NULL)*/
        VBoxVHWAColorKey dummyCKey(0, 0);
        pSrcSurf->setOverriddenDstOverlayCKey(&dummyCKey);
        /* tell the ckey is disabled */
        pSrcSurf->setDefaultDstOverlayCKey(NULL);
    }

    if(pCmd->u.in.flags & VBOXVHWA_OVER_KEYSRC)
    {
        VBOXQGLLOG((", KEYSRC"));
        pSrcSurf->resetDefaultSrcOverlayCKey();
    }
    else if(pCmd->u.in.flags & VBOXVHWA_OVER_KEYSRCOVERRIDE)
    {
        VBOXQGLLOG((", KEYSRCOVERRIDE"));
        VBoxVHWAColorKey ckey(pCmd->u.in.desc.SrcCK.high, pCmd->u.in.desc.SrcCK.low);
        pSrcSurf->setOverriddenSrcOverlayCKey(&ckey);
    }
    else
    {
        VBOXQGLLOG((", no KEYSRC"));
        pSrcSurf->setOverriddenSrcOverlayCKey(NULL);
    }
    VBOXQGLLOG(("\n"));
    if(pDstSurf)
    {
        QRect dstRect = VBOXVHWA_CONSTRUCT_QRECT_FROM_RECTL_WH(&pCmd->u.in.dstRect);
        QRect srcRect = VBOXVHWA_CONSTRUCT_QRECT_FROM_RECTL_WH(&pCmd->u.in.srcRect);

        VBOXQGLLOG(("*******overlay update*******\n"));
        VBOXQGLLOG(("dstSurfSize: w(%d), h(%d)\n", pDstSurf->width(), pDstSurf->height()));
        VBOXQGLLOG(("srcSurfSize: w(%d), h(%d)\n", pSrcSurf->width(), pSrcSurf->height()));
        VBOXQGLLOG_QRECT("dstRect:", &dstRect, "\n");
        VBOXQGLLOG_QRECT("srcRect:", &srcRect, "\n");

        const VBoxVHWAColorKey *pResSrcCKey = pSrcSurf->getActiveSrcOverlayCKey();
        const VBoxVHWAColorKey *pResDstCKey = pSrcSurf->getActiveDstOverlayCKey(pDstSurf);

        VBoxVHWAGlProgramVHWA *pProgram = vboxVHWAGetGlProgramMngr()->getProgram(pResDstCKey != NULL, pResSrcCKey != NULL, &pSrcSurf->colorFormat(), &pDstSurf->colorFormat());

        if(pProgram)
        {
            pProgram->start();
            if(pResSrcCKey || pResDstCKey)
            {
                if(pResSrcCKey)
                {
                    VBoxVHWASurfaceBase::setCKey(pProgram, &pSrcSurf->colorFormat(), pResSrcCKey, false);
                }
                if(pResDstCKey)
                {
                    VBoxVHWASurfaceBase::setCKey(pProgram, &pDstSurf->colorFormat(), pResDstCKey, true);
                }

            }

            switch(pSrcSurf->fourcc())
            {
                case 0:
                case FOURCC_AYUV:
                case FOURCC_YV12:
                    break;
                case FOURCC_UYVY:
                case FOURCC_YUY2:
                    break;
                default:
                    Assert(0);
                    break;
            }

            pProgram->stop();
        }

        pSrcSurf->setRects(pDstSurf, &dstRect, &srcRect);
    }
}

int VBoxGLWidget::vhwaSurfaceOverlayUpdate(struct _VBOXVHWACMD_SURF_OVERLAY_UPDATE *pCmd)
{
    VBoxVHWASurfaceBase *pSrcSurf = handle2Surface(pCmd->u.in.hSrcSurf);
    VBoxVHWASurfList *pList = pSrcSurf->getComplexList();
    vboxCheckUpdateAddress (pSrcSurf, pCmd->u.in.offSrcSurface);
    VBOXQGLLOG(("OverlayUpdate: pSrcSurf (0x%x)\n",pSrcSurf));
    VBoxVHWASurfaceBase *pDstSurf = NULL;

    if(pCmd->u.in.hDstSurf)
    {
        pDstSurf = handle2Surface(pCmd->u.in.hDstSurf);
        vboxCheckUpdateAddress (pDstSurf, pCmd->u.in.offDstSurface);
        VBOXQGLLOG(("pDstSurf (0x%x)\n",pDstSurf));
    }

    const SurfList & surfaces = pList->surfaces();

    for (SurfList::const_iterator it = surfaces.begin();
             it != surfaces.end(); ++ it)
    {
        VBoxVHWASurfaceBase *pCurSrcSurf = (*it);
        vhwaDoSurfaceOverlayUpdate(pDstSurf, pCurSrcSurf, pCmd);
    }

    if(pCmd->u.in.flags & VBOXVHWA_OVER_HIDE)
    {
        VBOXQGLLOG(("hide"));
        pList->setCurrentVisible(NULL);
    }
    else if(pCmd->u.in.flags & VBOXVHWA_OVER_SHOW)
    {
        VBOXQGLLOG(("show"));
        pList->setCurrentVisible(pSrcSurf);
    }

    return VINF_SUCCESS;
}

int VBoxGLWidget::vhwaSurfaceOverlaySetPosition(struct _VBOXVHWACMD_SURF_OVERLAY_SETPOSITION *pCmd)
{
    VBoxVHWASurfaceBase *pDstSurf = handle2Surface(pCmd->u.in.hDstSurf);
    VBoxVHWASurfaceBase *pSrcSurf = handle2Surface(pCmd->u.in.hSrcSurf);

    VBOXQGLLOG_ENTER(("pDstSurf (0x%x), pSrcSurf (0x%x)\n",pDstSurf,pSrcSurf));

    vboxCheckUpdateAddress (pSrcSurf, pCmd->u.in.offSrcSurface);
    vboxCheckUpdateAddress (pDstSurf, pCmd->u.in.offDstSurface);

    VBoxVHWASurfList *pList = pSrcSurf->getComplexList();
    const SurfList & surfaces = pList->surfaces();

    QPoint pos(pCmd->u.in.xPos, pCmd->u.in.yPos);

    for (SurfList::const_iterator it = surfaces.begin();
             it != surfaces.end(); ++ it)
    {
        VBoxVHWASurfaceBase *pCurSrcSurf = (*it);
        pCurSrcSurf->setTargetRectPosition(pDstSurf, &pos);
    }

    return VINF_SUCCESS;
}

int VBoxGLWidget::vhwaSurfaceColorkeySet(struct _VBOXVHWACMD_SURF_COLORKEY_SET *pCmd)
{
    VBoxVHWASurfaceBase *pSurf = handle2Surface(pCmd->u.in.hSurf);

    VBOXQGLLOG_ENTER(("pSurf (0x%x)\n",pSurf));

    vboxCheckUpdateAddress (pSurf, pCmd->u.in.offSurface);

//    VBOXVHWA_CKEY_COLORSPACE
    if(pCmd->u.in.flags & VBOXVHWA_CKEY_DESTBLT)
    {
        VBoxVHWAColorKey ckey(pCmd->u.in.CKey.high, pCmd->u.in.CKey.low);
        pSurf->setDstBltCKey(&ckey);
    }
    if(pCmd->u.in.flags & VBOXVHWA_CKEY_DESTOVERLAY)
    {
        VBoxVHWAColorKey ckey(pCmd->u.in.CKey.high, pCmd->u.in.CKey.low);
        pSurf->setDefaultDstOverlayCKey(&ckey);
    }
    if(pCmd->u.in.flags & VBOXVHWA_CKEY_SRCBLT)
    {
        VBoxVHWAColorKey ckey(pCmd->u.in.CKey.high, pCmd->u.in.CKey.low);
        pSurf->setSrcBltCKey(&ckey);
    }
    if(pCmd->u.in.flags & VBOXVHWA_CKEY_SRCOVERLAY)
    {
        VBoxVHWAColorKey ckey(pCmd->u.in.CKey.high, pCmd->u.in.CKey.low);
        pSurf->setDefaultSrcOverlayCKey(&ckey);
    }

    return VINF_SUCCESS;
}

int VBoxGLWidget::vhwaQueryInfo1(struct _VBOXVHWACMD_QUERYINFO1 *pCmd)
{
    VBOXQGLLOG_ENTER(("\n"));
    bool bEnabled = false;
    if(vboxVHWASupportedInternal())
    {
        Assert(pCmd->u.in.guestVersion.maj == VBOXVHWA_VERSION_MAJ);
        if(pCmd->u.in.guestVersion.maj == VBOXVHWA_VERSION_MAJ)
        {
            Assert(pCmd->u.in.guestVersion.min == VBOXVHWA_VERSION_MIN);
            if(pCmd->u.in.guestVersion.min == VBOXVHWA_VERSION_MIN)
            {
                Assert(pCmd->u.in.guestVersion.bld == VBOXVHWA_VERSION_BLD);
                if(pCmd->u.in.guestVersion.bld == VBOXVHWA_VERSION_BLD)
                {
                    Assert(pCmd->u.in.guestVersion.reserved == VBOXVHWA_VERSION_RSV);
                    if(pCmd->u.in.guestVersion.reserved == VBOXVHWA_VERSION_RSV)
                    {
                        bEnabled = true;
                    }
                }
            }
        }
    }

    memset(pCmd, 0, sizeof(VBOXVHWACMD_QUERYINFO1));
    if(bEnabled)
    {
        pCmd->u.out.cfgFlags = VBOXVHWA_CFG_ENABLED;

        pCmd->u.out.caps =
//                        VBOXVHWA_CAPS_BLT | VBOXVHWA_CAPS_BLTSTRETCH | VBOXVHWA_CAPS_BLTQUEUE
//                                 // | VBOXVHWA_CAPS_BLTCOLORFILL not supported, although adding it is trivial
//                                 // | VBOXVHWA_CAPS_BLTFOURCC set below if shader support is available
                                 VBOXVHWA_CAPS_OVERLAY
                                 | VBOXVHWA_CAPS_OVERLAYSTRETCH
                                 | VBOXVHWA_CAPS_OVERLAYCANTCLIP
                                 // | VBOXVHWA_CAPS_OVERLAYFOURCC set below if shader support is available
                                 ;

        pCmd->u.out.caps2 = VBOXVHWA_CAPS2_CANRENDERWINDOWED
                                    | VBOXVHWA_CAPS2_WIDESURFACES;

        //TODO: setup stretchCaps
        pCmd->u.out.stretchCaps = 0;

        pCmd->u.out.numOverlays = 1;
        /* TODO: set curOverlays properly */
        pCmd->u.out.curOverlays = 0;

        pCmd->u.out.surfaceCaps =
                            VBOXVHWA_SCAPS_PRIMARYSURFACE
                    //        | VBOXVHWA_SCAPS_OFFSCREENPLAIN
                            | VBOXVHWA_SCAPS_FLIP
                            | VBOXVHWA_SCAPS_LOCALVIDMEM
                            | VBOXVHWA_SCAPS_OVERLAY
                    //        | VBOXVHWA_SCAPS_VIDEOMEMORY
                    //        | VBOXVHWA_SCAPS_COMPLEX
                    //        | VBOXVHWA_SCAPS_VISIBLE
                            ;

        if(g_vboxVHWAGlShaderSupported && g_vboxVHWAGlMultiTexNumSupported >= 2)
        {
            pCmd->u.out.caps |= VBOXVHWA_CAPS_COLORKEY
                            | VBOXVHWA_CAPS_COLORKEYHWASSIST
                            ;

            pCmd->u.out.colorKeyCaps =
//                          VBOXVHWA_CKEYCAPS_DESTBLT | VBOXVHWA_CKEYCAPS_DESTBLTCLRSPACE | VBOXVHWA_CKEYCAPS_SRCBLT| VBOXVHWA_CKEYCAPS_SRCBLTCLRSPACE |
//                          VBOXVHWA_CKEYCAPS_SRCOVERLAY | VBOXVHWA_CKEYCAPS_SRCOVERLAYONEACTIVE |
                            VBOXVHWA_CKEYCAPS_DESTOVERLAY          |
                            VBOXVHWA_CKEYCAPS_DESTOVERLAYONEACTIVE;
                            ;

            if(g_vboxVHWAGlTextureRectangleSupported)
            {
                pCmd->u.out.caps |= VBOXVHWA_CAPS_OVERLAYFOURCC
//                              | VBOXVHWA_CAPS_BLTFOURCC
                                ;

                pCmd->u.out.colorKeyCaps |=
//                               VBOXVHWA_CKEYCAPS_SRCOVERLAYYUV |
                                 VBOXVHWA_CKEYCAPS_DESTOVERLAYYUV;
                                 ;

//              pCmd->u.out.caps2 |= VBOXVHWA_CAPS2_COPYFOURCC;

                pCmd->u.out.numFourCC = g_vboxVHWAFourccSupportedCount;
            }
        }
    }

    return VINF_SUCCESS;
}

int VBoxGLWidget::vhwaQueryInfo2(struct _VBOXVHWACMD_QUERYINFO2 *pCmd)
{
    VBOXQGLLOG_ENTER(("\n"));

    Assert(pCmd->numFourCC >= g_vboxVHWAFourccSupportedCount);
    if(pCmd->numFourCC < g_vboxVHWAFourccSupportedCount)
        return VERR_GENERAL_FAILURE;

    pCmd->numFourCC = g_vboxVHWAFourccSupportedCount;
    for(uint32_t i = 0; i < g_vboxVHWAFourccSupportedCount; i++)
    {
        pCmd->FourCC[i] = g_vboxVHWAFourccSupportedList[i];
    }
    return VINF_SUCCESS;
}

static DECLCALLBACK(void) vboxQGLSaveExec(PSSMHANDLE pSSM, void *pvUser)
{
    VBoxGLWidget * pw = (VBoxGLWidget*)pvUser;
    pw->vhwaSaveExec(pSSM);
}

static DECLCALLBACK(int) vboxQGLLoadExec(PSSMHANDLE pSSM, void *pvUser, uint32_t u32Version, uint32_t uPhase)
{
    Q_UNUSED(uPhase);
    VBoxGLWidget * pw = (VBoxGLWidget*)pvUser;
    return pw->vhwaLoadExec(pSSM, u32Version);
}

int VBoxGLWidget::vhwaSaveSurface(struct SSMHANDLE * pSSM, VBoxVHWASurfaceBase *pSurf, uint32_t surfCaps)
{
    VBOXQGL_SAVE_SURFSTART(pSSM);

    uint64_t u64 = vboxVRAMOffset(pSurf);
    int rc;
    rc = SSMR3PutU32(pSSM, pSurf->handle());         AssertRC(rc);
    rc = SSMR3PutU64(pSSM, u64);         AssertRC(rc);
    rc = SSMR3PutU32(pSSM, pSurf->width());         AssertRC(rc);
    rc = SSMR3PutU32(pSSM, pSurf->height());         AssertRC(rc);
    rc = SSMR3PutU32(pSSM, surfCaps);         AssertRC(rc);
    uint32_t flags = 0;
    const VBoxVHWAColorKey * pDstBltCKey = pSurf->dstBltCKey();
    const VBoxVHWAColorKey * pSrcBltCKey = pSurf->srcBltCKey();
    const VBoxVHWAColorKey * pDstOverlayCKey = pSurf->dstOverlayCKey();
    const VBoxVHWAColorKey * pSrcOverlayCKey = pSurf->srcOverlayCKey();
    if(pDstBltCKey)
    {
        flags |= VBOXVHWA_SD_CKDESTBLT;
    }
    if(pSrcBltCKey)
    {
        flags |= VBOXVHWA_SD_CKSRCBLT;
    }
    if(pDstOverlayCKey)
    {
        flags |= VBOXVHWA_SD_CKDESTOVERLAY;
    }
    if(pSrcOverlayCKey)
    {
        flags |= VBOXVHWA_SD_CKSRCOVERLAY;
    }

    rc = SSMR3PutU32(pSSM, flags);         AssertRC(rc);
    if(pDstBltCKey)
    {
        rc = SSMR3PutU32(pSSM, pDstBltCKey->lower());         AssertRC(rc);
        rc = SSMR3PutU32(pSSM, pDstBltCKey->upper());         AssertRC(rc);
    }
    if(pSrcBltCKey)
    {
        rc = SSMR3PutU32(pSSM, pSrcBltCKey->lower());         AssertRC(rc);
        rc = SSMR3PutU32(pSSM, pSrcBltCKey->upper());         AssertRC(rc);
    }
    if(pDstOverlayCKey)
    {
        rc = SSMR3PutU32(pSSM, pDstOverlayCKey->lower());         AssertRC(rc);
        rc = SSMR3PutU32(pSSM, pDstOverlayCKey->upper());         AssertRC(rc);
    }
    if(pSrcOverlayCKey)
    {
        rc = SSMR3PutU32(pSSM, pSrcOverlayCKey->lower());         AssertRC(rc);
        rc = SSMR3PutU32(pSSM, pSrcOverlayCKey->upper());         AssertRC(rc);
    }

    const VBoxVHWAColorFormat & format = pSurf->colorFormat();
    flags = 0;
    if(format.fourcc())
    {
        flags |= VBOXVHWA_PF_FOURCC;
        rc = SSMR3PutU32(pSSM, flags);         AssertRC(rc);
        rc = SSMR3PutU32(pSSM, format.fourcc());         AssertRC(rc);
    }
    else
    {
        flags |= VBOXVHWA_PF_RGB;
        rc = SSMR3PutU32(pSSM, flags);         AssertRC(rc);
        rc = SSMR3PutU32(pSSM, format.bitsPerPixel());         AssertRC(rc);
        rc = SSMR3PutU32(pSSM, format.r().mask());         AssertRC(rc);
        rc = SSMR3PutU32(pSSM, format.g().mask());         AssertRC(rc);
        rc = SSMR3PutU32(pSSM, format.b().mask());         AssertRC(rc);
        rc = SSMR3PutU32(pSSM, format.a().mask());         AssertRC(rc);
    }

    VBOXQGL_SAVE_SURFSTOP(pSSM);

    return rc;
}

int VBoxGLWidget::vhwaLoadSurface(struct SSMHANDLE * pSSM, uint32_t u32Version)
{
    Q_UNUSED(u32Version);

    VBOXQGL_LOAD_SURFSTART(pSSM);

    char *buf = (char*)malloc(VBOXVHWACMD_SIZE(VBOXVHWACMD_SURF_CREATE));
    memset(buf, 0, sizeof(buf));
    VBOXVHWACMD * pCmd = (VBOXVHWACMD*)buf;
    pCmd->enmCmd = VBOXVHWACMD_TYPE_SURF_CREATE;
    pCmd->Flags = VBOXVHWACMD_FLAG_HH_CMD;

    VBOXVHWACMD_SURF_CREATE * pCreateSurf = VBOXVHWACMD_BODY(pCmd, VBOXVHWACMD_SURF_CREATE);
    int rc;
    uint32_t u32;
    rc = SSMR3GetU32(pSSM, &u32);         AssertRC(rc);
    pCreateSurf->SurfInfo.hSurf = (VBOXVHWA_SURFHANDLE)u32;
    if(RT_SUCCESS(rc))
    {
        rc = SSMR3GetU64(pSSM, &pCreateSurf->SurfInfo.offSurface);         AssertRC(rc);
        rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.width);         AssertRC(rc);
        rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.height);         AssertRC(rc);
        rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.surfCaps);         AssertRC(rc);
        rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.flags);         AssertRC(rc);
        if(pCreateSurf->SurfInfo.flags & VBOXVHWA_SD_CKDESTBLT)
        {
            rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.DstBltCK.low);         AssertRC(rc);
            rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.DstBltCK.high);         AssertRC(rc);
        }
        if(pCreateSurf->SurfInfo.flags & VBOXVHWA_SD_CKSRCBLT)
        {
            rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.SrcBltCK.low);         AssertRC(rc);
            rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.SrcBltCK.high);         AssertRC(rc);
        }
        if(pCreateSurf->SurfInfo.flags & VBOXVHWA_SD_CKDESTOVERLAY)
        {
            rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.DstOverlayCK.low);         AssertRC(rc);
            rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.DstOverlayCK.high);         AssertRC(rc);
        }
        if(pCreateSurf->SurfInfo.flags & VBOXVHWA_SD_CKSRCOVERLAY)
        {
            rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.SrcOverlayCK.low);         AssertRC(rc);
            rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.SrcOverlayCK.high);         AssertRC(rc);
        }

        rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.PixelFormat.flags);         AssertRC(rc);
        if(pCreateSurf->SurfInfo.PixelFormat.flags & VBOXVHWA_PF_RGB)
        {
            rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.PixelFormat.c.rgbBitCount);         AssertRC(rc);
            rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.PixelFormat.m1.rgbRBitMask);         AssertRC(rc);
            rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.PixelFormat.m2.rgbGBitMask);         AssertRC(rc);
            rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.PixelFormat.m3.rgbBBitMask);         AssertRC(rc);
            rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.PixelFormat.m4.rgbABitMask);         AssertRC(rc);
        }
        else if(pCreateSurf->SurfInfo.PixelFormat.flags & VBOXVHWA_PF_FOURCC)
        {
            rc = SSMR3GetU32(pSSM, &pCreateSurf->SurfInfo.PixelFormat.fourCC);         AssertRC(rc);
        }
        else
        {
            Assert(0);
        }

        vboxExecOnResize(&VBoxGLWidget::vboxDoVHWACmdAndFree, pCmd); AssertRC(rc);
//        if(RT_SUCCESS(rc))
//        {
//            rc = pCmd->rc;
//            AssertRC(rc);
//        }
    }

    VBOXQGL_LOAD_SURFSTOP(pSSM);

    return rc;
}

int VBoxGLWidget::vhwaSaveOverlayData(struct SSMHANDLE * pSSM, VBoxVHWASurfaceBase *pSurf, bool bVisible)
{
    VBOXQGL_SAVE_OVERLAYSTART(pSSM);

    uint32_t flags = 0;
    const VBoxVHWAColorKey * dstCKey = pSurf->dstOverlayCKey();
    const VBoxVHWAColorKey * defaultDstCKey = pSurf->defaultDstOverlayCKey();
    const VBoxVHWAColorKey * srcCKey = pSurf->srcOverlayCKey();;
    const VBoxVHWAColorKey * defaultSrcCKey = pSurf->defaultSrcOverlayCKey();
    bool bSaveDstCKey = false;
    bool bSaveSrcCKey = false;

    if(bVisible)
    {
        flags |= VBOXVHWA_OVER_SHOW;
    }
    else
    {
        flags |= VBOXVHWA_OVER_HIDE;
    }

    if(!dstCKey)
    {
        flags |= VBOXVHWA_OVER_KEYDEST;
    }
    else if(defaultDstCKey)
    {
        flags |= VBOXVHWA_OVER_KEYDESTOVERRIDE;
        bSaveDstCKey = true;
    }

    if(srcCKey == defaultSrcCKey)
    {
        flags |= VBOXVHWA_OVER_KEYSRC;
    }
    else if(srcCKey)
    {
        flags |= VBOXVHWA_OVER_KEYSRCOVERRIDE;
        bSaveSrcCKey = true;
    }

    int rc = SSMR3PutU32(pSSM, flags); AssertRC(rc);

    rc = SSMR3PutU32(pSSM, mDisplay.getVGA()->handle()); AssertRC(rc);
    rc = SSMR3PutU32(pSSM, pSurf->handle()); AssertRC(rc);

    if(bSaveDstCKey)
    {
        rc = SSMR3PutU32(pSSM, dstCKey->lower()); AssertRC(rc);
        rc = SSMR3PutU32(pSSM, dstCKey->upper()); AssertRC(rc);
    }
    if(bSaveSrcCKey)
    {
        rc = SSMR3PutU32(pSSM, srcCKey->lower()); AssertRC(rc);
        rc = SSMR3PutU32(pSSM, srcCKey->upper()); AssertRC(rc);
    }

    int x1, x2, y1, y2;
    pSurf->targRect().getCoords(&x1, &y1, &x2, &y2);
    rc = SSMR3PutS32(pSSM, x1); AssertRC(rc);
    rc = SSMR3PutS32(pSSM, x2+1); AssertRC(rc);
    rc = SSMR3PutS32(pSSM, y1); AssertRC(rc);
    rc = SSMR3PutS32(pSSM, y2+1); AssertRC(rc);

    pSurf->srcRect().getCoords(&x1, &y1, &x2, &y2);
    rc = SSMR3PutS32(pSSM, x1); AssertRC(rc);
    rc = SSMR3PutS32(pSSM, x2+1); AssertRC(rc);
    rc = SSMR3PutS32(pSSM, y1); AssertRC(rc);
    rc = SSMR3PutS32(pSSM, y2+1); AssertRC(rc);

    VBOXQGL_SAVE_OVERLAYSTOP(pSSM);

    return rc;
}

int VBoxGLWidget::vhwaLoadOverlayData(struct SSMHANDLE * pSSM, uint32_t u32Version)
{
    Q_UNUSED(u32Version);
    
    VBOXQGL_LOAD_OVERLAYSTART(pSSM);

//    char buf[VBOXVHWACMD_SIZE(VBOXVHWACMD_SURF_OVERLAY_UPDATE)];
    char *buf = new char[VBOXVHWACMD_SIZE(VBOXVHWACMD_SURF_CREATE)];
    memset(buf, 0, sizeof(buf));
    VBOXVHWACMD * pCmd = (VBOXVHWACMD*)buf;
    pCmd->enmCmd = VBOXVHWACMD_TYPE_SURF_OVERLAY_UPDATE;
    pCmd->Flags = VBOXVHWACMD_FLAG_HH_CMD;

    VBOXVHWACMD_SURF_OVERLAY_UPDATE * pUpdateOverlay = VBOXVHWACMD_BODY(pCmd, VBOXVHWACMD_SURF_OVERLAY_UPDATE);
    int rc;

    rc = SSMR3GetU32(pSSM, &pUpdateOverlay->u.in.flags); AssertRC(rc);
    uint32_t hSrc, hDst;
    rc = SSMR3GetU32(pSSM, &hDst); AssertRC(rc);
    rc = SSMR3GetU32(pSSM, &hSrc); AssertRC(rc);
    pUpdateOverlay->u.in.hSrcSurf = hSrc;
    pUpdateOverlay->u.in.hDstSurf = hDst;
//    Assert(hDst == mDisplay.getVGA()->handle());
//    VBoxVHWASurfaceBase *pDstSurf = handle2Surface(hDst);
//    VBoxVHWASurfaceBase *pSrcSurf = handle2Surface(hSrc);
//    Assert(pSrcSurf);
//    Assert(pDstSurf);
//    if(pSrcSurf && pDstSurf)
    {
        pUpdateOverlay->u.in.offDstSurface = VBOXVHWA_OFFSET64_VOID;
//        vboxVRAMOffset(pDstSurf);
        pUpdateOverlay->u.in.offSrcSurface = VBOXVHWA_OFFSET64_VOID;
//        vboxVRAMOffset(pSrcSurf);

        if(pUpdateOverlay->u.in.flags & VBOXVHWA_OVER_KEYDESTOVERRIDE)
        {
            rc = SSMR3GetU32(pSSM, &pUpdateOverlay->u.in.desc.DstCK.low); AssertRC(rc);
            rc = SSMR3GetU32(pSSM, &pUpdateOverlay->u.in.desc.DstCK.high); AssertRC(rc);
        }

        if(pUpdateOverlay->u.in.flags & VBOXVHWA_OVER_KEYSRCOVERRIDE)
        {
            rc = SSMR3GetU32(pSSM, &pUpdateOverlay->u.in.desc.SrcCK.low); AssertRC(rc);
            rc = SSMR3GetU32(pSSM, &pUpdateOverlay->u.in.desc.SrcCK.high); AssertRC(rc);
        }

        rc = SSMR3GetS32(pSSM, &pUpdateOverlay->u.in.dstRect.left); AssertRC(rc);
        rc = SSMR3GetS32(pSSM, &pUpdateOverlay->u.in.dstRect.right); AssertRC(rc);
        rc = SSMR3GetS32(pSSM, &pUpdateOverlay->u.in.dstRect.top); AssertRC(rc);
        rc = SSMR3GetS32(pSSM, &pUpdateOverlay->u.in.dstRect.bottom); AssertRC(rc);

        rc = SSMR3GetS32(pSSM, &pUpdateOverlay->u.in.srcRect.left); AssertRC(rc);
        rc = SSMR3GetS32(pSSM, &pUpdateOverlay->u.in.srcRect.right); AssertRC(rc);
        rc = SSMR3GetS32(pSSM, &pUpdateOverlay->u.in.srcRect.top); AssertRC(rc);
        rc = SSMR3GetS32(pSSM, &pUpdateOverlay->u.in.srcRect.bottom); AssertRC(rc);

        vboxExecOnResize(&VBoxGLWidget::vboxDoVHWACmdAndFree, pCmd); AssertRC(rc);
//        if(RT_SUCCESS(rc))
//        {
//            rc = pCmd->rc;
//            AssertRC(rc);
//        }
    }
//    else
//    {
//        rc = VERR_GENERAL_FAILURE;
//    }

    VBOXQGL_LOAD_OVERLAYSTOP(pSSM);

    return rc;
}

void VBoxGLWidget::vhwaSaveExec(struct SSMHANDLE * pSSM)
{
    VBOXQGL_SAVE_START(pSSM);

    /* the mechanism of restoring data is based on generating VHWA commands that restore the surfaces state
     * the following commands are generated:
     * I.    CreateSurface
     * II.   UpdateOverlay
     *
     * Data format is the following:
     * I.    u32 - Num primary surfaces - (the current frontbuffer is detected in the stored surf flags which are posted to the generated CreateSurface cmd)
     * II.   for each primary surf
     * II.1    generate & execute CreateSurface cmd (see below on the generation logic)
     * III.  u32 - Num overlays
     * IV.   for each overlay
     * IV.1    u32 - Num surfaces in overlay (the current frontbuffer is detected in the stored surf flags which are posted to the generated CreateSurface cmd)
     * IV.2    for each surface in overlay
     * IV.2.a    generate & execute CreateSurface cmd (see below on the generation logic)
     * IV.2.b    generate & execute UpdateOverlay cmd (see below on the generation logic)
     *
     */
    const SurfList & primaryList = mDisplay.getVGA()->getComplexList()->surfaces();
    uint32_t cPrimary = (uint32_t)primaryList.size();
    Assert(cPrimary >= 1);
    if(!mbVGASurfCreated)
    {
        cPrimary -= 1;
    }
    int rc = SSMR3PutU32(pSSM, cPrimary);         AssertRC(rc);

    for (SurfList::const_iterator pr = primaryList.begin();
         pr != primaryList.end(); ++ pr)
    {
        VBoxVHWASurfaceBase *pSurf = *pr;
        bool bVga = (pSurf == mDisplay.getVGA());
        bool bVisible = (pSurf == mDisplay.getPrimary());
        uint32_t flags = VBOXVHWA_SCAPS_PRIMARYSURFACE;
        if(bVisible)
            flags |= VBOXVHWA_SCAPS_VISIBLE;

        if(mbVGASurfCreated || !bVga)
        {
            rc = vhwaSaveSurface(pSSM, *pr, flags);    AssertRC(rc);
#ifdef DEBUG
            --cPrimary;
            Assert(cPrimary < UINT32_MAX / 2);
#endif
        }
    }

#ifdef DEBUG
    Assert(!cPrimary);
#endif

    const OverlayList & overlays = mDisplay.overlays();
    rc = SSMR3PutU32(pSSM, (uint32_t)overlays.size());         AssertRC(rc);

    for (OverlayList::const_iterator it = overlays.begin();
         it != overlays.end(); ++ it)
    {
        VBoxVHWASurfList * pSurfList = *it;
        const SurfList & surfaces = pSurfList->surfaces();
        uint32_t cSurfs = (uint32_t)surfaces.size();
        uint32_t flags = VBOXVHWA_SCAPS_OVERLAY;
        if(cSurfs > 1)
            flags |= VBOXVHWA_SCAPS_COMPLEX;
        rc = SSMR3PutU32(pSSM, cSurfs);         AssertRC(rc);
        for (SurfList::const_iterator sit = surfaces.begin();
             sit != surfaces.end(); ++ sit)
        {
            rc = vhwaSaveSurface(pSSM, *sit, flags);    AssertRC(rc);
        }

        bool bVisible = true;
        VBoxVHWASurfaceBase * pOverlayData = pSurfList->current();
        if(!pOverlayData)
        {
            pOverlayData = surfaces.front();
            bVisible = false;
        }

        rc = vhwaSaveOverlayData(pSSM, pOverlayData, bVisible);    AssertRC(rc);
    }

    VBOXQGL_SAVE_STOP(pSSM);
}

int VBoxGLWidget::vhwaLoadExec(struct SSMHANDLE * pSSM, uint32_t u32Version)
{
    VBOXQGL_LOAD_START(pSSM);

    int rc;
    uint32_t u32;

    rc = SSMR3GetU32(pSSM, &u32); AssertRC(rc);
    if(RT_SUCCESS(rc))
    {
        for(uint32_t i = 0; i < u32; ++i)
        {
            rc = vhwaLoadSurface(pSSM, u32Version);  AssertRC(rc);
            if(RT_FAILURE(rc))
                break;
        }

        if(RT_SUCCESS(rc))
        {
            rc = SSMR3GetU32(pSSM, &u32); AssertRC(rc);
            if(RT_SUCCESS(rc))
            {
                for(uint32_t i = 0; i < u32; ++i)
                {
                    uint32_t cSurfs;
                    rc = SSMR3GetU32(pSSM, &cSurfs); AssertRC(rc);
                    for(uint32_t j = 0; j < cSurfs; ++j)
                    {
                        rc = vhwaLoadSurface(pSSM, u32Version);  AssertRC(rc);
                        if(RT_FAILURE(rc))
                            break;
                    }

                    if(RT_SUCCESS(rc))
                    {
                        rc = vhwaLoadOverlayData(pSSM, u32Version);  AssertRC(rc);
                    }

                    if(RT_FAILURE(rc))
                    {
                        break;
                    }
                }
            }
        }
    }

    VBOXQGL_LOAD_STOP(pSSM);

    return rc;
}

int VBoxGLWidget::vhwaConstruct(struct _VBOXVHWACMD_HH_CONSTRUCT *pCmd)
{
    PVM pVM = (PVM)pCmd->pVM;
    uint32_t intsId = 0; /* @todo: set the proper id */

    char nameFuf[sizeof(VBOXQGL_STATE_NAMEBASE) + 8];

    char * pszName = nameFuf;
    sprintf(pszName, "%s%d", VBOXQGL_STATE_NAMEBASE, intsId);
    int rc = SSMR3RegisterExternal(
            pVM,                    /* The VM handle*/
            pszName,                /* Data unit name. */
            intsId,                 /* The instance identifier of the data unit.
                                     * This must together with the name be unique. */
            VBOXQGL_STATE_VERSION,   /* Data layout version number. */
            128,             /* The approximate amount of data in the unit.
                              * Only for progress indicators. */
            NULL, NULL, NULL, /* pfnLiveXxx */
            NULL,            /* Prepare save callback, optional. */
            vboxQGLSaveExec, /* Execute save callback, optional. */
            NULL,            /* Done save callback, optional. */
            NULL,            /* Prepare load callback, optional. */
            vboxQGLLoadExec, /* Execute load callback, optional. */
            NULL,            /* Done load callback, optional. */
            this             /* User argument. */
            );
    Assert(RT_SUCCESS(rc));
    return rc;
}

uchar * VBoxGLWidget::vboxVRAMAddressFromOffset(uint64_t offset)
{
    return ((offset != VBOXVHWA_OFFSET64_VOID) && vboxUsesGuestVRAM()) ? vboxAddress() + offset : NULL;
}

uint64_t VBoxGLWidget::vboxVRAMOffsetFromAddress(uchar* addr)
{
    return uint64_t(addr - vboxAddress());
}

uint64_t VBoxGLWidget::vboxVRAMOffset(VBoxVHWASurfaceBase * pSurf)
{
    return pSurf->addressAlocated() ? VBOXVHWA_OFFSET64_VOID : vboxVRAMOffsetFromAddress(pSurf->address());
}

#endif

void VBoxGLWidget::initializeGL()
{
    vboxVHWAGlInit(context());
    VBoxVHWASurfaceBase::globalInit();
}

#ifdef VBOXQGL_DBG_SURF

int g_iCur = 0;
VBoxVHWASurfaceBase * g_apSurf[] = {NULL, NULL, NULL};

void VBoxGLWidget::vboxDoTestSurfaces(void* context)
{
//    uint32_t width = 103;
//    uint32_t height = 47;
//    uint32_t rgbBitCount = 32;
//    uint32_t r = 0xff, g = 0xff00, b = 0xff0000;
//    QRect dstRect(10, 50, width, height);
//    QRect srcRect(0, 0, width, height);
////    Assert(0);
//    if(!pSurf1)
//    {
//
////        pSurf1 = new VBoxVHWASurfaceBase(this, width, height,
////                        VBoxVHWAColorFormat(rgbBitCount,
////                                r,
////                                g,
////                                b),
////                        NULL, NULL, NULL, NULL);
//        pSurf1 = new VBoxVHWASurfaceBase(this, &QSize(width, height),
////                        ((pCmd->SurfInfo.surfCaps & VBOXVHWA_SCAPS_OVERLAY) ? mDisplay.getPrimary()->rect().size() : &QSize(pCmd->SurfInfo.width, pCmd->SurfInfo.height)),
//                        &mDisplay.getPrimary()->rect().size(),
//                        VBoxVHWAColorFormat(rgbBitCount,
//                                r,
//                                g,
//                                b),
////                        pSrcBltCKey, pDstBltCKey, pSrcOverlayCKey, pDstOverlayCKey);
//                        NULL, NULL, NULL, NULL);
//
//        pSurf1->init(mDisplay.getVGA(), NULL);
//
//        VBoxVHWASurfList *pConstructingList = new VBoxVHWASurfList();
//        mDisplay.addOverlay(pConstructingList);
//        pConstructingList->add(pSurf1);
//        pConstructingList->setCurrentVisible(pSurf1);
////        pSurf1->performDisplay();
//    }
//
////    pDisplay->blt(&dstRect, pSurf1, &srcRect, NULL, NULL);
////    pDisplay->performDisplay();
    if(g_iCur >= RT_ELEMENTS(g_apSurf))
        g_iCur = 0;
    VBoxVHWASurfaceBase * pSurf1 = g_apSurf[g_iCur];
    if(pSurf1)
    {
        pSurf1->getComplexList()->setCurrentVisible(pSurf1);
    }
}
#endif

void VBoxGLWidget::vboxDoPaint(void *pe)
{
    Q_UNUSED(pe);

#ifdef VBOXQGL_DBG_SURF
    vboxDoTestSurfaces(NULL);
#endif
    QRect vp(mView->contentsX(), mView->contentsY(), width(), height());

    if(vp != mViewport)
    {
        adjustViewport(mDisplay.getVGA()->size(), vp);
        mViewport = vp;
    }

//#ifdef VBOXQGL_PROF_BASE
//    vboxDoUpdateRect(&((QPaintEvent*)pe)->rect());
//#endif
    mDisplay.performDisplay();
}

void VBoxGLWidget::vboxDoUpdateRect(const QRect * pRect)
{
    mDisplay.getVGA()->updatedMem(pRect);
}

void VBoxGLWidget::vboxDoResize(void *resize)
{
//    Assert(!format().accum());
//    Assert(format().alpha());
//    Assert(format().alphaBufferSize() == 8);
    Assert(format().blueBufferSize() == 8);
    Assert(format().greenBufferSize() == 8);
    Assert(format().redBufferSize() == 8);

//    Assert(!format().depth());
    Assert(format().directRendering());
    Assert(format().doubleBuffer());
    Assert(format().hasOpenGL());
    VBOXQGLLOG(("hasOpenGLOverlays(%d), hasOverlay(%d)\n", format().hasOpenGLOverlays(), format().hasOverlay()));
//    Assert(format().hasOpenGLOverlays());
//    Assert(format().hasOverlay());
    Assert(format().plane() == 0);
    Assert(format().rgba());
    Assert(!format().sampleBuffers());
//    Assert(!format().stencil());
    Assert(!format().stereo());
    VBOXQGLLOG(("swapInterval(%d)\n", format().swapInterval()));
//    Assert(format().swapInterval() == 0);


    VBOXQGL_CHECKERR(
            vboxglActiveTexture(GL_TEXTURE0);
        );

    VBoxResizeEvent *re = (VBoxResizeEvent*)resize;
    bool remind = false;
    bool fallback = false;

    VBOXQGLLOG(("resizing: fmt=%d, vram=%p, bpp=%d, bpl=%d, width=%d, height=%d\n",
                      re->pixelFormat(), re->VRAM(),
                      re->bitsPerPixel(), re->bytesPerLine(),
                      re->width(), re->height()));

    /* clean the old values first */

    ulong bytesPerLine;
    uint32_t bitsPerPixel;
    uint32_t b = 0xff, g = 0xff00, r = 0xff0000;

    /* check if we support the pixel format and can use the guest VRAM directly */
    if (re->pixelFormat() == FramebufferPixelFormat_FOURCC_RGB)
    {

        bitsPerPixel = re->bitsPerPixel();
        bytesPerLine = re->bytesPerLine();
        ulong bitsPerLine = bytesPerLine * 8;

        switch (bitsPerPixel)
        {
            case 32:
//                format = VBoxVHWAColorFormat(bitsPerPixel, 0xff, 0xff00, 0xff0000);
                break;
            case 24:
#ifdef DEBUG_misha
                Assert(0);
#endif
//                format = VBoxVHWAColorFormat(bitsPerPixel, 0xff, 0xff00, 0xff0000);
//                remind = true;
                break;
//            case 16:
//                Assert(0);
////                r = 0xf800;
////                g = 0x7e0;
////                b = 0x1f;
//                break;
            case 8:
#ifdef DEBUG_misha
                Assert(0);
#endif
                g = b = 0;
                remind = true;
                break;
            case 1:
#ifdef DEBUG_misha
                Assert(0);
#endif
                r = 1;
                g = b = 0;
                remind = true;
                break;
            default:
#ifdef DEBUG_misha
                Assert(0);
#endif
                remind = true;
                fallback = true;
                break;
        }

        if (!fallback)
        {
            /* QImage only supports 32-bit aligned scan lines... */
            Assert ((re->bytesPerLine() & 3) == 0);
            fallback = ((re->bytesPerLine() & 3) != 0);
        }
        if (!fallback)
        {
            /* ...and the scan lines ought to be a whole number of pixels. */
            Assert ((bitsPerLine & (re->bitsPerPixel() - 1)) == 0);
            fallback = ((bitsPerLine & (re->bitsPerPixel() - 1)) != 0);
        }
        if (!fallback)
        {
            // ulong virtWdt = bitsPerLine / re->bitsPerPixel();
            mPixelFormat = FramebufferPixelFormat_FOURCC_RGB;
            mUsesGuestVRAM = true;
        }
    }
    else
    {
        fallback = true;
    }

    if (fallback)
    {
        /* we don't support either the pixel format or the color depth,
         * fallback to a self-provided 32bpp RGB buffer */
        bitsPerPixel = 32;
        b = 0xff;
        g = 0xff00;
        r = 0xff0000;
        bytesPerLine = re->width()*bitsPerPixel/8;
        mPixelFormat = FramebufferPixelFormat_FOURCC_RGB;
//        internalformat = 3;//GL_RGB;
//        format = GL_BGRA_EXT;//GL_RGBA;
//        type = GL_UNSIGNED_BYTE;
        mUsesGuestVRAM = false;
    }

    ulong bytesPerPixel = bitsPerPixel/8;
    ulong displayWidth = bytesPerLine/bytesPerPixel;
    ulong displayHeight = re->height();

#ifdef VBOXQGL_DBG_SURF
    for(int i = 0; i < RT_ELEMENTS(g_apSurf); i++)
    {
        VBoxVHWASurfaceBase * pSurf1 = g_apSurf[i];
        if(pSurf1)
        {
            VBoxVHWASurfList *pConstructingList = pSurf1->getComplexList();
            delete pSurf1;
            if(pConstructingList)
                delete pConstructingList;
    //
    //            mDisplay.addOverlay(pConstructingList);
    //            //            pConstructingList->add(pSurf1);
    //            //            pConstructingList->setCurrentVisible(pSurf1);
    //            ////            pConstructingList->setCurrentVisible(NULL);
        }
    }
#endif

    VBoxVHWASurfaceBase * pDisplay = mDisplay.setVGA(NULL);
    if(pDisplay)
        delete pDisplay;

    VBoxVHWAColorFormat format(bitsPerPixel, r,g,b);
    QSize dispSize(displayWidth, displayHeight);
    pDisplay = new VBoxVHWASurfaceBase(this, &dispSize, &dispSize,
    		format,
            (VBoxVHWAColorKey*)NULL, (VBoxVHWAColorKey*)NULL, (VBoxVHWAColorKey*)NULL, (VBoxVHWAColorKey*)NULL, true);
    pDisplay->init(NULL, mUsesGuestVRAM ? re->VRAM() : NULL);
    mDisplay.setVGA(pDisplay);
    VBOXQGLLOG(("\n\n*******\n\n     viewport size is: (%d):(%d)\n\n*******\n\n", size().width(), size().height()));
    mViewport = QRect(0,0,displayWidth, displayHeight);
    adjustViewport(dispSize, mViewport);
    setupMatricies(dispSize);

#ifdef VBOXQGL_DBG_SURF
    {
        uint32_t width = 100;
        uint32_t height = 60;
//        uint32_t rgbBitCount = 32;
//        uint32_t r = 0xff, g = 0xff00, b = 0xff0000;
//        QRect dstRect(150, 200, width, height);
//        QRect srcRect(0, 0, 720, 480);
    //    Assert(0);

        for(int i = 0; i < RT_ELEMENTS(g_apSurf); i++)
        {

    //        pSurf1 = new VBoxVHWASurfaceBase(this, width, height,
    //                        VBoxVHWAColorFormat(rgbBitCount,
    //                                r,
    //                                g,
    //                                b),
    //                        NULL, NULL, NULL, NULL);
            VBoxVHWASurfaceBase *pSurf1 = new VBoxVHWASurfaceBase(this, &QSize(width, height),
    //                        ((pCmd->SurfInfo.surfCaps & VBOXVHWA_SCAPS_OVERLAY) ? mDisplay.getPrimary()->rect().size() : &QSize(pCmd->SurfInfo.width, pCmd->SurfInfo.height)),
                            &mDisplay.getPrimary()->rect().size(),
//                            VBoxVHWAColorFormat(rgbBitCount,
//                                    r,
//                                    g,
//                                    b),
                            VBoxVHWAColorFormat(FOURCC_YV12),
    //                        pSrcBltCKey, pDstBltCKey, pSrcOverlayCKey, pDstOverlayCKey);
                            NULL, NULL, NULL, &VBoxVHWAColorKey(0,0), false);

            Assert(mDisplay.getVGA());
            pSurf1->init(mDisplay.getVGA(), NULL);
            uchar *addr = pSurf1->address();
            uchar cur = 0;
            for(uint32_t k = 0; k < width*height; k++)
            {
            	addr[k] = cur;
            	cur+=64;
            }
            pSurf1->updatedMem(&QRect(0,0,width, height));
//            VBOXQGL_CHECKERR(
//                    vboxglActiveTexture(GL_TEXTURE0);
//                );

            VBoxVHWASurfList *pConstructingList = new VBoxVHWASurfList();
            mDisplay.addOverlay(pConstructingList);
            pConstructingList->add(pSurf1);
//            pConstructingList->setCurrentVisible(pSurf1);
//            pConstructingList->setCurrentVisible(NULL);
            g_apSurf[i] = pSurf1;

//            VBoxVHWAGlProgramVHWA * pProgram = vboxVHWAGetGlProgramMngr()->getProgram(true, false, &pSurf1->colorFormat(), &pDisplay->colorFormat());
//            pProgram->start();
//            pProgram->setSrcTexImgWidth(pSurf1->texRect().width());
//            pProgram->stop();
        }
//        else
//        {
//            VBoxVHWASurfList *pConstructingList = pSurf1->getComplexList();
//            mDisplay.addOverlay(pConstructingList);
//            pConstructingList->add(pSurf1);
//            pConstructingList->setCurrentVisible(pSurf1);
////            pConstructingList->setCurrentVisible(NULL);
//        }

        VBOXVHWACMD_SURF_OVERLAY_UPDATE updateCmd;
        memset(&updateCmd, 0, sizeof(updateCmd));
        updateCmd.u.in.hSrcSurf = (VBOXVHWA_SURFHANDLE)g_apSurf[0];
        updateCmd.u.in.hDstSurf = (VBOXVHWA_SURFHANDLE)pDisplay;
        updateCmd.u.in.flags =
        		VBOXVHWA_OVER_SHOW
        		| VBOXVHWA_OVER_KEYDESTOVERRIDE;

        updateCmd.u.in.desc.DstCK.high = 1;
        updateCmd.u.in.desc.DstCK.low = 1;

        updateCmd.u.in.dstRect.left = 0;
        updateCmd.u.in.dstRect.right = pDisplay->width();
        updateCmd.u.in.dstRect.top = (pDisplay->height() - height)/2;
        updateCmd.u.in.dstRect.bottom = updateCmd.u.in.dstRect.top + height;

        updateCmd.u.in.srcRect.left = 0;
        updateCmd.u.in.srcRect.right = width;
        updateCmd.u.in.srcRect.top = 0;
        updateCmd.u.in.srcRect.bottom = height;

        updateCmd.u.in.offDstSurface = 0xffffffffffffffffL; /* just a magic to avoid surf mem buffer change  */
        updateCmd.u.in.offSrcSurface = 0xffffffffffffffffL; /* just a magic to avoid surf mem buffer change  */

        vhwaSurfaceOverlayUpdate(&updateCmd);
    }
#endif

    VBoxVHWACommandElement * pPpCmd = mResizePostProcessCmds.detachList();
    if(pPpCmd)
    {
        processCmdList(pPpCmd);
        while(pPpCmd)
        {
            VBoxVHWACommandElement * pCur = pPpCmd;
            pPpCmd = pCur->mpNext;
            delete pCur;
        }
    }


    mDisplay.performDisplay();

    if (remind)
    {
        class RemindEvent : public VBoxAsyncEvent
        {
            ulong mRealBPP;
        public:
            RemindEvent (ulong aRealBPP)
                : mRealBPP (aRealBPP) {}
            void handle()
            {
                vboxProblem().remindAboutWrongColorDepth (mRealBPP, 32);
            }
        };
        (new RemindEvent (re->bitsPerPixel()))->post();
    }
}

//typedef struct VBOXQGLDEFFEREDOPCONTEXT
//{
//    PFNVBOXQGLOP pfn;
//    void *pContext;
//    VBoxVHWACommandElement * pEl;
//}VBOXQGLDEFFEREDOPCONTEXT;
//
//void VBoxGLWidget::vboxDefferedOpCallback(void * pContext)
//{
//    VBOXQGLDEFFEREDOPCONTEXT * pData = (VBOXQGLDEFFEREDOPCONTEXT*)pContext;
//    this->*(pData->pfn)(pData->pContext);
//    delete pEl;
//}

void VBoxGLWidget::vboxExecOnResize(PFNVBOXQGLOP pfn, void* pContext)
{
    VBoxVHWACommandElement *pCmd = new VBoxVHWACommandElement();
    VBOXVHWACALLBACKINFO info;
    info.pThis = this;
    info.pfnCallback = pfn;
    info.pContext = pContext;
    pCmd->setOp(info);
    mResizePostProcessCmds.put(pCmd);
}

//int VBoxGLWidget::vboxExecOpSynch(PFNVBOXQGLOP pfn, void* pContext)
//{
//    VBOXQGLOPCALLBACKCONTEXT data;
//    data.pThis = this;
//    data.pfn = pfn;
//    data.pContext = pContext;
//    int rc = RTSemEventCreate(&data.hEvent);
//    AssertRC(rc);
//    if(RT_SUCCESS(rc))
//    {
//        VBOXVHWACALLBACKINFO info;
//        info.pContext = &data;
//        info.pfnCallback = vboxQGLOpCallback;
//        postCmd(VBOXVHWA_PIPECMD_OP, &info);
//        rc = RTSemEventWaitNoResume(data.hEvent, RT_INDEFINITE_WAIT);
//        AssertRC(rc);
//        if(RT_SUCCESS(rc))
//        {
//            RTSemEventDestroy(data.hEvent);
//        }
//    }
//    return rc;
//}

VBoxVHWAColorFormat::VBoxVHWAColorFormat(uint32_t bitsPerPixel, uint32_t r, uint32_t g, uint32_t b) :
    mWidthCompression(1),
    mHeightCompression(1)
{
    init(bitsPerPixel, r, g, b);
}

VBoxVHWAColorFormat::VBoxVHWAColorFormat(uint32_t fourcc) :
    mWidthCompression(1),
    mHeightCompression(1)
{
    init(fourcc);
}

void VBoxVHWAColorFormat::init(uint32_t fourcc)
{
    mDataFormat = fourcc;
    mInternalFormat = GL_RGBA8;//GL_RGB;
    mFormat = GL_BGRA_EXT;//GL_RGBA;
    mType = GL_UNSIGNED_BYTE;
    mR = VBoxVHWAColorComponent(0xff);
    mG = VBoxVHWAColorComponent(0xff);
    mB = VBoxVHWAColorComponent(0xff);
    mA = VBoxVHWAColorComponent(0xff);
    mBitsPerPixelTex = 32;

    switch(fourcc)
    {
        case FOURCC_AYUV:
            mBitsPerPixel = 32;
            mWidthCompression = 1;
            break;
        case FOURCC_UYVY:
        case FOURCC_YUY2:
            mBitsPerPixel = 16;
            mWidthCompression = 2;
            break;
        case FOURCC_YV12:
            mBitsPerPixel = 8;
            mWidthCompression = 4;
            break;
        default:
            Assert(0);
            mBitsPerPixel = 0;
            mBitsPerPixelTex = 0;
            mWidthCompression = 0;
            break;
    }
}

void VBoxVHWAColorFormat::init(uint32_t bitsPerPixel, uint32_t r, uint32_t g, uint32_t b)
{
    mBitsPerPixel = bitsPerPixel;
    mBitsPerPixelTex = bitsPerPixel;
    mDataFormat = 0;
    switch (bitsPerPixel)
    {
        case 32:
            mInternalFormat = GL_RGB;//3;//GL_RGB;
            mFormat = GL_BGRA_EXT;//GL_RGBA;
            mType = GL_UNSIGNED_BYTE;
            mR = VBoxVHWAColorComponent(r);
            mG = VBoxVHWAColorComponent(g);
            mB = VBoxVHWAColorComponent(b);
            break;
        case 24:
#ifdef DEBUG_misha
            Assert(0);
#endif
            mInternalFormat = 3;//GL_RGB;
            mFormat = GL_BGR_EXT;
            mType = GL_UNSIGNED_BYTE;
            mR = VBoxVHWAColorComponent(r);
            mG = VBoxVHWAColorComponent(g);
            mB = VBoxVHWAColorComponent(b);
            break;
        case 16:
#ifdef DEBUG_misha
            Assert(0);
#endif
            mInternalFormat = GL_RGB5;
            mFormat = GL_BGR_EXT;
            mType = GL_UNSIGNED_BYTE; /* TODO" ??? */
            mR = VBoxVHWAColorComponent(r);
            mG = VBoxVHWAColorComponent(g);
            mB = VBoxVHWAColorComponent(b);
            break;
        case 8:
#ifdef DEBUG_misha
            Assert(0);
#endif
            mInternalFormat = 1;//GL_RGB;
            mFormat = GL_RED;//GL_RGB;
            mType = GL_UNSIGNED_BYTE;
            mR = VBoxVHWAColorComponent(0xff);
            break;
        case 1:
#ifdef DEBUG_misha
            Assert(0);
#endif
            mInternalFormat = 1;
            mFormat = GL_COLOR_INDEX;
            mType = GL_BITMAP;
            mR = VBoxVHWAColorComponent(0x1);
            break;
        default:
#ifdef DEBUG_misha
            Assert(0);
#endif
            mBitsPerPixel = 0;
            mBitsPerPixelTex = 0;
            break;
    }
}

bool VBoxVHWAColorFormat::equals (const VBoxVHWAColorFormat & other) const
{
    if(fourcc())
        return fourcc() == other.fourcc();
    if(other.fourcc())
        return false;

    return bitsPerPixel() == other.bitsPerPixel();
}

VBoxVHWAColorComponent::VBoxVHWAColorComponent(uint32_t aMask)
{
    unsigned f = ASMBitFirstSetU32(aMask);
    if(f)
    {
        mOffset = f - 1;
        f = ASMBitFirstSetU32(~(aMask >> mOffset));
        if(f)
        {
            mcBits = f - 1;
        }
        else
        {
            mcBits = 32 - mOffset;
        }

        Assert(mcBits);
        mMask = (((uint32_t)0xffffffff) >> (32 - mcBits)) << mOffset;
        Assert(mMask == aMask);

        mRange = (mMask >> mOffset) + 1;
    }
    else
    {
        mMask = 0;
        mRange = 0;
        mOffset = 32;
        mcBits = 0;
    }
}

void VBoxVHWAColorFormat::pixel2Normalized (uint32_t pix, float *r, float *g, float *b) const
{
    *r = mR.colorValNorm(pix);
    *g = mG.colorValNorm(pix);
    *b = mB.colorValNorm(pix);
}

#endif
