summaryrefslogtreecommitdiffstats
path: root/src/VBox/GuestHost/OpenGL/state_tracker/dump.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/GuestHost/OpenGL/state_tracker/dump.cpp')
-rw-r--r--src/VBox/GuestHost/OpenGL/state_tracker/dump.cpp1741
1 files changed, 1741 insertions, 0 deletions
diff --git a/src/VBox/GuestHost/OpenGL/state_tracker/dump.cpp b/src/VBox/GuestHost/OpenGL/state_tracker/dump.cpp
new file mode 100644
index 00000000..42a41df1
--- /dev/null
+++ b/src/VBox/GuestHost/OpenGL/state_tracker/dump.cpp
@@ -0,0 +1,1741 @@
+/* $Id: dump.cpp $ */
+
+/** @file
+ * Blitter API implementation
+ */
+/*
+ * Copyright (C) 2013-2019 Oracle Corporation
+ *
+ * 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.
+ */
+#include "cr_blitter.h"
+#include "cr_spu.h"
+#include "chromium.h"
+#include "cr_error.h"
+#include "cr_net.h"
+#include "cr_rand.h"
+#include "cr_mem.h"
+#include "cr_string.h"
+#include <cr_dump.h>
+#include "cr_pixeldata.h"
+
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+#include <iprt/mem.h>
+
+#include <stdio.h>
+
+#ifdef VBOX_WITH_CRDUMPER
+
+static uint32_t g_CrDbgDumpRecTexInfo = 1;
+static uint32_t g_CrDbgDumpAlphaData = 1;
+
+/* dump stuff */
+#pragma pack(1)
+typedef struct VBOX_BITMAPFILEHEADER {
+ uint16_t bfType;
+ uint32_t bfSize;
+ uint16_t bfReserved1;
+ uint16_t bfReserved2;
+ uint32_t bfOffBits;
+} VBOX_BITMAPFILEHEADER;
+
+typedef struct VBOX_BITMAPINFOHEADER {
+ uint32_t biSize;
+ int32_t biWidth;
+ int32_t biHeight;
+ uint16_t biPlanes;
+ uint16_t biBitCount;
+ uint32_t biCompression;
+ uint32_t biSizeImage;
+ int32_t biXPelsPerMeter;
+ int32_t biYPelsPerMeter;
+ uint32_t biClrUsed;
+ uint32_t biClrImportant;
+} VBOX_BITMAPINFOHEADER;
+#pragma pack()
+
+void crDmpImgBmp(CR_BLITTER_IMG *pImg, const char *pszFilename)
+{
+ static int sIdx = 0;
+
+ if ( pImg->bpp != 16
+ && pImg->bpp != 24
+ && pImg->bpp != 32)
+ {
+ crWarning("not supported bpp %d", pImg->bpp);
+ return;
+ }
+
+ FILE *f = fopen (pszFilename, "wb");
+ if (!f)
+ {
+ crWarning("fopen failed");
+ return;
+ }
+
+ VBOX_BITMAPFILEHEADER bf;
+
+ bf.bfType = 'MB';
+ bf.bfSize = sizeof (VBOX_BITMAPFILEHEADER) + sizeof (VBOX_BITMAPINFOHEADER) + pImg->cbData;
+ bf.bfReserved1 = 0;
+ bf.bfReserved2 = 0;
+ bf.bfOffBits = sizeof (VBOX_BITMAPFILEHEADER) + sizeof (VBOX_BITMAPINFOHEADER);
+
+ VBOX_BITMAPINFOHEADER bi;
+
+ bi.biSize = sizeof (bi);
+ bi.biWidth = pImg->width;
+ bi.biHeight = pImg->height;
+ bi.biPlanes = 1;
+ bi.biBitCount = pImg->bpp;
+ bi.biCompression = 0;
+ bi.biSizeImage = pImg->cbData;
+ bi.biXPelsPerMeter = 0;
+ bi.biYPelsPerMeter = 0;
+ bi.biClrUsed = 0;
+ bi.biClrImportant = 0;
+
+ fwrite (&bf, 1, sizeof (bf), f);
+ fwrite (&bi, 1, sizeof (bi), f);
+ fwrite (pImg->pvData, 1, pImg->cbData, f);
+
+ fclose (f);
+}
+
+typedef struct CRDUMPGETHWID_DATA
+{
+ GLuint hwid;
+ PFNCRDUMPGETHWID pfnGetHwid;
+ unsigned long Key;
+ void* pvObj;
+} CRDUMPGETHWID_DATA;
+
+static void crDmpHashtableSearchByHwidCB(unsigned long key, void *pData1, void *pData2)
+{
+ CRDUMPGETHWID_DATA *pData = (CRDUMPGETHWID_DATA*)pData2;
+ if (pData->pvObj)
+ return;
+
+ if (pData->hwid == pData->pfnGetHwid(pData1))
+ {
+ pData->Key = key;
+ pData->pvObj = pData1;
+ }
+}
+
+void* crDmpHashtableSearchByHwid(CRHashTable *pHash, GLuint hwid, PFNCRDUMPGETHWID pfnGetHwid, unsigned long *pKey)
+{
+ CRDUMPGETHWID_DATA Data = {0};
+ Data.hwid = hwid;
+ Data.pfnGetHwid = pfnGetHwid;
+ crHashtableWalk(pHash, crDmpHashtableSearchByHwidCB, &Data);
+
+ Assert(Data.pvObj);
+
+ if (pKey)
+ *pKey = Data.Key;
+ return Data.pvObj;
+}
+
+#if 0
+typedef struct CR_SERVER_DUMP_FIND_TEX
+{
+ GLint hwid;
+ CRTextureObj *pTobj
+} CR_SERVER_DUMP_FIND_TEX;
+
+void crServerDumpFindTexCb(unsigned long key, void *pData1, void *pData2)
+{
+ CR_SERVER_DUMP_FIND_TEX *pTex = (CR_SERVER_DUMP_FIND_TEX*)pData2;
+ CRTextureObj *pTobj = (CRTextureObj *)pData1;
+ if (pTobj->hwid == pTex->hwid)
+ pTex->pTobj = pTobj;
+}
+#endif
+
+#define CR_DUMP_MAKE_CASE(_val) case _val: return #_val
+#define CR_DUMP_MAKE_CASE_UNKNOWN(_val, _str, _pDumper) default: { \
+ crWarning("%s %d", (_str), _val); \
+ crDmpStrF((_pDumper), "WARNING: %s %d", (_str), _val); \
+ return (_str); \
+}
+
+DECLINLINE(size_t) crDmpFormatVal(char *pString, size_t cbString, const char *pszElFormat, uint32_t cbVal, const void *pvVal)
+{
+ if (pszElFormat[0] != '%' || pszElFormat[1] == '\0')
+ {
+ crWarning("invalid format %s", pszElFormat);
+ return 0;
+ }
+ switch (cbVal)
+ {
+ case 8:
+ return sprintf_s(pString, cbString, pszElFormat, *((double*)pvVal));
+ case 4:
+ {
+ /* we do not care only about type specifiers, all the rest is not accepted */
+ switch (pszElFormat[1])
+ {
+ case 'f':
+ /* float would be promoted to double */
+ return sprintf_s(pString, cbString, pszElFormat, *((float*)pvVal));
+ default:
+ return sprintf_s(pString, cbString, pszElFormat, *((uint32_t*)pvVal));
+ }
+ }
+ case 2:
+ return sprintf_s(pString, cbString, pszElFormat, *((uint16_t*)pvVal));
+ case 1:
+ return sprintf_s(pString, cbString, pszElFormat, *((uint8_t*)pvVal));
+ default:
+ crWarning("unsupported size %d", cbVal);
+ return 0;
+ }
+}
+
+VBOXDUMPDECL(size_t) crDmpFormatRawArray(char *pString, size_t cbString, const char *pszElFormat, uint32_t cbEl, const void *pvVal, uint32_t cVal)
+{
+ if (cbString < 2)
+ {
+ crWarning("too few buffer size");
+ return 0;
+ }
+
+ const size_t cbInitString = cbString;
+ *pString++ = '{';
+ --cbString;
+ size_t cbWritten;
+ const uint8_t *pu8Val = (const uint8_t *)pvVal;
+ for (uint32_t i = 0; i < cVal; ++i)
+ {
+ cbWritten = crDmpFormatVal(pString, cbString, pszElFormat, cbEl, (const void *)pu8Val);
+ pu8Val += cbEl;
+ Assert(cbString >= cbWritten);
+ pString += cbWritten;
+ cbString -= cbWritten;
+ if (i != cVal - 1)
+ {
+ cbWritten = sprintf_s(pString, cbString, ", ");
+ Assert(cbString >= cbWritten);
+ pString += cbWritten;
+ cbString -= cbWritten;
+ }
+ }
+
+ if (!cbString)
+ {
+ crWarning("too few buffer size");
+ return 0;
+ }
+ *pString++ = '}';
+ --cbString;
+
+ if (!cbString)
+ {
+ crWarning("too few buffer size");
+ return 0;
+ }
+ *pString++ = '\0';
+
+ return cbInitString - cbString;
+}
+
+VBOXDUMPDECL(size_t) crDmpFormatMatrixArray(char *pString, size_t cbString, const char *pszElFormat, uint32_t cbEl, const void *pvVal, uint32_t cX, uint32_t cY)
+{
+ if (cbString < 2)
+ {
+ crWarning("too few buffer size");
+ return 0;
+ }
+
+ const size_t cbInitString = cbString;
+ *pString++ = '{';
+ --cbString;
+ size_t cbWritten;
+ const uint8_t *pu8Val = (const uint8_t *)pvVal;
+ for (uint32_t i = 0; i < cY; ++i)
+ {
+ cbWritten = crDmpFormatRawArray(pString, cbString, pszElFormat, cbEl, (const void *)pu8Val, cX);
+ pu8Val += (cbEl * cX);
+ Assert(cbString >= cbWritten);
+ pString += cbWritten;
+ cbString -= cbWritten;
+ if (i != cY - 1)
+ {
+ if (cbString < 3)
+ {
+ crWarning("too few buffer size");
+ return 0;
+ }
+ *pString++ = ',';
+ --cbString;
+ *pString++ = '\n';
+ --cbString;
+ }
+ }
+ if (!cbString)
+ {
+ crWarning("too few buffer size");
+ return 0;
+ }
+ *pString++ = '}';
+ --cbString;
+
+ if (!cbString)
+ {
+ crWarning("too few buffer size");
+ return 0;
+ }
+ *pString++ = '\0';
+
+ return cbInitString - cbString;
+}
+
+VBOXDUMPDECL(size_t) crDmpFormatArray(char *pString, size_t cbString, const char *pszElFormat, uint32_t cbEl, const void *pvVal, uint32_t cVal)
+{
+ switch(cVal)
+ {
+ case 1:
+ return crDmpFormatVal(pString, cbString, pszElFormat, cbEl, pvVal);
+ case 16:
+ return crDmpFormatMatrixArray(pString, cbString, pszElFormat, cbEl, pvVal, 4, 4);
+ case 9:
+ return crDmpFormatMatrixArray(pString, cbString, pszElFormat, cbEl, pvVal, 3, 3);
+ case 0:
+ crWarning("value array is empty");
+ return 0;
+ default:
+ return crDmpFormatRawArray(pString, cbString, pszElFormat, cbEl, pvVal, cVal);
+ }
+}
+
+VBOXDUMPDECL(void) crRecDumpVertAttrv(CR_RECORDER *pRec, CRContext *ctx, GLuint idx, const char*pszElFormat, uint32_t cbEl, const void *pvVal, uint32_t cVal)
+{
+ char aBuf[1024];
+ crDmpFormatRawArray(aBuf, sizeof (aBuf), pszElFormat, cbEl, pvVal, cVal);
+ crDmpStrF(pRec->pDumper, "(%u, %s)", idx, aBuf);
+}
+
+VBOXDUMPDECL(void) crRecDumpVertAttrV(CR_RECORDER *pRec, CRContext *ctx, const char*pszFormat, va_list pArgList)
+{
+ crDmpStrV(pRec->pDumper, pszFormat, pArgList);
+}
+
+VBOXDUMPDECL(void) crRecDumpVertAttrF(CR_RECORDER *pRec, CRContext *ctx, const char*pszFormat, ...)
+{
+ va_list pArgList;
+ va_start(pArgList, pszFormat);
+ crRecDumpVertAttrV(pRec, ctx, pszFormat, pArgList);
+ va_end(pArgList);
+}
+
+void crRecDumpBuffer(CR_RECORDER *pRec, CRContext *ctx, GLint idRedirFBO, VBOXVR_TEXTURE *pRedirTex)
+{
+ GLenum texTarget = 0;
+ GLint hwBuf = 0, hwDrawBuf = 0;
+ GLint hwTex = 0, hwObjType = 0, hwTexLevel = 0, hwCubeFace = 0;
+ GLint width = 0, height = 0, depth = 0;
+ GLint id = 0;
+ CR_BLITTER_IMG Img = {0};
+ VBOXVR_TEXTURE Tex;
+ int rc;
+
+ pRec->pDispatch->GetIntegerv(GL_DRAW_BUFFER, &hwDrawBuf);
+ pRec->pDispatch->GetIntegerv(GL_FRAMEBUFFER_BINDING, &hwBuf);
+ if (hwBuf)
+ {
+ pRec->pDispatch->GetFramebufferAttachmentParameterivEXT(GL_DRAW_FRAMEBUFFER, hwDrawBuf, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &hwTex);
+ pRec->pDispatch->GetFramebufferAttachmentParameterivEXT(GL_DRAW_FRAMEBUFFER, hwDrawBuf, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &hwObjType);
+ if (hwObjType == GL_TEXTURE)
+ {
+ pRec->pDispatch->GetFramebufferAttachmentParameterivEXT(GL_DRAW_FRAMEBUFFER, hwDrawBuf, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, &hwTexLevel);
+ pRec->pDispatch->GetFramebufferAttachmentParameterivEXT(GL_DRAW_FRAMEBUFFER, hwDrawBuf, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE, &hwCubeFace);
+ if (hwCubeFace)
+ {
+ crWarning("cube face: unsupported");
+ return;
+ }
+
+ if (hwTexLevel)
+ {
+ crWarning("non-zero tex level attached, unsupported");
+ return;
+ }
+ }
+ else
+ {
+ crWarning("unsupported");
+ return;
+ }
+ }
+ else
+ {
+ crWarning("no buffer attached: unsupported");
+ return;
+ }
+
+ if (ctx->framebufferobject.drawFB)
+ {
+ GLuint iColor = (hwDrawBuf - GL_COLOR_ATTACHMENT0_EXT);
+ CRTextureObj *pTobj = (CRTextureObj *)crHashtableSearch(ctx->shared->textureTable, ctx->framebufferobject.drawFB->color[iColor].name);
+ CRTextureLevel *pTl = NULL;
+
+ id = pTobj->id;
+
+ Assert(iColor < RT_ELEMENTS(ctx->framebufferobject.drawFB->color));
+
+ if (!pTobj)
+ {
+ crWarning("no tobj");
+ return;
+ }
+ Assert(pTobj->hwid == hwTex);
+ Assert(pTobj);
+ Assert(ctx->framebufferobject.drawFB->hwid);
+ Assert(ctx->framebufferobject.drawFB->hwid == hwBuf);
+ Assert(ctx->framebufferobject.drawFB->drawbuffer[0] == hwDrawBuf);
+
+ Assert(ctx->framebufferobject.drawFB->color[iColor].level == hwTexLevel);
+ Assert(ctx->framebufferobject.drawFB->color[iColor].type == hwObjType);
+
+ texTarget = pTobj->target;
+
+ Assert(texTarget == GL_TEXTURE_2D);
+
+ pTl = &pTobj->level[0][hwTexLevel];
+
+ rc = CrBltEnter(pRec->pBlitter);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("CrBltEnter failed, rc %d", rc);
+ return;
+ }
+
+ pRec->pDispatch->BindTexture(texTarget, hwTex);
+
+ pRec->pDispatch->GetTexLevelParameteriv(texTarget, hwTexLevel, GL_TEXTURE_WIDTH, &width);
+ pRec->pDispatch->GetTexLevelParameteriv(texTarget, hwTexLevel, GL_TEXTURE_HEIGHT, &height);
+ pRec->pDispatch->GetTexLevelParameteriv(texTarget, hwTexLevel, GL_TEXTURE_DEPTH, &depth);
+
+ Assert(width == pTl->width);
+ Assert(height == pTl->height);
+ Assert(depth == pTl->depth);
+
+ pRec->pDispatch->BindTexture(texTarget, 0);
+ }
+ else
+ {
+ Assert(hwBuf == idRedirFBO);
+ if (!pRedirTex)
+ {
+ crWarning("pRedirTex is expected for non-FBO state!");
+ return;
+ }
+
+ Assert(hwTex == pRedirTex->hwid);
+
+ texTarget = pRedirTex->target;
+
+ width = pRedirTex->width;
+ height = pRedirTex->height;
+
+ rc = CrBltEnter(pRec->pBlitter);
+ if (!RT_SUCCESS(rc))
+ {
+ crWarning("CrBltEnter failed, rc %d", rc);
+ return;
+ }
+ }
+
+ Tex.width = width;
+ Tex.height = height;
+ Tex.target = texTarget;
+ Tex.hwid = hwTex;
+
+ rc = CrBltImgGetTex(pRec->pBlitter, &Tex, GL_BGRA, &Img);
+ if (RT_SUCCESS(rc))
+ {
+ crDmpImgF(pRec->pDumper, &Img, "ctx(%d), BUFFER: id(%d) hwid(%d), width(%d), height(%d)", ctx, id, Tex.hwid, width, height);
+
+ if (g_CrDbgDumpAlphaData)
+ {
+ CR_BLITTER_IMG AlphaImg = {0};
+ rc = crRecAlphaImgCreate(&Img, &AlphaImg);
+ if (RT_SUCCESS(rc))
+ {
+ crDmpImgF(pRec->pDumper, &AlphaImg, "Buffer ALPHA Data");
+ crRecAlphaImgDestroy(&AlphaImg);
+ }
+ else
+ {
+ crWarning("crRecAlphaImgCreate failed rc %d", rc);
+ }
+ }
+
+ CrBltImgFree(pRec->pBlitter, &Img);
+ }
+ else
+ {
+ crWarning("CrBltImgGetTex failed, rc %d", rc);
+ }
+
+ CrBltLeave(pRec->pBlitter);
+}
+
+static const char *crRecDumpShaderTypeString(GLenum enmType, CR_DUMPER *pDumper)
+{
+ switch (enmType)
+ {
+ CR_DUMP_MAKE_CASE(GL_VERTEX_SHADER_ARB);
+ CR_DUMP_MAKE_CASE(GL_FRAGMENT_SHADER_ARB);
+ CR_DUMP_MAKE_CASE(GL_GEOMETRY_SHADER_ARB);
+ CR_DUMP_MAKE_CASE_UNKNOWN(enmType, "Unknown Shader Type", pDumper);
+ }
+}
+
+static const char *crRecDumpVarTypeString(GLenum enmType, CR_DUMPER *pDumper)
+{
+ switch (enmType)
+ {
+ CR_DUMP_MAKE_CASE(GL_BYTE);
+ CR_DUMP_MAKE_CASE(GL_UNSIGNED_BYTE);
+ CR_DUMP_MAKE_CASE(GL_SHORT);
+ CR_DUMP_MAKE_CASE(GL_UNSIGNED_SHORT);
+ CR_DUMP_MAKE_CASE(GL_FLOAT);
+ CR_DUMP_MAKE_CASE(GL_DOUBLE);
+ CR_DUMP_MAKE_CASE(GL_FLOAT_VEC2);
+ CR_DUMP_MAKE_CASE(GL_FLOAT_VEC3);
+ CR_DUMP_MAKE_CASE(GL_FLOAT_VEC4);
+ CR_DUMP_MAKE_CASE(GL_INT);
+ CR_DUMP_MAKE_CASE(GL_UNSIGNED_INT);
+ CR_DUMP_MAKE_CASE(GL_INT_VEC2);
+ CR_DUMP_MAKE_CASE(GL_INT_VEC3);
+ CR_DUMP_MAKE_CASE(GL_INT_VEC4);
+ CR_DUMP_MAKE_CASE(GL_BOOL);
+ CR_DUMP_MAKE_CASE(GL_BOOL_VEC2);
+ CR_DUMP_MAKE_CASE(GL_BOOL_VEC3);
+ CR_DUMP_MAKE_CASE(GL_BOOL_VEC4);
+ CR_DUMP_MAKE_CASE(GL_FLOAT_MAT2);
+ CR_DUMP_MAKE_CASE(GL_FLOAT_MAT3);
+ CR_DUMP_MAKE_CASE(GL_FLOAT_MAT4);
+ CR_DUMP_MAKE_CASE(GL_SAMPLER_1D);
+ CR_DUMP_MAKE_CASE(GL_SAMPLER_2D);
+ CR_DUMP_MAKE_CASE(GL_SAMPLER_3D);
+ CR_DUMP_MAKE_CASE(GL_SAMPLER_CUBE);
+ CR_DUMP_MAKE_CASE(GL_SAMPLER_1D_SHADOW);
+ CR_DUMP_MAKE_CASE(GL_SAMPLER_2D_SHADOW);
+ CR_DUMP_MAKE_CASE(GL_SAMPLER_2D_RECT_ARB);
+ CR_DUMP_MAKE_CASE(GL_SAMPLER_2D_RECT_SHADOW_ARB);
+ CR_DUMP_MAKE_CASE(GL_FLOAT_MAT2x3);
+ CR_DUMP_MAKE_CASE(GL_FLOAT_MAT2x4);
+ CR_DUMP_MAKE_CASE(GL_FLOAT_MAT3x2);
+ CR_DUMP_MAKE_CASE(GL_FLOAT_MAT3x4);
+ CR_DUMP_MAKE_CASE(GL_FLOAT_MAT4x2);
+ CR_DUMP_MAKE_CASE(GL_FLOAT_MAT4x3);
+ CR_DUMP_MAKE_CASE_UNKNOWN(enmType, "Unknown Variable Type", pDumper);
+ }
+}
+
+static char *crRecDumpGetLine(char **ppszStr, uint32_t *pcbStr)
+{
+ char *pszStr, *pNewLine;
+ const uint32_t cbStr = *pcbStr;
+
+ if (!cbStr)
+ {
+ /* zero-length string */
+ return NULL;
+ }
+
+ if ((*ppszStr)[cbStr-1] != '\0')
+ {
+ crWarning("string should be null-rerminated, forcing it!");
+ (*ppszStr)[cbStr-1] = '\0';
+ }
+ pszStr = *ppszStr;
+ if (!*pszStr)
+ {
+ *pcbStr = 0;
+ return NULL;
+ }
+
+ if (!(pNewLine = strstr(pszStr, "\n")))
+ {
+ /* the string contains a single line! */
+ *ppszStr += strlen(pszStr);
+ *pcbStr = 0;
+ return pszStr;
+ }
+
+ *pNewLine = '\0';
+ *pcbStr = cbStr - (((uintptr_t)pNewLine) - ((uintptr_t)pszStr)) - 1;
+ Assert((*pcbStr) < UINT32_MAX/2);
+ Assert((*pcbStr) < cbStr);
+ *ppszStr = pNewLine + 1;
+
+ return pszStr;
+}
+
+static void crRecDumpStrByLine(CR_DUMPER *pDumper, char *pszStr, uint32_t cbStr)
+{
+ char *pszCurLine;
+ while ((pszCurLine = crRecDumpGetLine(&pszStr, &cbStr)) != NULL)
+ {
+ crDmpStrF(pDumper, "%s", pszCurLine);
+ }
+}
+
+static DECLCALLBACK(GLuint) crDmpGetHwidShaderCB(void *pvObj)
+{
+ return ((CRGLSLShader*)pvObj)->hwid;
+}
+
+static DECLCALLBACK(GLuint) crDmpGetHwidProgramCB(void *pvObj)
+{
+ return ((CRGLSLProgram*)pvObj)->hwid;
+}
+
+/* Context activation is done by the caller. */
+void crRecDumpLog(CR_RECORDER *pRec, GLint hwid)
+{
+ GLint cbLog = 0;
+ pRec->pDispatch->GetObjectParameterivARB(hwid, GL_OBJECT_INFO_LOG_LENGTH_ARB, &cbLog);
+
+ crDmpStrF(pRec->pDumper, "Log===%d===", hwid);
+
+ if (cbLog > 1)
+ {
+ GLchar *pszLog = (GLchar *) crAlloc(cbLog*sizeof (GLchar));
+
+ pRec->pDispatch->GetInfoLogARB(hwid, cbLog, NULL, pszLog);
+
+ crRecDumpStrByLine(pRec->pDumper, pszLog, cbLog);
+
+ crFree(pszLog);
+ }
+ crDmpStrF(pRec->pDumper, "End Log======");
+}
+
+void crRecDumpShader(CR_RECORDER *pRec, CRContext *ctx, GLint id, GLint hwid)
+{
+ GLint length = 0;
+ GLint type = 0;
+ GLint compileStatus = 0;
+
+#ifndef IN_GUEST
+ CRGLSLShader *pShad;
+
+ if (!id)
+ {
+ unsigned long tstKey = 0;
+ Assert(hwid);
+ pShad = (CRGLSLShader *)crDmpHashtableSearchByHwid(ctx->glsl.shaders, hwid, crDmpGetHwidShaderCB, &tstKey);
+ Assert(pShad);
+ if (!pShad)
+ return;
+ id = pShad->id;
+ Assert(tstKey == id);
+ }
+ else
+ {
+ pShad = (CRGLSLShader *)crHashtableSearch(ctx->glsl.shaders, id);
+ Assert(pShad);
+ if (!pShad)
+ return;
+ }
+
+ if (!hwid)
+ hwid = pShad->hwid;
+
+ Assert(pShad->hwid == hwid);
+ Assert(pShad->id == id);
+#else
+ if (!id)
+ id = hwid;
+ else if (!hwid)
+ hwid = id;
+
+ Assert(id);
+ Assert(hwid);
+ Assert(hwid == id);
+#endif
+
+ pRec->pDispatch->GetObjectParameterivARB(hwid, GL_OBJECT_SUBTYPE_ARB, &type);
+ pRec->pDispatch->GetObjectParameterivARB(hwid, GL_OBJECT_COMPILE_STATUS_ARB, &compileStatus);
+ crDmpStrF(pRec->pDumper, "SHADER ctx(%d) id(%d) hwid(%d) type(%s) status(%d):", ctx->id, id, hwid, crRecDumpShaderTypeString(type, pRec->pDumper), compileStatus);
+
+ crRecDumpLog(pRec, hwid);
+
+ pRec->pDispatch->GetObjectParameterivARB(hwid, GL_OBJECT_SHADER_SOURCE_LENGTH_ARB, &length);
+
+ char *pszSource = (char*)crCalloc(length + 1);
+ if (!pszSource)
+ {
+ crWarning("crCalloc failed");
+ crDmpStrF(pRec->pDumper, "WARNING: crCalloc failed");
+ return;
+ }
+
+ pRec->pDispatch->GetShaderSource(hwid, length, NULL, pszSource);
+ crRecDumpStrByLine(pRec->pDumper, pszSource, length);
+
+ crFree(pszSource);
+
+ crDmpStr(pRec->pDumper, "===END SHADER====");
+}
+
+void crRecDumpProgram(CR_RECORDER *pRec, CRContext *ctx, GLint id, GLint hwid)
+{
+ GLint cShaders = 0, linkStatus = 0;
+ char *source = NULL;
+ CRGLSLProgram *pProg;
+
+ if (!id)
+ {
+ unsigned long tstKey = 0;
+ Assert(hwid);
+ pProg = (CRGLSLProgram*)crDmpHashtableSearchByHwid(ctx->glsl.programs, hwid, crDmpGetHwidProgramCB, &tstKey);
+ Assert(pProg);
+ if (!pProg)
+ return;
+ id = pProg->id;
+ Assert(tstKey == id);
+ }
+ else
+ {
+ pProg = (CRGLSLProgram *) crHashtableSearch(ctx->glsl.programs, id);
+ Assert(pProg);
+ if (!pProg)
+ return;
+ }
+
+ if (!hwid)
+ hwid = pProg->hwid;
+
+ Assert(pProg->hwid == hwid);
+ Assert(pProg->id == id);
+
+ pRec->pDispatch->GetObjectParameterivARB(hwid, GL_OBJECT_ATTACHED_OBJECTS_ARB, &cShaders);
+ pRec->pDispatch->GetObjectParameterivARB(hwid, GL_OBJECT_LINK_STATUS_ARB, &linkStatus);
+
+ crDmpStrF(pRec->pDumper, "PROGRAM ctx(%d) id(%d) hwid(%d) status(%d) shaders(%d):", ctx->id, id, hwid, linkStatus, cShaders);
+
+ crRecDumpLog(pRec, hwid);
+
+ VBoxGLhandleARB *pShaders = (VBoxGLhandleARB*)crCalloc(cShaders * sizeof (*pShaders));
+ if (!pShaders)
+ {
+ crWarning("crCalloc failed");
+ crDmpStrF(pRec->pDumper, "WARNING: crCalloc failed");
+ return;
+ }
+
+ pRec->pDispatch->GetAttachedObjectsARB(hwid, cShaders, NULL, pShaders);
+ for (GLint i = 0; i < cShaders; ++i)
+ {
+ if (pShaders[i])
+ crRecDumpShader(pRec, ctx, 0, pShaders[i]);
+ else
+ crDmpStrF(pRec->pDumper, "WARNING: Shader[%d] is null", i);
+ }
+
+ crFree(pShaders);
+
+ GLsizei cbLog = 0;
+
+ pRec->pDispatch->GetObjectParameterivARB(hwid, GL_OBJECT_INFO_LOG_LENGTH_ARB, &cbLog);
+ if (cbLog)
+ {
+ char *pszLog = (char *)crCalloc(cbLog+1);
+ pRec->pDispatch->GetInfoLogARB(hwid, cbLog, NULL, pszLog);
+ crDmpStrF(pRec->pDumper, "==LOG==");
+ crRecDumpStrByLine(pRec->pDumper, pszLog, cbLog);
+ crDmpStrF(pRec->pDumper, "==Done LOG==");
+ crFree(pszLog);
+ }
+ else
+ {
+ crDmpStrF(pRec->pDumper, "==No LOG==");
+ }
+
+ crDmpStr(pRec->pDumper, "===END PROGRAM====");
+}
+
+void crRecRecompileShader(CR_RECORDER *pRec, CRContext *ctx, GLint id, GLint hwid)
+{
+ GLint length = 0;
+ GLint type = 0;
+ GLint compileStatus = 0;
+ CRGLSLShader *pShad;
+
+ if (!id)
+ {
+ unsigned long tstKey = 0;
+ Assert(hwid);
+ pShad = (CRGLSLShader *)crDmpHashtableSearchByHwid(ctx->glsl.shaders, hwid, crDmpGetHwidShaderCB, &tstKey);
+ Assert(pShad);
+ if (!pShad)
+ return;
+ id = pShad->id;
+ Assert(tstKey == id);
+ }
+ else
+ {
+ pShad = (CRGLSLShader *)crHashtableSearch(ctx->glsl.shaders, id);
+ Assert(pShad);
+ if (!pShad)
+ return;
+ }
+
+ if (!hwid)
+ hwid = pShad->hwid;
+
+ Assert(pShad->hwid == hwid);
+ Assert(pShad->id == id);
+
+ pRec->pDispatch->GetObjectParameterivARB(hwid, GL_OBJECT_SUBTYPE_ARB, &type);
+ pRec->pDispatch->GetObjectParameterivARB(hwid, GL_OBJECT_COMPILE_STATUS_ARB, &compileStatus);
+ crDmpStrF(pRec->pDumper, "==RECOMPILE SHADER ctx(%d) id(%d) hwid(%d) type(%s) status(%d)==", ctx->id, id, hwid, crRecDumpShaderTypeString(type, pRec->pDumper), compileStatus);
+
+ compileStatus = 0;
+ GLenum status;
+ while ((status = pRec->pDispatch->GetError()) != GL_NO_ERROR) {/*Assert(0);*/}
+ pRec->pDispatch->CompileShader(hwid);
+ while ((status = pRec->pDispatch->GetError()) != GL_NO_ERROR) {Assert(0);}
+ pRec->pDispatch->GetObjectParameterivARB(hwid, GL_OBJECT_COMPILE_STATUS_ARB, &compileStatus);
+
+ crDmpStrF(pRec->pDumper, "==Done RECOMPILE SHADER, status(%d)==", compileStatus);
+}
+
+void crRecRecompileProgram(CR_RECORDER *pRec, CRContext *ctx, GLint id, GLint hwid)
+{
+ GLint cShaders = 0, linkStatus = 0;
+ char *source = NULL;
+ CRGLSLProgram *pProg;
+
+ if (!id)
+ {
+ unsigned long tstKey = 0;
+ Assert(hwid);
+ pProg = (CRGLSLProgram*)crDmpHashtableSearchByHwid(ctx->glsl.programs, hwid, crDmpGetHwidProgramCB, &tstKey);
+ Assert(pProg);
+ if (!pProg)
+ return;
+ id = pProg->id;
+ Assert(tstKey == id);
+ }
+ else
+ {
+ pProg = (CRGLSLProgram *) crHashtableSearch(ctx->glsl.programs, id);
+ Assert(pProg);
+ if (!pProg)
+ return;
+ }
+
+ if (!hwid)
+ hwid = pProg->hwid;
+
+ Assert(pProg->hwid == hwid);
+ Assert(pProg->id == id);
+
+ pRec->pDispatch->GetObjectParameterivARB(hwid, GL_OBJECT_ATTACHED_OBJECTS_ARB, &cShaders);
+ pRec->pDispatch->GetObjectParameterivARB(hwid, GL_OBJECT_LINK_STATUS_ARB, &linkStatus);
+
+ crDmpStrF(pRec->pDumper, "==RECOMPILE PROGRAM ctx(%d) id(%d) hwid(%d) status(%d) shaders(%d)==", ctx->id, id, hwid, linkStatus, cShaders);
+
+ VBoxGLhandleARB *pShaders = (VBoxGLhandleARB*)crCalloc(cShaders * sizeof (*pShaders));
+ if (!pShaders)
+ {
+ crWarning("crCalloc failed");
+ crDmpStrF(pRec->pDumper, "WARNING: crCalloc failed");
+ return;
+ }
+
+ pRec->pDispatch->GetAttachedObjectsARB(hwid, cShaders, NULL, pShaders);
+ for (GLint i = 0; i < cShaders; ++i)
+ {
+ crRecRecompileShader(pRec, ctx, 0, pShaders[i]);
+ }
+
+ crFree(pShaders);
+
+ linkStatus = 0;
+ GLenum status;
+ while ((status = pRec->pDispatch->GetError()) != GL_NO_ERROR) {/*Assert(0);*/}
+ pRec->pDispatch->LinkProgram(hwid);
+ while ((status = pRec->pDispatch->GetError()) != GL_NO_ERROR) {Assert(0);}
+ pRec->pDispatch->GetObjectParameterivARB(hwid, GL_OBJECT_LINK_STATUS_ARB, &linkStatus);
+
+ crDmpStrF(pRec->pDumper, "==Done RECOMPILE PROGRAM, status(%d)==", linkStatus);
+}
+
+VBOXDUMPDECL(void) crRecDumpCurrentProgram(CR_RECORDER *pRec, CRContext *ctx)
+{
+ GLint curProgram = 0;
+ pRec->pDispatch->GetIntegerv(GL_CURRENT_PROGRAM, &curProgram);
+ if (curProgram)
+ {
+ Assert(ctx->glsl.activeProgram);
+ if (!ctx->glsl.activeProgram)
+ crWarning("no active program state with active hw program");
+ else
+ Assert(ctx->glsl.activeProgram->hwid == curProgram);
+ crRecDumpProgram(pRec, ctx, 0, curProgram);
+ }
+ else
+ {
+ Assert(!ctx->glsl.activeProgram);
+ crDmpStrF(pRec->pDumper, "--no active program");
+ }
+}
+
+void crRecDumpProgramUniforms(CR_RECORDER *pRec, CRContext *ctx, GLint id, GLint hwid)
+{
+ CRGLSLProgram *pProg;
+
+ if (!id)
+ {
+ unsigned long tstKey = 0;
+ Assert(hwid);
+ pProg = (CRGLSLProgram*)crDmpHashtableSearchByHwid(ctx->glsl.programs, hwid, crDmpGetHwidProgramCB, &tstKey);
+ Assert(pProg);
+ if (!pProg)
+ return;
+ id = pProg->id;
+ Assert(tstKey == id);
+ }
+ else
+ {
+ pProg = (CRGLSLProgram *) crHashtableSearch(ctx->glsl.programs, id);
+ Assert(pProg);
+ if (!pProg)
+ return;
+ }
+
+ if (!hwid)
+ hwid = pProg->hwid;
+
+ Assert(pProg->hwid == hwid);
+ Assert(pProg->id == id);
+
+ GLint maxUniformLen = 0, activeUniforms = 0, i, j, uniformsCount = 0;
+ GLenum type;
+ GLint size, location;
+ GLchar *pszName = NULL;
+ pRec->pDispatch->GetProgramiv(hwid, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformLen);
+ pRec->pDispatch->GetProgramiv(hwid, GL_ACTIVE_UNIFORMS, &activeUniforms);
+
+ if (!maxUniformLen)
+ {
+ if (activeUniforms)
+ {
+ crWarning("activeUniforms (%d), while maxUniformLen is zero", activeUniforms);
+ activeUniforms = 0;
+ }
+ }
+
+ if (activeUniforms>0)
+ {
+ pszName = (GLchar *) crAlloc((maxUniformLen+8)*sizeof(GLchar));
+
+ if (!pszName)
+ {
+ crWarning("crRecDumpProgramUniforms: out of memory");
+ return;
+ }
+ }
+
+ for (i=0; i<activeUniforms; ++i)
+ {
+ pRec->pDispatch->GetActiveUniform(hwid, i, maxUniformLen, NULL, &size, &type, pszName);
+ uniformsCount += size;
+ }
+ Assert(uniformsCount>=activeUniforms);
+
+ if (activeUniforms>0)
+ {
+ GLfloat fdata[16];
+ GLint idata[16];
+ char *pIndexStr=NULL;
+
+ for (i=0; i<activeUniforms; ++i)
+ {
+ bool fPrintBraketsWithName = false;
+ pRec->pDispatch->GetActiveUniform(hwid, i, maxUniformLen, NULL, &size, &type, pszName);
+
+ if (size>1)
+ {
+ pIndexStr = crStrchr(pszName, '[');
+ if (!pIndexStr)
+ {
+ pIndexStr = pszName+crStrlen(pszName);
+ fPrintBraketsWithName = true;
+ }
+ }
+
+ if (fPrintBraketsWithName)
+ {
+ crDmpStrF(pRec->pDumper, "%s %s[%d];", crRecDumpVarTypeString(type, pRec->pDumper), pszName, size);
+ Assert(size > 1);
+ }
+ else
+ crDmpStrF(pRec->pDumper, "%s %s;", crRecDumpVarTypeString(type, pRec->pDumper), pszName);
+
+ GLint uniformTypeSize = crStateGetUniformSize(type);
+ Assert(uniformTypeSize >= 1);
+
+ for (j=0; j<size; ++j)
+ {
+ if (size>1)
+ {
+ sprintf(pIndexStr, "[%i]", j);
+ }
+ location = pRec->pDispatch->GetUniformLocation(hwid, pszName);
+
+ if (crStateIsIntUniform(type))
+ {
+ pRec->pDispatch->GetUniformiv(hwid, location, &idata[0]);
+ switch (uniformTypeSize)
+ {
+ case 1:
+ crDmpStrF(pRec->pDumper, "%s = %d; //location %d", pszName, idata[0], location);
+ break;
+ case 2:
+ crDmpStrF(pRec->pDumper, "%s = {%d, %d}; //location %d", pszName, idata[0], idata[1], location);
+ break;
+ case 3:
+ crDmpStrF(pRec->pDumper, "%s = {%d, %d, %d}; //location %d", pszName, idata[0], idata[1], idata[2], location);
+ break;
+ case 4:
+ crDmpStrF(pRec->pDumper, "%s = {%d, %d, %d, %d}; //location %d", pszName, idata[0], idata[1], idata[2], idata[3], location);
+ break;
+ default:
+ for (GLint k = 0; k < uniformTypeSize; ++k)
+ {
+ crDmpStrF(pRec->pDumper, "%s[%d] = %d; //location %d", pszName, k, idata[k], location);
+ }
+ break;
+ }
+ }
+ else
+ {
+ pRec->pDispatch->GetUniformfv(hwid, location, &fdata[0]);
+ switch (uniformTypeSize)
+ {
+ case 1:
+ crDmpStrF(pRec->pDumper, "%s = %f; //location %d", pszName, fdata[0], location);
+ break;
+ case 2:
+ crDmpStrF(pRec->pDumper, "%s = {%f, %f}; //location %d", pszName, fdata[0], fdata[1], location);
+ break;
+ case 3:
+ crDmpStrF(pRec->pDumper, "%s = {%f, %f, %f}; //location %d", pszName, fdata[0], fdata[1], fdata[2], location);
+ break;
+ case 4:
+ crDmpStrF(pRec->pDumper, "%s = {%f, %f, %f, %f}; //location %d", pszName, fdata[0], fdata[1], fdata[2], fdata[3], location);
+ break;
+ default:
+ for (GLint k = 0; k < uniformTypeSize; ++k)
+ {
+ crDmpStrF(pRec->pDumper, "%s[%d] = %f; //location %d", pszName, k, fdata[k], location);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ crFree(pszName);
+ }
+}
+
+void crRecDumpProgramAttribs(CR_RECORDER *pRec, CRContext *ctx, GLint id, GLint hwid)
+{
+ CRGLSLProgram *pProg;
+
+ if (!id)
+ {
+ unsigned long tstKey = 0;
+ Assert(hwid);
+ pProg = (CRGLSLProgram*)crDmpHashtableSearchByHwid(ctx->glsl.programs, hwid, crDmpGetHwidProgramCB, &tstKey);
+ Assert(pProg);
+ if (!pProg)
+ return;
+ id = pProg->id;
+ Assert(tstKey == id);
+ }
+ else
+ {
+ pProg = (CRGLSLProgram *) crHashtableSearch(ctx->glsl.programs, id);
+ Assert(pProg);
+ if (!pProg)
+ return;
+ }
+
+ if (!hwid)
+ hwid = pProg->hwid;
+
+ Assert(pProg->hwid == hwid);
+ Assert(pProg->id == id);
+
+ GLint maxAttribLen = 0, activeAttrib = 0, i, j, attribCount = 0;
+ GLenum type;
+ GLint size, location;
+ GLchar *pszName = NULL;
+ pRec->pDispatch->GetProgramiv(hwid, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxAttribLen);
+ pRec->pDispatch->GetProgramiv(hwid, GL_ACTIVE_ATTRIBUTES, &activeAttrib);
+
+ if (!maxAttribLen)
+ {
+ if (activeAttrib)
+ {
+ crWarning("activeAttrib (%d), while maxAttribLen is zero", activeAttrib);
+ activeAttrib = 0;
+ }
+ }
+
+ if (activeAttrib>0)
+ {
+ pszName = (GLchar *) crAlloc((maxAttribLen+8)*sizeof(GLchar));
+
+ if (!pszName)
+ {
+ crWarning("crRecDumpProgramAttrib: out of memory");
+ return;
+ }
+ }
+
+ for (i=0; i<activeAttrib; ++i)
+ {
+ pRec->pDispatch->GetActiveAttrib(hwid, i, maxAttribLen, NULL, &size, &type, pszName);
+ attribCount += size;
+ }
+ Assert(attribCount>=activeAttrib);
+
+ if (activeAttrib>0)
+ {
+ GLfloat fdata[16];
+ GLint idata[16];
+ char *pIndexStr=NULL;
+
+ for (i=0; i<activeAttrib; ++i)
+ {
+ bool fPrintBraketsWithName = false;
+ pRec->pDispatch->GetActiveAttrib(hwid, i, maxAttribLen, NULL, &size, &type, pszName);
+ GLint arrayBufferBind = 0, arrayEnabled = 0, arraySize = 0, arrayStride = 0, arrayType = 0, arrayNormalized = 0, arrayInteger = 0/*, arrayDivisor = 0*/;
+
+ pRec->pDispatch->GetVertexAttribivARB(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &arrayBufferBind);
+ pRec->pDispatch->GetVertexAttribivARB(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &arrayEnabled);
+ pRec->pDispatch->GetVertexAttribivARB(i, GL_VERTEX_ATTRIB_ARRAY_SIZE, &arraySize);
+ pRec->pDispatch->GetVertexAttribivARB(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &arrayStride);
+ pRec->pDispatch->GetVertexAttribivARB(i, GL_VERTEX_ATTRIB_ARRAY_TYPE, &arrayType);
+ pRec->pDispatch->GetVertexAttribivARB(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &arrayNormalized);
+ pRec->pDispatch->GetVertexAttribivARB(i, GL_VERTEX_ATTRIB_ARRAY_INTEGER, &arrayInteger);
+// pRec->pDispatch->GetVertexAttribivARB(i, GL_VERTEX_ATTRIB_ARRAY_DIVISOR, &arrayDivisor);
+
+ if (size>1)
+ {
+ pIndexStr = crStrchr(pszName, '[');
+ if (!pIndexStr)
+ {
+ pIndexStr = pszName+crStrlen(pszName);
+ fPrintBraketsWithName = true;
+ }
+ }
+
+ if (fPrintBraketsWithName)
+ {
+ crDmpStrF(pRec->pDumper, "%s %s[%d];", crRecDumpVarTypeString(type, pRec->pDumper), pszName, size);
+ Assert(size > 1);
+ }
+ else
+ crDmpStrF(pRec->pDumper, "%s %s;", crRecDumpVarTypeString(type, pRec->pDumper), pszName);
+
+ crDmpStrF(pRec->pDumper, "Array buff(%d), enabled(%d) size(%d), stride(%d), type(%s), normalized(%d), integer(%d)", arrayBufferBind, arrayEnabled, arraySize, arrayStride, crRecDumpVarTypeString(arrayType, pRec->pDumper), arrayNormalized, arrayInteger);
+
+ GLint attribTypeSize = crStateGetUniformSize(type);
+ Assert(attribTypeSize >= 1);
+
+ for (j=0; j<size; ++j)
+ {
+ if (size>1)
+ {
+ sprintf(pIndexStr, "[%i]", j);
+ }
+ location = pRec->pDispatch->GetAttribLocation(hwid, pszName);
+
+ if (crStateIsIntUniform(type))
+ {
+ pRec->pDispatch->GetVertexAttribivARB(location, GL_CURRENT_VERTEX_ATTRIB, &idata[0]);
+ switch (attribTypeSize)
+ {
+ case 1:
+ crDmpStrF(pRec->pDumper, "%s = %d; //location %d", pszName, idata[0], location);
+ break;
+ case 2:
+ crDmpStrF(pRec->pDumper, "%s = {%d, %d}; //location %d", pszName, idata[0], idata[1], location);
+ break;
+ case 3:
+ crDmpStrF(pRec->pDumper, "%s = {%d, %d, %d}; //location %d", pszName, idata[0], idata[1], idata[2], location);
+ break;
+ case 4:
+ crDmpStrF(pRec->pDumper, "%s = {%d, %d, %d, %d}; //location %d", pszName, idata[0], idata[1], idata[2], idata[3], location);
+ break;
+ default:
+ for (GLint k = 0; k < attribTypeSize; ++k)
+ {
+ crDmpStrF(pRec->pDumper, "%s[%d] = %d; //location %d", pszName, k, idata[k], location);
+ }
+ break;
+ }
+ }
+ else
+ {
+ pRec->pDispatch->GetVertexAttribfvARB(location, GL_CURRENT_VERTEX_ATTRIB, &fdata[0]);
+ switch (attribTypeSize)
+ {
+ case 1:
+ crDmpStrF(pRec->pDumper, "%s = %f; //location %d", pszName, fdata[0], location);
+ break;
+ case 2:
+ crDmpStrF(pRec->pDumper, "%s = {%f, %f}; //location %d", pszName, fdata[0], fdata[1], location);
+ break;
+ case 3:
+ crDmpStrF(pRec->pDumper, "%s = {%f, %f, %f}; //location %d", pszName, fdata[0], fdata[1], fdata[2], location);
+ break;
+ case 4:
+ crDmpStrF(pRec->pDumper, "%s = {%f, %f, %f, %f}; //location %d", pszName, fdata[0], fdata[1], fdata[2], fdata[3], location);
+ break;
+ default:
+ for (GLint k = 0; k < attribTypeSize; ++k)
+ {
+ crDmpStrF(pRec->pDumper, "%s[%d] = %f; //location %d", pszName, k, fdata[k], location);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ crFree(pszName);
+ }
+}
+
+VBOXDUMPDECL(void) crRecDumpCurrentProgramUniforms(CR_RECORDER *pRec, CRContext *ctx)
+{
+ GLint curProgram = 0;
+ pRec->pDispatch->GetIntegerv(GL_CURRENT_PROGRAM, &curProgram);
+ if (curProgram)
+ {
+ Assert(ctx->glsl.activeProgram);
+ if (!ctx->glsl.activeProgram)
+ crWarning("no active program state with active hw program");
+ else
+ Assert(ctx->glsl.activeProgram->hwid == curProgram);
+ crRecDumpProgramUniforms(pRec, ctx, 0, curProgram);
+ }
+ else
+ {
+ Assert(!ctx->glsl.activeProgram);
+ crDmpStrF(pRec->pDumper, "--no active program");
+ }
+}
+
+VBOXDUMPDECL(void) crRecDumpCurrentProgramAttribs(CR_RECORDER *pRec, CRContext *ctx)
+{
+ GLint curProgram = 0;
+ pRec->pDispatch->GetIntegerv(GL_CURRENT_PROGRAM, &curProgram);
+ if (curProgram)
+ {
+ Assert(ctx->glsl.activeProgram);
+ if (!ctx->glsl.activeProgram)
+ crWarning("no active program state with active hw program");
+ else
+ Assert(ctx->glsl.activeProgram->hwid == curProgram);
+ crRecDumpProgramAttribs(pRec, ctx, 0, curProgram);
+ }
+ else
+ {
+ Assert(!ctx->glsl.activeProgram);
+ crDmpStrF(pRec->pDumper, "--no active program");
+ }
+}
+
+VBOXDUMPDECL(void) crRecRecompileCurrentProgram(CR_RECORDER *pRec, CRContext *ctx)
+{
+ GLint curProgram = 0;
+ pRec->pDispatch->GetIntegerv(GL_CURRENT_PROGRAM, &curProgram);
+ if (curProgram)
+ {
+ Assert(ctx->glsl.activeProgram);
+ if (!ctx->glsl.activeProgram)
+ crWarning("no active program state with active hw program");
+ else
+ Assert(ctx->glsl.activeProgram->hwid == curProgram);
+ crRecRecompileProgram(pRec, ctx, 0, curProgram);
+ }
+ else
+ {
+ Assert(!ctx->glsl.activeProgram);
+ crDmpStrF(pRec->pDumper, "--no active program");
+ }
+}
+
+int crRecAlphaImgCreate(const CR_BLITTER_IMG *pImg, CR_BLITTER_IMG *pAlphaImg)
+{
+ if (pImg->enmFormat != GL_RGBA
+ && pImg->enmFormat != GL_BGRA)
+ {
+ crWarning("unsupported format 0x%x", pImg->enmFormat);
+ return VERR_NOT_IMPLEMENTED;
+ }
+
+ pAlphaImg->bpp = 32;
+ pAlphaImg->pitch = pImg->width * 4;
+ pAlphaImg->cbData = pAlphaImg->pitch * pImg->height;
+ pAlphaImg->enmFormat = GL_BGRA;
+ pAlphaImg->width = pImg->width;
+ pAlphaImg->height = pImg->height;
+
+ pAlphaImg->pvData = RTMemAlloc(pAlphaImg->cbData);
+ if (!pAlphaImg->pvData)
+ {
+ crWarning("RTMemAlloc failed");
+ return VERR_NO_MEMORY;
+ }
+
+ uint8_t *pu8SrcBuf = (uint8_t*)pImg->pvData;
+ uint8_t *pu8DstBuf = (uint8_t*)pAlphaImg->pvData;
+ for (uint32_t ih = 0; ih < pAlphaImg->height; ++ih)
+ {
+ uint32_t *pu32SrcBuf = (uint32_t*)pu8SrcBuf;
+ uint32_t *pu32DstBuf = (uint32_t*)pu8DstBuf;
+ for (uint32_t iw = 0; iw < pAlphaImg->width; ++iw)
+ {
+ uint8_t alpha = (((*pu32SrcBuf) >> 24) & 0xff);
+ *pu32DstBuf = (0xff << 24) || (alpha << 16) || (alpha << 8) || alpha;
+ ++pu32SrcBuf;
+ ++pu32DstBuf;
+ }
+ pu8SrcBuf += pImg->pitch;
+ pu8DstBuf += pAlphaImg->pitch;
+ }
+
+ return VINF_SUCCESS;
+}
+
+void crRecAlphaImgDestroy(CR_BLITTER_IMG *pImg)
+{
+ RTMemFree(pImg->pvData);
+ pImg->pvData = NULL;
+}
+
+void crRecDumpTextureV(CR_RECORDER *pRec, const VBOXVR_TEXTURE *pTex, const char *pszStr, va_list pArgList)
+{
+ CR_BLITTER_IMG Img = {0};
+ int rc = CrBltEnter(pRec->pBlitter);
+ if (RT_SUCCESS(rc))
+ {
+ rc = CrBltImgGetTex(pRec->pBlitter, pTex, GL_BGRA, &Img);
+ if (RT_SUCCESS(rc))
+ {
+ crDmpImgV(pRec->pDumper, &Img, pszStr, pArgList);
+ if (g_CrDbgDumpAlphaData)
+ {
+ CR_BLITTER_IMG AlphaImg = {0};
+ rc = crRecAlphaImgCreate(&Img, &AlphaImg);
+ if (RT_SUCCESS(rc))
+ {
+ crDmpImgF(pRec->pDumper, &AlphaImg, "Texture ALPHA Data");
+ crRecAlphaImgDestroy(&AlphaImg);
+ }
+ else
+ {
+ crWarning("crRecAlphaImgCreate failed rc %d", rc);
+ }
+ }
+ CrBltImgFree(pRec->pBlitter, &Img);
+ }
+ else
+ {
+ crWarning("CrBltImgGetTex failed, rc %d", rc);
+ }
+ CrBltLeave(pRec->pBlitter);
+ }
+ else
+ {
+ crWarning("CrBltEnter failed, rc %d", rc);
+ }
+}
+
+void crRecDumpTextureF(CR_RECORDER *pRec, const VBOXVR_TEXTURE *pTex, const char *pszStr, ...)
+{
+ va_list pArgList;
+ va_start(pArgList, pszStr);
+ crRecDumpTextureV(pRec, pTex, pszStr, pArgList);
+ va_end(pArgList);
+}
+
+void crRecDumpTextureByIdV(CR_RECORDER *pRec, CRContext *ctx, GLint id, const char *pszStr, va_list pArgList)
+{
+ CRTextureObj *pTobj = (CRTextureObj *)crHashtableSearch(ctx->shared->textureTable, id);
+ if (!pTobj)
+ {
+ crWarning("no texture of id %d", id);
+ return;
+ }
+
+ CRTextureLevel *pTl = &pTobj->level[0][0 /* level */];
+ VBOXVR_TEXTURE Tex;
+ Tex.width = pTl->width;
+ Tex.height = pTl->height;
+ Tex.target = pTobj->target;
+ Assert(Tex.target == GL_TEXTURE_2D);
+ Tex.hwid = pTobj->hwid;
+ if (!Tex.hwid)
+ {
+ crWarning("no texture hwid of id %d", id);
+ return;
+ }
+
+ crRecDumpTextureV(pRec, &Tex, pszStr, pArgList);
+}
+
+void crRecDumpTextureByIdF(CR_RECORDER *pRec, CRContext *ctx, GLint id, const char *pszStr, ...)
+{
+ va_list pArgList;
+ va_start(pArgList, pszStr);
+ crRecDumpTextureByIdV(pRec, ctx, id, pszStr, pArgList);
+ va_end(pArgList);
+}
+
+void crRecDumpTextures(CR_RECORDER *pRec, CRContext *ctx)
+{
+ GLint maxUnits = 0;
+ GLint curTexUnit = 0;
+ GLint restoreTexUnit = 0;
+ GLint curProgram = 0;
+ int i;
+
+ pRec->pDispatch->GetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxUnits);
+ maxUnits = RT_MIN(CR_MAX_TEXTURE_UNITS, maxUnits);
+
+ pRec->pDispatch->GetIntegerv(GL_CURRENT_PROGRAM, &curProgram);
+ Assert(curProgram);
+ Assert(ctx->glsl.activeProgram && ctx->glsl.activeProgram->hwid == curProgram);
+
+ Assert(maxUnits);
+ pRec->pDispatch->GetIntegerv(GL_ACTIVE_TEXTURE, &curTexUnit);
+ restoreTexUnit = curTexUnit;
+ Assert(curTexUnit >= GL_TEXTURE0);
+ Assert(curTexUnit < GL_TEXTURE0 + maxUnits);
+
+ Assert(ctx->texture.curTextureUnit == restoreTexUnit - GL_TEXTURE0);
+
+ for (i = 0; i < maxUnits; ++i)
+ {
+ GLboolean enabled1D;
+ GLboolean enabled2D;
+ GLboolean enabled3D;
+ GLboolean enabledCubeMap;
+ GLboolean enabledRect;
+ CRTextureUnit *tu = &ctx->texture.unit[i];
+
+ if (i > 1)
+ break;
+
+ if (curTexUnit != i + GL_TEXTURE0)
+ {
+ pRec->pDispatch->ActiveTextureARB(i + GL_TEXTURE0);
+ curTexUnit = i + GL_TEXTURE0;
+ }
+
+ enabled1D = pRec->pDispatch->IsEnabled(GL_TEXTURE_1D);
+ enabled2D = pRec->pDispatch->IsEnabled(GL_TEXTURE_2D);
+ enabled3D = pRec->pDispatch->IsEnabled(GL_TEXTURE_3D);
+ enabledCubeMap = pRec->pDispatch->IsEnabled(GL_TEXTURE_CUBE_MAP_ARB);
+ enabledRect = pRec->pDispatch->IsEnabled(GL_TEXTURE_RECTANGLE_NV);
+
+ Assert(enabled1D == tu->enabled1D);
+ Assert(enabled2D == tu->enabled2D);
+ Assert(enabled3D == tu->enabled3D);
+ Assert(enabledCubeMap == tu->enabledCubeMap);
+ Assert(enabledRect == tu->enabledRect);
+
+ if (enabled1D)
+ {
+ crWarning("GL_TEXTURE_1D: unsupported");
+ }
+
+// if (enabled2D)
+ {
+ GLint hwTex = 0;
+ VBOXVR_TEXTURE Tex;
+
+ GLint width = 0, height = 0, depth = 0;
+ CRTextureObj *pTobj = tu->currentTexture2D;
+
+ pRec->pDispatch->GetIntegerv(GL_TEXTURE_BINDING_2D, &hwTex);
+ if (hwTex)
+ {
+ CRTextureLevel *pTl = &pTobj->level[0][0 /* level */];
+ Assert(pTobj
+ && pTobj->hwid == hwTex);
+
+ pRec->pDispatch->GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
+ pRec->pDispatch->GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
+ pRec->pDispatch->GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_DEPTH, &depth);
+
+ Assert(width == pTl->width);
+ Assert(height == pTl->height);
+ Assert(depth == pTl->depth);
+
+ Tex.width = width;
+ Tex.height = height;
+ Tex.target = GL_TEXTURE_2D;
+ Tex.hwid = hwTex;
+
+ if (g_CrDbgDumpRecTexInfo)
+ {
+ crRecDumpTexParam(pRec, ctx, GL_TEXTURE_2D);
+ crRecDumpTexEnv(pRec, ctx);
+ crRecDumpTexGen(pRec, ctx);
+ }
+
+ crRecDumpTextureF(pRec, &Tex, "ctx(%d), Unit %d: TEXTURE_2D id(%d) hwid(%d), width(%d), height(%d)", ctx, i, pTobj->id, pTobj->hwid, width, height);
+ }
+// else
+// {
+// Assert(!pTobj || pTobj->hwid == 0);
+// crWarning("no TEXTURE_2D bound!");
+// }
+ }
+#if 0
+ if (enabled3D)
+ {
+ crWarning("GL_TEXTURE_3D: unsupported");
+ }
+
+ if (enabledCubeMap)
+ {
+ crWarning("GL_TEXTURE_CUBE_MAP_ARB: unsupported");
+ }
+
+// if (enabledRect)
+ {
+ GLint hwTex = 0;
+ CR_BLITTER_IMG Img = {0};
+ VBOXVR_TEXTURE Tex;
+
+ GLint width = 0, height = 0, depth = 0;
+ CRTextureObj *pTobj = tu->currentTextureRect;
+
+ pRec->pDispatch->GetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_NV, &hwTex);
+ if (hwTex)
+ {
+ CRTextureLevel *pTl = &pTobj->level[0][0 /* level */];
+ Assert(pTobj
+ && pTobj->hwid == hwTex);
+
+ pRec->pDispatch->GetTexLevelParameteriv(GL_TEXTURE_RECTANGLE_NV, 0, GL_TEXTURE_WIDTH, &width);
+ pRec->pDispatch->GetTexLevelParameteriv(GL_TEXTURE_RECTANGLE_NV, 0, GL_TEXTURE_HEIGHT, &height);
+ pRec->pDispatch->GetTexLevelParameteriv(GL_TEXTURE_RECTANGLE_NV, 0, GL_TEXTURE_DEPTH, &depth);
+
+ Assert(width == pTl->width);
+ Assert(height == pTl->height);
+ Assert(depth == pTl->depth);
+
+ Tex.width = width;
+ Tex.height = height;
+ Tex.target = GL_TEXTURE_RECTANGLE_NV;
+ Tex.hwid = hwTex;
+
+ rc = CrBltEnter(pRec->pBlitter);
+ if (RT_SUCCESS(rc))
+ {
+ rc = CrBltImgGetTex(pRec->pBlitter, &Tex, GL_BGRA, &Img);
+ if (RT_SUCCESS(rc))
+ {
+ crDmpImgF(pRec->pDumper, &Img, "Unit %d: TEXTURE_RECTANGLE data", i);
+ CrBltImgFree(pRec->pBlitter, &Img);
+ }
+ else
+ {
+ crWarning("CrBltImgGetTex failed, rc %d", rc);
+ }
+ CrBltLeave(pRec->pBlitter);
+ }
+ else
+ {
+ crWarning("CrBltEnter failed, rc %d", rc);
+ }
+ }
+// else
+// {
+// Assert(!pTobj || pTobj->hwid == 0);
+// crWarning("no TEXTURE_RECTANGLE bound!");
+// }
+ }
+#endif
+ }
+
+ if (curTexUnit != restoreTexUnit)
+ {
+ pRec->pDispatch->ActiveTextureARB(restoreTexUnit);
+ curTexUnit = restoreTexUnit;
+ }
+}
+
+#ifdef RT_OS_WINDOWS
+static void crDmpPrint(const char* szString, ...)
+{
+ char szBuffer[4096] = {0};
+ va_list pArgList;
+ va_start(pArgList, szString);
+ RTStrPrintfV(szBuffer, sizeof (szBuffer), szString, pArgList);
+ va_end(pArgList);
+
+ OutputDebugStringA(szBuffer);
+}
+
+static void crDmpPrintDmlCmd(const char* pszDesc, const char* pszCmd)
+{
+ crDmpPrint("<?dml?><exec cmd=\"%s\">%s</exec>, ( %s )\n", pszCmd, pszDesc, pszCmd);
+}
+
+void crDmpPrintDumpDmlCmd(const char* pszDesc, const void *pvData, uint32_t width, uint32_t height, uint32_t bpp, uint32_t pitch)
+{
+ char Cmd[1024];
+ sprintf(Cmd, "!vbvdbg.ms 0x%p 0n%d 0n%d 0n%d 0n%d", pvData, width, height, bpp, pitch);
+ crDmpPrintDmlCmd(pszDesc, Cmd);
+}
+
+DECLCALLBACK(void) crDmpDumpImgDmlBreak(struct CR_DUMPER * pDumper, CR_BLITTER_IMG *pImg, const char*pszEntryDesc)
+{
+ crDmpPrintDumpDmlCmd(pszEntryDesc, pImg->pvData, pImg->width, pImg->height, pImg->bpp, pImg->pitch);
+ RT_BREAKPOINT();
+}
+
+DECLCALLBACK(void) crDmpDumpStrDbgPrint(struct CR_DUMPER * pDumper, const char*pszStr)
+{
+ crDmpPrint("%s\n", pszStr);
+}
+#endif
+
+static void crDmpHtmlDumpStrExact(struct CR_HTML_DUMPER * pDumper, const char *pszStr)
+{
+ fprintf(pDumper->pFile, "%s", pszStr);
+ fflush(pDumper->pFile);
+}
+
+static DECLCALLBACK(void) crDmpHtmlDumpStr(struct CR_DUMPER * pDumper, const char*pszStr)
+{
+ CR_HTML_DUMPER * pHtmlDumper = (CR_HTML_DUMPER*)pDumper;
+ fprintf(pHtmlDumper->pFile, "<pre>%s</pre>\n", pszStr);
+ fflush(pHtmlDumper->pFile);
+}
+
+static DECLCALLBACK(void) crDmpHtmlDumpImg(struct CR_DUMPER * pDumper, CR_BLITTER_IMG *pImg, const char*pszEntryDesc)
+{
+ CR_HTML_DUMPER * pHtmlDumper = (CR_HTML_DUMPER*)pDumper;
+ char szBuffer[4096] = {0};
+ size_t cbWritten = RTStrPrintf(szBuffer, sizeof(szBuffer), "%s/", pHtmlDumper->pszDir);
+ char *pszFileName = szBuffer + cbWritten;
+ RTStrPrintf(pszFileName, sizeof(szBuffer) - cbWritten, "img%d.bmp", ++pHtmlDumper->cImg);
+ crDmpImgBmp(pImg, szBuffer);
+ fprintf(pHtmlDumper->pFile, "<a href=\"%s\"><pre>%s</pre><img src=\"%s\" alt=\"%s\" width=\"150\" height=\"100\" /></a><br>\n",
+ pszFileName, pszEntryDesc, pszFileName, pszEntryDesc);
+ fflush(pHtmlDumper->pFile);
+}
+
+static void crDmpHtmlPrintHeader(struct CR_HTML_DUMPER * pDumper)
+{
+ fprintf(pDumper->pFile, "<html><body>\n");
+ fflush(pDumper->pFile);
+}
+
+static void crDmpHtmlPrintFooter(struct CR_HTML_DUMPER * pDumper)
+{
+ fprintf(pDumper->pFile, "</body></html>\n");
+ fflush(pDumper->pFile);
+}
+
+DECLEXPORT(bool) crDmpHtmlIsInited(struct CR_HTML_DUMPER * pDumper)
+{
+ return !!pDumper->pFile;
+}
+
+DECLEXPORT(void) crDmpHtmlTerm(struct CR_HTML_DUMPER * pDumper)
+{
+ crDmpHtmlPrintFooter(pDumper);
+ fclose (pDumper->pFile);
+ pDumper->pFile = NULL;
+}
+
+DECLEXPORT(int) crDmpHtmlInit(struct CR_HTML_DUMPER * pDumper, const char *pszDir, const char *pszFile)
+{
+ int rc = VERR_NO_MEMORY;
+ pDumper->Base.pfnDumpImg = crDmpHtmlDumpImg;
+ pDumper->Base.pfnDumpStr = crDmpHtmlDumpStr;
+ pDumper->cImg = 0;
+ pDumper->pszDir = crStrdup(pszDir);
+ if (pDumper->pszDir)
+ {
+ pDumper->pszFile = crStrdup(pszFile);
+ if (pDumper->pszFile)
+ {
+ char szBuffer[4096] = {0};
+ RTStrPrintf(szBuffer, sizeof(szBuffer), "%s/%s", pszDir, pszFile);
+
+ pDumper->pszFile = crStrdup(pszFile);
+ pDumper->pFile = fopen(szBuffer, "w");
+ if (pDumper->pFile)
+ {
+ crDmpHtmlPrintHeader(pDumper);
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ crWarning("open failed");
+ rc = VERR_OPEN_FAILED;
+ }
+ crFree((void*)pDumper->pszFile);
+ }
+ else
+ {
+ crWarning("open failed");
+ }
+ crFree((void*)pDumper->pszDir);
+ }
+ else
+ {
+ crWarning("open failed");
+ }
+ return rc;
+}
+
+DECLEXPORT(int) crDmpHtmlInitV(struct CR_HTML_DUMPER * pDumper, const char *pszDir, const char *pszFile, va_list pArgList)
+{
+ char szBuffer[4096] = {0};
+ vsprintf_s(szBuffer, sizeof (szBuffer), pszFile, pArgList);
+ return crDmpHtmlInit(pDumper, pszDir, szBuffer);
+}
+
+DECLEXPORT(int) crDmpHtmlInitF(struct CR_HTML_DUMPER * pDumper, const char *pszDir, const char *pszFile, ...)
+{
+ int rc;
+ va_list pArgList;
+ va_start(pArgList, pszFile);
+ rc = crDmpHtmlInitV(pDumper, pszDir, pszFile, pArgList);
+ va_end(pArgList);
+ return rc;
+}
+
+#endif