diff options
| author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2014-03-26 19:21:20 +0000 |
|---|---|---|
| committer | <> | 2014-05-08 15:03:54 +0000 |
| commit | fb123f93f9f5ce42c8e5785d2f8e0edaf951740e (patch) | |
| tree | c2103d76aec5f1f10892cd1d3a38e24f665ae5db /src/VBox/GuestHost/OpenGL/util | |
| parent | 58ed4748338f9466599adfc8a9171280ed99e23f (diff) | |
| download | VirtualBox-master.tar.gz | |
Imported from /home/lorry/working-area/delta_VirtualBox/VirtualBox-4.3.10.tar.bz2.HEADVirtualBox-4.3.10master
Diffstat (limited to 'src/VBox/GuestHost/OpenGL/util')
| -rw-r--r-- | src/VBox/GuestHost/OpenGL/util/blitter.cpp | 1751 | ||||
| -rw-r--r-- | src/VBox/GuestHost/OpenGL/util/bmpscale.cpp | 319 | ||||
| -rw-r--r-- | src/VBox/GuestHost/OpenGL/util/compositor.cpp | 1015 | ||||
| -rw-r--r-- | src/VBox/GuestHost/OpenGL/util/dll.c | 74 | ||||
| -rw-r--r-- | src/VBox/GuestHost/OpenGL/util/error.c | 27 | ||||
| -rw-r--r-- | src/VBox/GuestHost/OpenGL/util/hash.c | 492 | ||||
| -rw-r--r-- | src/VBox/GuestHost/OpenGL/util/htable.cpp | 195 | ||||
| -rw-r--r-- | src/VBox/GuestHost/OpenGL/util/net.c | 18 | ||||
| -rw-r--r-- | src/VBox/GuestHost/OpenGL/util/pixel.c | 20 | ||||
| -rw-r--r-- | src/VBox/GuestHost/OpenGL/util/string.c | 123 | ||||
| -rw-r--r-- | src/VBox/GuestHost/OpenGL/util/util.def | 4 | ||||
| -rw-r--r-- | src/VBox/GuestHost/OpenGL/util/vboxhgcm.c | 149 | ||||
| -rw-r--r-- | src/VBox/GuestHost/OpenGL/util/vboxhgsmi.c | 2 | ||||
| -rw-r--r-- | src/VBox/GuestHost/OpenGL/util/vreg.cpp | 1692 |
14 files changed, 5535 insertions, 346 deletions
diff --git a/src/VBox/GuestHost/OpenGL/util/blitter.cpp b/src/VBox/GuestHost/OpenGL/util/blitter.cpp new file mode 100644 index 00000000..a14c0916 --- /dev/null +++ b/src/VBox/GuestHost/OpenGL/util/blitter.cpp @@ -0,0 +1,1751 @@ +/* $Id$ */ + +/** @file + * Blitter API implementation + */ +/* + * Copyright (C) 2013 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 <iprt/cdefs.h> +#include <iprt/types.h> +#include <iprt/mem.h> + +/* @param pCtxBase - contains the blitter context info. Its value is treated differently depending on the fCreateNewCtx value + * @param fCreateNewCtx - if true - the pCtxBase must NOT be NULL. its visualBits is used as a visual bits info for the new context, + * its id field is used to specified the shared context id to be used for blitter context. + * The id can be null to specify no shared context is needed + * if false - if pCtxBase is NOT null AND its id field is NOT null - + * specified the blitter context to be used + * blitter treats it as if it has default ogl state. + * otherwise - + * the blitter works in a "no-context" mode, i.e. a caller is responsible + * to making a proper context current before calling the blitter. + * Note that BltEnter/Leave MUST still be called, but the proper context + * must be set before doing BltEnter, and ResoreContext info is ignored in that case. + * Also note that blitter caches the current window info, and assumes the current context's values are preserved + * wrt that window before the calls, so if one uses different contexts for one blitter, + * the blitter current window values must be explicitly reset by doing CrBltMuralSetCurrentInfo(pBlitter, NULL) + * @param fForceDrawBlt - if true - forces the blitter to always use glDrawXxx-based blits even if GL_EXT_framebuffer_blit. + * This is needed because BlitFramebufferEXT is known to be often buggy, and glDrawXxx-based blits appear to be more reliable + */ +VBOXBLITTERDECL(int) CrBltInit(PCR_BLITTER pBlitter, const CR_BLITTER_CONTEXT *pCtxBase, bool fCreateNewCtx, bool fForceDrawBlt, const CR_GLSL_CACHE *pShaders, SPUDispatchTable *pDispatch) +{ + if (pCtxBase && pCtxBase->Base.id < 0) + { + crWarning("Default share context not initialized!"); + return VERR_INVALID_PARAMETER; + } + + if (!pCtxBase && fCreateNewCtx) + { + crWarning("pCtxBase is zero while fCreateNewCtx is set!"); + return VERR_INVALID_PARAMETER; + } + + memset(pBlitter, 0, sizeof (*pBlitter)); + + pBlitter->pDispatch = pDispatch; + if (pCtxBase) + pBlitter->CtxInfo = *pCtxBase; + + pBlitter->Flags.ForceDrawBlit = fForceDrawBlt; + + if (fCreateNewCtx) + { + pBlitter->CtxInfo.Base.id = pDispatch->CreateContext("", pCtxBase->Base.visualBits, pCtxBase->Base.id); + if (!pBlitter->CtxInfo.Base.id) + { + memset(pBlitter, 0, sizeof (*pBlitter)); + crWarning("CreateContext failed!"); + return VERR_GENERAL_FAILURE; + } + pBlitter->Flags.CtxCreated = 1; + } + + if (pShaders) + { + pBlitter->pGlslCache = pShaders; + pBlitter->Flags.ShadersGloal = 1; + } + else + { + CrGlslInit(&pBlitter->LocalGlslCache, pDispatch); + pBlitter->pGlslCache = &pBlitter->LocalGlslCache; + } + + return VINF_SUCCESS; +} + +VBOXBLITTERDECL(int) CrBltCleanup(PCR_BLITTER pBlitter) +{ + if (CrBltIsEntered(pBlitter)) + { + WARN(("CrBltBlitTexTex: blitter is entered")); + return VERR_INVALID_STATE; + } + + if (pBlitter->Flags.ShadersGloal || !CrGlslNeedsCleanup(&pBlitter->LocalGlslCache)) + return VINF_SUCCESS; + + int rc = CrBltEnter(pBlitter); + if (!RT_SUCCESS(rc)) + { + WARN(("CrBltEnter failed, rc %d", rc)); + return rc; + } + + CrGlslCleanup(&pBlitter->LocalGlslCache); + + CrBltLeave(pBlitter); + + return VINF_SUCCESS; +} + +void CrBltTerm(PCR_BLITTER pBlitter) +{ + if (pBlitter->Flags.CtxCreated) + pBlitter->pDispatch->DestroyContext(pBlitter->CtxInfo.Base.id); + memset(pBlitter, 0, sizeof (*pBlitter)); +} + +int CrBltMuralSetCurrentInfo(PCR_BLITTER pBlitter, const CR_BLITTER_WINDOW *pMural) +{ + if (pMural) + { + if (!memcmp(&pBlitter->CurrentMural, pMural, sizeof (pBlitter->CurrentMural))) + return VINF_SUCCESS; + memcpy(&pBlitter->CurrentMural, pMural, sizeof (pBlitter->CurrentMural)); + } + else + { + if (CrBltIsEntered(pBlitter)) + { + WARN(("can not set null mural for entered bleater")); + return VERR_INVALID_STATE; + } + if (!pBlitter->CurrentMural.Base.id) + return VINF_SUCCESS; + pBlitter->CurrentMural.Base.id = 0; + } + + pBlitter->Flags.CurrentMuralChanged = 1; + + if (!CrBltIsEntered(pBlitter)) + return VINF_SUCCESS; + else if (!pBlitter->CtxInfo.Base.id) + { + WARN(("setting current mural for entered no-context blitter")); + return VERR_INVALID_STATE; + } + + WARN(("changing mural for entered blitter, is is somewhat expected?")); + + pBlitter->pDispatch->Flush(); + + pBlitter->pDispatch->MakeCurrent(pMural->Base.id, pBlitter->i32MakeCurrentUserData, pBlitter->CtxInfo.Base.id); + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) crBltBlitTexBufImplFbo(PCR_BLITTER pBlitter, const VBOXVR_TEXTURE *pSrc, const RTRECT *paSrcRect, const RTRECTSIZE *pDstSize, const RTRECT *paDstRect, uint32_t cRects, uint32_t fFlags) +{ + GLenum filter = CRBLT_FILTER_FROM_FLAGS(fFlags); + pBlitter->pDispatch->BindFramebufferEXT(GL_READ_FRAMEBUFFER, pBlitter->idFBO); + pBlitter->pDispatch->FramebufferTexture2DEXT(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, pSrc->target, pSrc->hwid, 0); + pBlitter->pDispatch->ReadBuffer(GL_COLOR_ATTACHMENT0); + + for (uint32_t i = 0; i < cRects; ++i) + { + const RTRECT * pSrcRect = &paSrcRect[i]; + const RTRECT * pDstRect = &paDstRect[i]; + int32_t srcY1; + int32_t srcY2; + int32_t dstY1; + int32_t dstY2; + int32_t srcX1 = pSrcRect->xLeft; + int32_t srcX2 = pSrcRect->xRight; + int32_t dstX1 = pDstRect->xLeft; + int32_t dstX2 = pDstRect->xRight; + + if (CRBLT_F_INVERT_SRC_YCOORDS & fFlags) + { + srcY1 = pSrc->height - pSrcRect->yTop; + srcY2 = pSrc->height - pSrcRect->yBottom; + } + else + { + srcY1 = pSrcRect->yTop; + srcY2 = pSrcRect->yBottom; + } + + if (CRBLT_F_INVERT_DST_YCOORDS & fFlags) + { + dstY1 = pDstSize->cy - pDstRect->yTop; + dstY2 = pDstSize->cy - pDstRect->yBottom; + } + else + { + dstY1 = pDstRect->yTop; + dstY2 = pDstRect->yBottom; + } + + if (srcY1 > srcY2) + { + if (dstY1 > dstY2) + { + /* use srcY1 < srcY2 && dstY1 < dstY2 whenever possible to avoid GPU driver bugs */ + int32_t tmp = srcY1; + srcY1 = srcY2; + srcY2 = tmp; + tmp = dstY1; + dstY1 = dstY2; + dstY2 = tmp; + } + } + + if (srcX1 > srcX2) + { + if (dstX1 > dstX2) + { + /* use srcX1 < srcX2 && dstX1 < dstX2 whenever possible to avoid GPU driver bugs */ + int32_t tmp = srcX1; + srcX1 = srcX2; + srcX2 = tmp; + tmp = dstX1; + dstX1 = dstX2; + dstX2 = tmp; + } + } + + pBlitter->pDispatch->BlitFramebufferEXT(srcX1, srcY1, srcX2, srcY2, + dstX1, dstY1, dstX2, dstY2, + GL_COLOR_BUFFER_BIT, filter); + } + + return VINF_SUCCESS; +} + +/* GL_TRIANGLE_FAN */ +DECLINLINE(GLfloat*) crBltVtRectTFNormalized(const RTRECT *pRect, uint32_t normalX, uint32_t normalY, GLfloat* pBuff, uint32_t height) +{ + /* going ccw: + * 1. (left;top) 4. (right;top) + * | ^ + * > | + * 2. (left;bottom) -> 3. (right;bottom) */ + /* xLeft yTop */ + pBuff[0] = ((float)pRect->xLeft)/((float)normalX); + pBuff[1] = ((float)(height ? height - pRect->yTop : pRect->yTop))/((float)normalY); + + /* xLeft yBottom */ + pBuff[2] = pBuff[0]; + pBuff[3] = ((float)(height ? height - pRect->yBottom : pRect->yBottom))/((float)normalY); + + /* xRight yBottom */ + pBuff[4] = ((float)pRect->xRight)/((float)normalX); + pBuff[5] = pBuff[3]; + + /* xRight yTop */ + pBuff[6] = pBuff[4]; + pBuff[7] = pBuff[1]; + return &pBuff[8]; +} + +DECLINLINE(GLfloat*) crBltVtRectsTFNormalized(const RTRECT *paRects, uint32_t cRects, uint32_t normalX, uint32_t normalY, GLfloat* pBuff, uint32_t height) +{ + for (uint32_t i = 0; i < cRects; ++i) + { + pBuff = crBltVtRectTFNormalized(&paRects[i], normalX, normalY, pBuff, height); + } + return pBuff; +} + +DECLINLINE(GLint*) crBltVtRectTF(const RTRECT *pRect, uint32_t normalX, uint32_t normalY, GLint* pBuff, uint32_t height) +{ + /* xLeft yTop */ + pBuff[0] = pRect->xLeft; + pBuff[1] = height ? height - pRect->yTop : pRect->yTop; + + /* xLeft yBottom */ + pBuff[2] = pBuff[0]; + pBuff[3] = height ? height - pRect->yBottom : pRect->yBottom; + + /* xRight yBottom */ + pBuff[4] = pRect->xRight; + pBuff[5] = pBuff[3]; + + /* xRight yTop */ + pBuff[6] = pBuff[4]; + pBuff[7] = pBuff[1]; + return &pBuff[8]; +} + +DECLINLINE(GLubyte*) crBltVtFillRectIndicies(GLubyte *pIndex, GLubyte *piBase) +{ + GLubyte iBase = *piBase; + /* triangle 1 */ + pIndex[0] = iBase; + pIndex[1] = iBase + 1; + pIndex[2] = iBase + 2; + + /* triangle 2 */ + pIndex[3] = iBase; + pIndex[4] = iBase + 2; + pIndex[5] = iBase + 3; + *piBase = iBase + 4; + return pIndex + 6; +} + +/* Indexed GL_TRIANGLES */ +DECLINLINE(GLfloat*) crBltVtRectITNormalized(const RTRECT *pRect, uint32_t normalX, uint32_t normalY, GLfloat* pBuff, uint32_t height) +{ + GLfloat* ret = crBltVtRectTFNormalized(pRect, normalX, normalY, pBuff, height); + return ret; +} + +DECLINLINE(GLint*) crBltVtRectIT(RTRECT *pRect, uint32_t normalX, uint32_t normalY, GLint* pBuff, GLubyte **ppIndex, GLubyte *piBase, uint32_t height) +{ + GLint* ret = crBltVtRectTF(pRect, normalX, normalY, pBuff, height); + + if (ppIndex) + *ppIndex = crBltVtFillRectIndicies(*ppIndex, piBase); + + return ret; +} + +DECLINLINE(GLuint) crBltVtGetNumVerticiesTF(GLuint cRects) +{ + return cRects * 4; +} + +#define crBltVtGetNumVerticiesIT crBltVtGetNumVerticiesTF + +DECLINLINE(GLuint) crBltVtGetNumIndiciesIT(GLuint cRects) +{ + return 6 * cRects; +} + + +static GLfloat* crBltVtRectsITNormalized(const RTRECT *paRects, uint32_t cRects, uint32_t normalX, uint32_t normalY, GLfloat* pBuff, GLubyte **ppIndex, GLubyte *piBase, uint32_t height) +{ + uint32_t i; + for (i = 0; i < cRects; ++i) + { + pBuff = crBltVtRectITNormalized(&paRects[i], normalX, normalY, pBuff, height); + } + + + if (ppIndex) + { + GLubyte *pIndex = (GLubyte*)pBuff; + *ppIndex = pIndex; + for (i = 0; i < cRects; ++i) + { + pIndex = crBltVtFillRectIndicies(pIndex, piBase); + } + pBuff = (GLfloat*)pIndex; + } + + return pBuff; +} + +static void* crBltBufGet(PCR_BLITTER_BUFFER pBuffer, GLuint cbBuffer) +{ + if (pBuffer->cbBuffer < cbBuffer) + { + if (pBuffer->pvBuffer) + { + RTMemFree(pBuffer->pvBuffer); + } + +#ifndef DEBUG_misha + /* debugging: ensure we calculate proper buffer size */ + cbBuffer += 16; +#endif + + pBuffer->pvBuffer = RTMemAlloc(cbBuffer); + if (pBuffer->pvBuffer) + pBuffer->cbBuffer = cbBuffer; + else + { + crWarning("failed to allocate buffer of size %d", cbBuffer); + pBuffer->cbBuffer = 0; + } + } + return pBuffer->pvBuffer; +} + +static void crBltCheckSetupViewport(PCR_BLITTER pBlitter, const RTRECTSIZE *pDstSize, bool fFBODraw) +{ + bool fUpdateViewport = pBlitter->Flags.CurrentMuralChanged; + if (pBlitter->CurrentSetSize.cx != pDstSize->cx + || pBlitter->CurrentSetSize.cy != pDstSize->cy) + { + pBlitter->CurrentSetSize = *pDstSize; + pBlitter->pDispatch->MatrixMode(GL_PROJECTION); + pBlitter->pDispatch->LoadIdentity(); + pBlitter->pDispatch->Ortho(0, pDstSize->cx, 0, pDstSize->cy, -1, 1); + fUpdateViewport = true; + } + + if (fUpdateViewport) + { + pBlitter->pDispatch->Viewport(0, 0, pBlitter->CurrentSetSize.cx, pBlitter->CurrentSetSize.cy); + pBlitter->Flags.CurrentMuralChanged = 0; + } + + pBlitter->Flags.LastWasFBODraw = fFBODraw; +} + +static DECLCALLBACK(int) crBltBlitTexBufImplDraw2D(PCR_BLITTER pBlitter, const VBOXVR_TEXTURE *pSrc, const RTRECT *paSrcRect, const RTRECTSIZE *pDstSize, const RTRECT *paDstRect, uint32_t cRects, uint32_t fFlags) +{ + GLuint normalX, normalY; + uint32_t srcHeight = (fFlags & CRBLT_F_INVERT_SRC_YCOORDS) ? pSrc->height : 0; + uint32_t dstHeight = (fFlags & CRBLT_F_INVERT_DST_YCOORDS) ? pDstSize->cy : 0; + + switch (pSrc->target) + { + case GL_TEXTURE_2D: + { + normalX = pSrc->width; + normalY = pSrc->height; + break; + } + + case GL_TEXTURE_RECTANGLE_ARB: + { + normalX = 1; + normalY = 1; + break; + } + + default: + { + crWarning("Unsupported texture target 0x%x", pSrc->target); + return VERR_INVALID_PARAMETER; + } + } + + Assert(pSrc->hwid); + + pBlitter->pDispatch->BindTexture(pSrc->target, pSrc->hwid); + + if (cRects == 1) + { + /* just optimization to draw a single rect with GL_TRIANGLE_FAN */ + GLfloat *pVerticies; + GLfloat *pTexCoords; + GLuint cElements = crBltVtGetNumVerticiesTF(cRects); + + pVerticies = (GLfloat*)crBltBufGet(&pBlitter->Verticies, cElements * 2 * 2 * sizeof (*pVerticies)); + pTexCoords = crBltVtRectsTFNormalized(paDstRect, cRects, 1, 1, pVerticies, dstHeight); + crBltVtRectsTFNormalized(paSrcRect, cRects, normalX, normalY, pTexCoords, srcHeight); + + pBlitter->pDispatch->EnableClientState(GL_VERTEX_ARRAY); + pBlitter->pDispatch->VertexPointer(2, GL_FLOAT, 0, pVerticies); + + pBlitter->pDispatch->EnableClientState(GL_TEXTURE_COORD_ARRAY); + pBlitter->pDispatch->TexCoordPointer(2, GL_FLOAT, 0, pTexCoords); + + pBlitter->pDispatch->Enable(pSrc->target); + + pBlitter->pDispatch->DrawArrays(GL_TRIANGLE_FAN, 0, cElements); + + pBlitter->pDispatch->Disable(pSrc->target); + + pBlitter->pDispatch->DisableClientState(GL_TEXTURE_COORD_ARRAY); + pBlitter->pDispatch->DisableClientState(GL_VERTEX_ARRAY); + } + else + { + GLfloat *pVerticies; + GLfloat *pTexCoords; + GLubyte *pIndicies; + GLuint cElements = crBltVtGetNumVerticiesIT(cRects); + GLuint cIndicies = crBltVtGetNumIndiciesIT(cRects); + GLubyte iIdxBase = 0; + + pVerticies = (GLfloat*)crBltBufGet(&pBlitter->Verticies, cElements * 2 * 2 * sizeof (*pVerticies) + cIndicies * sizeof (*pIndicies)); + pTexCoords = crBltVtRectsITNormalized(paDstRect, cRects, 1, 1, pVerticies, &pIndicies, &iIdxBase, dstHeight); + crBltVtRectsITNormalized(paSrcRect, cRects, normalX, normalY, pTexCoords, NULL, NULL, srcHeight); + + pBlitter->pDispatch->EnableClientState(GL_VERTEX_ARRAY); + pBlitter->pDispatch->VertexPointer(2, GL_FLOAT, 0, pVerticies); + + pBlitter->pDispatch->EnableClientState(GL_TEXTURE_COORD_ARRAY); + pBlitter->pDispatch->TexCoordPointer(2, GL_FLOAT, 0, pTexCoords); + + pBlitter->pDispatch->Enable(pSrc->target); + + pBlitter->pDispatch->DrawElements(GL_TRIANGLES, cIndicies, GL_UNSIGNED_BYTE, pIndicies); + + pBlitter->pDispatch->Disable(pSrc->target); + + pBlitter->pDispatch->DisableClientState(GL_TEXTURE_COORD_ARRAY); + pBlitter->pDispatch->DisableClientState(GL_VERTEX_ARRAY); + } + + pBlitter->pDispatch->BindTexture(pSrc->target, 0); + + return VINF_SUCCESS; +} + +static int crBltInitOnMakeCurent(PCR_BLITTER pBlitter) +{ + const char * pszExtension = (const char*)pBlitter->pDispatch->GetString(GL_EXTENSIONS); + if (crStrstr(pszExtension, "GL_EXT_framebuffer_object")) + { + pBlitter->Flags.SupportsFBO = 1; + pBlitter->pDispatch->GenFramebuffersEXT(1, &pBlitter->idFBO); + Assert(pBlitter->idFBO); + } + else + crWarning("GL_EXT_framebuffer_object not supported, blitter can only blit to window"); + + if (crStrstr(pszExtension, "GL_ARB_pixel_buffer_object")) + pBlitter->Flags.SupportsPBO = 1; + else + crWarning("GL_ARB_pixel_buffer_object not supported"); + + /* BlitFramebuffer seems to be buggy on Intel, + * try always glDrawXxx for now */ + if (!pBlitter->Flags.ForceDrawBlit && crStrstr(pszExtension, "GL_EXT_framebuffer_blit")) + { + pBlitter->pfnBlt = crBltBlitTexBufImplFbo; + } + else + { +// crWarning("GL_EXT_framebuffer_blit not supported, will use Draw functions for blitting, which might be less efficient"); + pBlitter->pfnBlt = crBltBlitTexBufImplDraw2D; + } + + /* defaults. but just in case */ + pBlitter->pDispatch->MatrixMode(GL_TEXTURE); + pBlitter->pDispatch->LoadIdentity(); + pBlitter->pDispatch->MatrixMode(GL_MODELVIEW); + pBlitter->pDispatch->LoadIdentity(); + + return VINF_SUCCESS; +} + +void CrBltLeave(PCR_BLITTER pBlitter) +{ + if (!pBlitter->cEnters) + { + WARN(("blitter not entered!")); + return; + } + + if (--pBlitter->cEnters) + return; + + if (pBlitter->Flags.SupportsFBO) + { + pBlitter->pDispatch->BindFramebufferEXT(GL_FRAMEBUFFER, 0); + pBlitter->pDispatch->DrawBuffer(GL_BACK); + pBlitter->pDispatch->ReadBuffer(GL_BACK); + } + + pBlitter->pDispatch->Flush(); + + if (pBlitter->CtxInfo.Base.id) + pBlitter->pDispatch->MakeCurrent(0, 0, 0); +} + +int CrBltEnter(PCR_BLITTER pBlitter) +{ + if (!pBlitter->CurrentMural.Base.id && pBlitter->CtxInfo.Base.id) + { + WARN(("current mural not initialized!")); + return VERR_INVALID_STATE; + } + + if (pBlitter->cEnters++) + return VINF_SUCCESS; + + if (pBlitter->CurrentMural.Base.id) /* <- pBlitter->CurrentMural.Base.id can be null if the blitter is in a "no-context" mode (see comments to BltInit for detail)*/ + { + pBlitter->pDispatch->MakeCurrent(pBlitter->CurrentMural.Base.id, pBlitter->i32MakeCurrentUserData, pBlitter->CtxInfo.Base.id); + } + + if (pBlitter->Flags.Initialized) + return VINF_SUCCESS; + + int rc = crBltInitOnMakeCurent(pBlitter); + if (RT_SUCCESS(rc)) + { + pBlitter->Flags.Initialized = 1; + return VINF_SUCCESS; + } + + WARN(("crBltInitOnMakeCurent failed, rc %d", rc)); + CrBltLeave(pBlitter); + return rc; +} + +static void crBltBlitTexBuf(PCR_BLITTER pBlitter, const VBOXVR_TEXTURE *pSrc, const RTRECT *paSrcRects, GLenum enmDstBuff, const RTRECTSIZE *pDstSize, const RTRECT *paDstRects, uint32_t cRects, uint32_t fFlags) +{ + pBlitter->pDispatch->DrawBuffer(enmDstBuff); + + crBltCheckSetupViewport(pBlitter, pDstSize, enmDstBuff == GL_DRAW_FRAMEBUFFER); + + if (!(fFlags & CRBLT_F_NOALPHA)) + pBlitter->pfnBlt(pBlitter, pSrc, paSrcRects, pDstSize, paDstRects, cRects, fFlags); + else + { + int rc = pBlitter->Flags.ShadersGloal ? + CrGlslProgUseNoAlpha(pBlitter->pGlslCache, pSrc->target) + : + CrGlslProgUseGenNoAlpha(&pBlitter->LocalGlslCache, pSrc->target); + + if (!RT_SUCCESS(rc)) + { + crWarning("Failed to use no-alpha program rc %d!, falling back to default blit", rc); + pBlitter->pfnBlt(pBlitter, pSrc, paSrcRects, pDstSize, paDstRects, cRects, fFlags); + return; + } + + /* since we use shaders, we need to use draw commands rather than framebuffer blits. + * force using draw-based blitting */ + crBltBlitTexBufImplDraw2D(pBlitter, pSrc, paSrcRects, pDstSize, paDstRects, cRects, fFlags); + + Assert(pBlitter->Flags.ShadersGloal || &pBlitter->LocalGlslCache == pBlitter->pGlslCache); + + CrGlslProgClear(pBlitter->pGlslCache); + } +} + +void CrBltCheckUpdateViewport(PCR_BLITTER pBlitter) +{ + RTRECTSIZE DstSize = {pBlitter->CurrentMural.width, pBlitter->CurrentMural.height}; + crBltCheckSetupViewport(pBlitter, &DstSize, false); +} + +void CrBltBlitTexMural(PCR_BLITTER pBlitter, bool fBb, const VBOXVR_TEXTURE *pSrc, const RTRECT *paSrcRects, const RTRECT *paDstRects, uint32_t cRects, uint32_t fFlags) +{ + if (!CrBltIsEntered(pBlitter)) + { + WARN(("CrBltBlitTexMural: blitter not entered")); + return; + } + + RTRECTSIZE DstSize = {pBlitter->CurrentMural.width, pBlitter->CurrentMural.height}; + + pBlitter->pDispatch->BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0); + + crBltBlitTexBuf(pBlitter, pSrc, paSrcRects, fBb ? GL_BACK : GL_FRONT, &DstSize, paDstRects, cRects, fFlags); +} + +void CrBltBlitTexTex(PCR_BLITTER pBlitter, const VBOXVR_TEXTURE *pSrc, const RTRECT *pSrcRect, const VBOXVR_TEXTURE *pDst, const RTRECT *pDstRect, uint32_t cRects, uint32_t fFlags) +{ + if (!CrBltIsEntered(pBlitter)) + { + WARN(("CrBltBlitTexTex: blitter not entered")); + return; + } + + RTRECTSIZE DstSize = {(uint32_t)pDst->width, (uint32_t)pDst->height}; + + pBlitter->pDispatch->BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, pBlitter->idFBO); + + /* TODO: mag/min filters ? */ + + pBlitter->pDispatch->FramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, pDst->target, pDst->hwid, 0); + +// pBlitter->pDispatch->FramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0); +// pBlitter->pDispatch->FramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + + crBltBlitTexBuf(pBlitter, pSrc, pSrcRect, GL_DRAW_FRAMEBUFFER, &DstSize, pDstRect, cRects, fFlags); + + pBlitter->pDispatch->FramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, pDst->target, 0, 0); +} + +void CrBltPresent(PCR_BLITTER pBlitter) +{ + if (!CrBltIsEntered(pBlitter)) + { + WARN(("CrBltPresent: blitter not entered")); + return; + } + + if (pBlitter->CtxInfo.Base.visualBits & CR_DOUBLE_BIT) + pBlitter->pDispatch->SwapBuffers(pBlitter->CurrentMural.Base.id, 0); + else + pBlitter->pDispatch->Flush(); +} + +static int crBltImgInitBaseForTex(const VBOXVR_TEXTURE *pSrc, CR_BLITTER_IMG *pDst, GLenum enmFormat) +{ + memset(pDst, 0, sizeof (*pDst)); + if (enmFormat != GL_RGBA + && enmFormat != GL_BGRA) + { + WARN(("unsupported format 0x%x", enmFormat)); + return VERR_NOT_IMPLEMENTED; + } + + uint32_t bpp = 32; + + uint32_t pitch = ((bpp * pSrc->width) + 7) >> 3; + uint32_t cbData = pitch * pSrc->height; + pDst->cbData = cbData; + pDst->enmFormat = enmFormat; + pDst->width = pSrc->width; + pDst->height = pSrc->height; + pDst->bpp = bpp; + pDst->pitch = pitch; + return VINF_SUCCESS; +} + +static int crBltImgCreateForTex(const VBOXVR_TEXTURE *pSrc, CR_BLITTER_IMG *pDst, GLenum enmFormat) +{ + int rc = crBltImgInitBaseForTex(pSrc, pDst, enmFormat); + if (!RT_SUCCESS(rc)) + { + crWarning("crBltImgInitBaseForTex failed rc %d", rc); + return rc; + } + + uint32_t cbData = pDst->cbData; + pDst->pvData = RTMemAllocZ(cbData); + if (!pDst->pvData) + { + crWarning("RTMemAlloc failed"); + return VERR_NO_MEMORY; + } + +#ifdef DEBUG_misha + { + char *pTmp = (char*)pDst->pvData; + for (uint32_t i = 0; i < cbData; ++i) + { + pTmp[i] = (char)((1 << i) % 255); + } + } +#endif + return VINF_SUCCESS; +} + +VBOXBLITTERDECL(int) CrBltImgGetTex(PCR_BLITTER pBlitter, const VBOXVR_TEXTURE *pSrc, GLenum enmFormat, CR_BLITTER_IMG *pDst) +{ + if (!CrBltIsEntered(pBlitter)) + { + WARN(("CrBltImgGetTex: blitter not entered")); + return VERR_INVALID_STATE; + } + + int rc = crBltImgCreateForTex(pSrc, pDst, enmFormat); + if (!RT_SUCCESS(rc)) + { + crWarning("crBltImgCreateForTex failed, rc %d", rc); + return rc; + } + pBlitter->pDispatch->BindTexture(pSrc->target, pSrc->hwid); + +#ifdef DEBUG_misha + { + GLint width = 0, height = 0, depth = 0; + pBlitter->pDispatch->GetTexLevelParameteriv(pSrc->target, 0, GL_TEXTURE_WIDTH, &width); + pBlitter->pDispatch->GetTexLevelParameteriv(pSrc->target, 0, GL_TEXTURE_HEIGHT, &height); + pBlitter->pDispatch->GetTexLevelParameteriv(pSrc->target, 0, GL_TEXTURE_DEPTH, &depth); + + Assert(width == pSrc->width); + Assert(height == pSrc->height); +// Assert(depth == pSrc->depth); + } +#endif + + pBlitter->pDispatch->GetTexImage(pSrc->target, 0, enmFormat, GL_UNSIGNED_BYTE, pDst->pvData); + + pBlitter->pDispatch->BindTexture(pSrc->target, 0); + return VINF_SUCCESS; +} + +VBOXBLITTERDECL(int) CrBltImgGetMural(PCR_BLITTER pBlitter, bool fBb, CR_BLITTER_IMG *pDst) +{ + if (!CrBltIsEntered(pBlitter)) + { + WARN(("CrBltImgGetMural: blitter not entered")); + return VERR_INVALID_STATE; + } + + crWarning("NOT IMPLEMENTED"); + return VERR_NOT_IMPLEMENTED; +} + +VBOXBLITTERDECL(void) CrBltImgFree(PCR_BLITTER pBlitter, CR_BLITTER_IMG *pDst) +{ + if (!CrBltIsEntered(pBlitter)) + { + WARN(("CrBltImgFree: blitter not entered")); + return; + } + + if (pDst->pvData) + { + RTMemFree(pDst->pvData); + pDst->pvData = NULL; + } +} + + +VBOXBLITTERDECL(bool) CrGlslIsSupported(CR_GLSL_CACHE *pCache) +{ + if (pCache->iGlVersion == 0) + { + const char * pszStr = (const char*)pCache->pDispatch->GetString(GL_VERSION); + pCache->iGlVersion = crStrParseGlVersion(pszStr); + if (pCache->iGlVersion <= 0) + { + crWarning("crStrParseGlVersion returned %d", pCache->iGlVersion); + pCache->iGlVersion = -1; + } + } + + if (pCache->iGlVersion >= CR_GLVERSION_COMPOSE(2, 0, 0)) + return true; + + crWarning("GLSL unsuported, gl version %d", pCache->iGlVersion); + + /* @todo: we could also check for GL_ARB_shader_objects and GL_ARB_fragment_shader, + * but seems like chromium does not support properly gl*Object versions of shader functions used with those extensions */ + return false; +} + +#define CR_GLSL_STR_V_120 "#version 120\n" +#define CR_GLSL_STR_EXT_TR "#extension GL_ARB_texture_rectangle : enable\n" +#define CR_GLSL_STR_2D "2D" +#define CR_GLSL_STR_2DRECT "2DRect" + +#define CR_GLSL_PATTERN_FS_NOALPHA(_ver, _ext, _tex) \ + _ver \ + _ext \ + "uniform sampler" _tex " sampler0;\n" \ + "void main()\n" \ + "{\n" \ + "vec2 srcCoord = vec2(gl_TexCoord[0]);\n" \ + "gl_FragData[0].xyz = (texture" _tex "(sampler0, srcCoord).xyz);\n" \ + "gl_FragData[0].w = 1.0;\n" \ + "}\n" + +static const char* crGlslGetFsStringNoAlpha(CR_GLSL_CACHE *pCache, GLenum enmTexTarget) +{ + if (!CrGlslIsSupported(pCache)) + { + crWarning("CrGlslIsSupported is false"); + return NULL; + } + + if (pCache->iGlVersion >= CR_GLVERSION_COMPOSE(2, 1, 0)) + { + if (enmTexTarget == GL_TEXTURE_2D) + return CR_GLSL_PATTERN_FS_NOALPHA(CR_GLSL_STR_V_120, "", CR_GLSL_STR_2D); + else if (enmTexTarget == GL_TEXTURE_RECTANGLE_ARB) + return CR_GLSL_PATTERN_FS_NOALPHA(CR_GLSL_STR_V_120, CR_GLSL_STR_EXT_TR, CR_GLSL_STR_2DRECT); + + crWarning("invalid enmTexTarget %#x", enmTexTarget); + return NULL; + } + else if (pCache->iGlVersion >= CR_GLVERSION_COMPOSE(2, 0, 0)) + { + if (enmTexTarget == GL_TEXTURE_2D) + return CR_GLSL_PATTERN_FS_NOALPHA("", "", CR_GLSL_STR_2D); + else if (enmTexTarget == GL_TEXTURE_RECTANGLE_ARB) + return CR_GLSL_PATTERN_FS_NOALPHA("", CR_GLSL_STR_EXT_TR, CR_GLSL_STR_2DRECT); + + crWarning("invalid enmTexTarget %#x", enmTexTarget); + return NULL; + } + + crError("crGlslGetFsStringNoAlpha: we should not be here!"); + return NULL; +} + +static int crGlslProgGenNoAlpha(CR_GLSL_CACHE *pCache, GLenum enmTexTarget, GLuint *puiProgram) +{ + *puiProgram = 0; + + const char*pStrFsShader = crGlslGetFsStringNoAlpha(pCache, enmTexTarget); + if (!pStrFsShader) + { + crWarning("crGlslGetFsStringNoAlpha failed"); + return VERR_NOT_SUPPORTED; + } + + int rc = VINF_SUCCESS; + GLchar * pBuf = NULL; + GLuint uiProgram = 0; + GLint iUniform = -1; + GLuint uiShader = pCache->pDispatch->CreateShader(GL_FRAGMENT_SHADER); + if (!uiShader) + { + crWarning("CreateShader failed"); + return VERR_NOT_SUPPORTED; + } + + pCache->pDispatch->ShaderSource(uiShader, 1, &pStrFsShader, NULL); + + pCache->pDispatch->CompileShader(uiShader); + + GLint compiled = 0; + pCache->pDispatch->GetShaderiv(uiShader, GL_COMPILE_STATUS, &compiled); + +#ifndef DEBUG_misha + if(!compiled) +#endif + { + if (!pBuf) + pBuf = (GLchar *)RTMemAlloc(16300); + pCache->pDispatch->GetShaderInfoLog(uiShader, 16300, NULL, pBuf); +#ifdef DEBUG_misha + if (compiled) + crDebug("compile success:\n-------------------\n%s\n--------\n", pBuf); + else +#endif + { + crWarning("compile FAILURE:\n-------------------\n%s\n--------\n", pBuf); + rc = VERR_NOT_SUPPORTED; + goto end; + } + } + + Assert(compiled); + + uiProgram = pCache->pDispatch->CreateProgram(); + if (!uiProgram) + { + rc = VERR_NOT_SUPPORTED; + goto end; + } + + pCache->pDispatch->AttachShader(uiProgram, uiShader); + + pCache->pDispatch->LinkProgram(uiProgram); + + GLint linked; + pCache->pDispatch->GetProgramiv(uiProgram, GL_LINK_STATUS, &linked); +#ifndef DEBUG_misha + if(!linked) +#endif + { + if (!pBuf) + pBuf = (GLchar *)RTMemAlloc(16300); + pCache->pDispatch->GetProgramInfoLog(uiProgram, 16300, NULL, pBuf); +#ifdef DEBUG_misha + if (linked) + crDebug("link success:\n-------------------\n%s\n--------\n", pBuf); + else +#endif + { + crWarning("link FAILURE:\n-------------------\n%s\n--------\n", pBuf); + rc = VERR_NOT_SUPPORTED; + goto end; + } + } + + Assert(linked); + + iUniform = pCache->pDispatch->GetUniformLocation(uiProgram, "sampler0"); + if (iUniform == -1) + { + crWarning("GetUniformLocation failed for sampler0"); + } + else + { + pCache->pDispatch->Uniform1i(iUniform, 0); + } + + *puiProgram = uiProgram; + + /* avoid end finalizer from cleaning it */ + uiProgram = 0; + + end: + if (uiShader) + pCache->pDispatch->DeleteShader(uiShader); + if (uiProgram) + pCache->pDispatch->DeleteProgram(uiProgram); + if (pBuf) + RTMemFree(pBuf); + return rc; +} + +DECLINLINE(GLuint) crGlslProgGetNoAlpha(const CR_GLSL_CACHE *pCache, GLenum enmTexTarget) +{ + switch (enmTexTarget) + { + case GL_TEXTURE_2D: + return pCache->uNoAlpha2DProg; + case GL_TEXTURE_RECTANGLE_ARB: + return pCache->uNoAlpha2DRectProg; + default: + crWarning("invalid tex enmTexTarget %#x", enmTexTarget); + return 0; + } +} + +DECLINLINE(GLuint*) crGlslProgGetNoAlphaPtr(CR_GLSL_CACHE *pCache, GLenum enmTexTarget) +{ + switch (enmTexTarget) + { + case GL_TEXTURE_2D: + return &pCache->uNoAlpha2DProg; + case GL_TEXTURE_RECTANGLE_ARB: + return &pCache->uNoAlpha2DRectProg; + default: + crWarning("invalid tex enmTexTarget %#x", enmTexTarget); + return NULL; + } +} + +VBOXBLITTERDECL(int) CrGlslProgGenNoAlpha(CR_GLSL_CACHE *pCache, GLenum enmTexTarget) +{ + GLuint*puiProgram = crGlslProgGetNoAlphaPtr(pCache, enmTexTarget); + if (!puiProgram) + return VERR_INVALID_PARAMETER; + + if (*puiProgram) + return VINF_SUCCESS; + + return crGlslProgGenNoAlpha(pCache, enmTexTarget, puiProgram); +} + +VBOXBLITTERDECL(int) CrGlslProgGenAllNoAlpha(CR_GLSL_CACHE *pCache) +{ + int rc = CrGlslProgGenNoAlpha(pCache, GL_TEXTURE_2D); + if (!RT_SUCCESS(rc)) + { + crWarning("CrGlslProgGenNoAlpha GL_TEXTURE_2D failed rc %d", rc); + return rc; + } + + rc = CrGlslProgGenNoAlpha(pCache, GL_TEXTURE_RECTANGLE_ARB); + if (!RT_SUCCESS(rc)) + { + crWarning("CrGlslProgGenNoAlpha GL_TEXTURE_RECTANGLE failed rc %d", rc); + return rc; + } + + return VINF_SUCCESS; +} + +VBOXBLITTERDECL(void) CrGlslProgClear(const CR_GLSL_CACHE *pCache) +{ + pCache->pDispatch->UseProgram(0); +} + +VBOXBLITTERDECL(int) CrGlslProgUseNoAlpha(const CR_GLSL_CACHE *pCache, GLenum enmTexTarget) +{ + GLuint uiProg = crGlslProgGetNoAlpha(pCache, enmTexTarget); + if (!uiProg) + { + crWarning("request to use inexistent program!"); + return VERR_INVALID_STATE; + } + + Assert(uiProg); + + pCache->pDispatch->UseProgram(uiProg); + + return VINF_SUCCESS; +} + +VBOXBLITTERDECL(int) CrGlslProgUseGenNoAlpha(CR_GLSL_CACHE *pCache, GLenum enmTexTarget) +{ + GLuint uiProg = crGlslProgGetNoAlpha(pCache, enmTexTarget); + if (!uiProg) + { + int rc = CrGlslProgGenNoAlpha(pCache, enmTexTarget); + if (!RT_SUCCESS(rc)) + { + crWarning("CrGlslProgGenNoAlpha failed, rc %d", rc); + return rc; + } + + uiProg = crGlslProgGetNoAlpha(pCache, enmTexTarget); + CRASSERT(uiProg); + } + + Assert(uiProg); + + pCache->pDispatch->UseProgram(uiProg); + + return VINF_SUCCESS; +} + +VBOXBLITTERDECL(bool) CrGlslNeedsCleanup(const CR_GLSL_CACHE *pCache) +{ + return pCache->uNoAlpha2DProg || pCache->uNoAlpha2DRectProg; +} + +VBOXBLITTERDECL(void) CrGlslCleanup(CR_GLSL_CACHE *pCache) +{ + if (pCache->uNoAlpha2DProg) + { + pCache->pDispatch->DeleteProgram(pCache->uNoAlpha2DProg); + pCache->uNoAlpha2DProg = 0; + } + + if (pCache->uNoAlpha2DRectProg) + { + pCache->pDispatch->DeleteProgram(pCache->uNoAlpha2DRectProg); + pCache->uNoAlpha2DRectProg = 0; + } +} + +VBOXBLITTERDECL(void) CrGlslTerm(CR_GLSL_CACHE *pCache) +{ + CRASSERT(!CrGlslNeedsCleanup(pCache)); + + CrGlslCleanup(pCache); + + /* sanity */ + memset(pCache, 0, sizeof (*pCache)); +} + + +/*TdBlt*/ +static void crTdBltCheckPBO(PCR_TEXDATA pTex) +{ + if (pTex->idPBO) + return; + + PCR_BLITTER pBlitter = pTex->pBlitter; + + if (!pBlitter->Flags.SupportsPBO) + return; + + pBlitter->pDispatch->GenBuffersARB(1, &pTex->idPBO); + if (!pTex->idPBO) + { + crWarning("PBO create failed"); + return; + } + + pBlitter->pDispatch->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pTex->idPBO); + pBlitter->pDispatch->BufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, + pTex->Tex.width*pTex->Tex.height*4, + 0, GL_STREAM_READ_ARB); + pBlitter->pDispatch->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); +} + +static uint32_t crTdBltTexCreate(PCR_BLITTER pBlitter, uint32_t width, uint32_t height, GLenum enmTarget) +{ + uint32_t tex = 0; + pBlitter->pDispatch->GenTextures(1, &tex); + if (!tex) + { + crWarning("Tex create failed"); + return 0; + } + + pBlitter->pDispatch->BindTexture(enmTarget, tex); + pBlitter->pDispatch->TexParameteri(enmTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + pBlitter->pDispatch->TexParameteri(enmTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + pBlitter->pDispatch->TexParameteri(enmTarget, GL_TEXTURE_WRAP_S, GL_CLAMP); + pBlitter->pDispatch->TexParameteri(enmTarget, GL_TEXTURE_WRAP_T, GL_CLAMP); + pBlitter->pDispatch->TexImage2D(enmTarget, 0, GL_RGBA8, + width, height, + 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + + + /*Restore gl state*/ + pBlitter->pDispatch->BindTexture(enmTarget, 0); + + return tex; +} + +int crTdBltCheckInvertTex(PCR_TEXDATA pTex) +{ + if (pTex->idInvertTex) + return VINF_SUCCESS; + + pTex->idInvertTex = crTdBltTexCreate(pTex->pBlitter, pTex->Tex.width, pTex->Tex.height, pTex->Tex.target); + if (!pTex->idInvertTex) + { + crWarning("Invert Tex create failed"); + return VERR_GENERAL_FAILURE; + } + return VINF_SUCCESS; +} + +void crTdBltImgRelease(PCR_TEXDATA pTex) +{ + pTex->Flags.DataValid = 0; +} + +void crTdBltImgFree(PCR_TEXDATA pTex) +{ + if (!pTex->Img.pvData) + { + Assert(!pTex->Flags.DataValid); + return; + } + + crTdBltImgRelease(pTex); + + Assert(!pTex->Flags.DataValid); + + + if (pTex->idPBO) + { + PCR_BLITTER pBlitter = pTex->pBlitter; + + Assert(CrBltIsEntered(pBlitter)); + pBlitter->pDispatch->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pTex->idPBO); + pBlitter->pDispatch->UnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); + pBlitter->pDispatch->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); + } + else + { + Assert(pTex->Img.pvData); + RTMemFree(pTex->Img.pvData); + } + + pTex->Img.pvData = NULL; +} + +int crTdBltImgAcquire(PCR_TEXDATA pTex, GLenum enmFormat, bool fInverted) +{ + void *pvData = pTex->Img.pvData; + Assert(!pTex->Flags.DataValid); + int rc = crBltImgInitBaseForTex(&pTex->Tex, &pTex->Img, enmFormat); + if (!RT_SUCCESS(rc)) + { + WARN(("crBltImgInitBaseForTex failed rc %d", rc)); + return rc; + } + + PCR_BLITTER pBlitter = pTex->pBlitter; + Assert(CrBltIsEntered(pBlitter)); + pBlitter->pDispatch->BindTexture(pTex->Tex.target, fInverted ? pTex->idInvertTex : pTex->Tex.hwid); + + pBlitter->pDispatch->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pTex->idPBO); + + if (pvData) + { + if (pTex->idPBO) + { + pBlitter->pDispatch->UnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); + pvData = NULL; + + } + } + else + { + if (!pTex->idPBO) + { + pvData = RTMemAlloc(4*pTex->Tex.width*pTex->Tex.height); + if (!pvData) + { + WARN(("Out of memory in crTdBltImgAcquire")); + pBlitter->pDispatch->BindTexture(pTex->Tex.target, 0); + return VERR_NO_MEMORY; + } + } + } + + Assert(!pvData == !!pTex->idPBO); + + /*read the texture, note pixels are NULL for PBO case as it's offset in the buffer*/ + pBlitter->pDispatch->GetTexImage(GL_TEXTURE_2D, 0, enmFormat, GL_UNSIGNED_BYTE, pvData); + + /*restore gl state*/ + pBlitter->pDispatch->BindTexture(pTex->Tex.target, 0); + + if (pTex->idPBO) + { + pvData = pBlitter->pDispatch->MapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY); + if (!pvData) + { + WARN(("Failed to MapBuffer in CrHlpGetTexImage")); + return VERR_GENERAL_FAILURE; + } + + pBlitter->pDispatch->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); + } + + Assert(pvData); + pTex->Img.pvData = pvData; + pTex->Flags.DataValid = 1; + pTex->Flags.DataInverted = fInverted; + return VINF_SUCCESS; +} + +/* release the texture data, the data remains cached in the CR_TEXDATA object until it is discarded with CrTdBltDataInvalidateNe or CrTdBltDataCleanup */ +VBOXBLITTERDECL(int) CrTdBltDataRelease(PCR_TEXDATA pTex) +{ + if (!pTex->Flags.Entered) + { + WARN(("tex not entered")); + return VERR_INVALID_STATE; + } + + if (!pTex->Flags.DataAcquired) + { + WARN(("Data NOT acquired")); + return VERR_INVALID_STATE; + } + + Assert(pTex->Img.pvData); + Assert(pTex->Flags.DataValid); + + pTex->Flags.DataAcquired = 0; + + return VINF_SUCCESS; +} + +static void crTdBltDataFree(PCR_TEXDATA pTex) +{ + crTdBltImgFree(pTex); + + if (pTex->pScaledCache) + CrTdBltDataFreeNe(pTex->pScaledCache); +} + +/* discard the texture data cached with previous CrTdBltDataAcquire. + * Must be called wit data released (CrTdBltDataRelease) */ +VBOXBLITTERDECL(int) CrTdBltDataFree(PCR_TEXDATA pTex) +{ + if (!pTex->Flags.Entered) + { + WARN(("tex not entered")); + return VERR_INVALID_STATE; + } + + crTdBltDataFree(pTex); + + return VINF_SUCCESS; +} + +VBOXBLITTERDECL(void) CrTdBltDataInvalidateNe(PCR_TEXDATA pTex) +{ + crTdBltImgRelease(pTex); + + if (pTex->pScaledCache) + CrTdBltDataInvalidateNe(pTex->pScaledCache); +} + +VBOXBLITTERDECL(int) CrTdBltDataFreeNe(PCR_TEXDATA pTex) +{ + if (!pTex->Img.pvData) + return VINF_SUCCESS; + + bool fEntered = false; + if (pTex->idPBO) + { + int rc = CrTdBltEnter(pTex); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + fEntered = true; + } + + crTdBltDataFree(pTex); + + if (fEntered) + CrTdBltLeave(pTex); + + return VINF_SUCCESS; +} + +static void crTdBltSdCleanupCacheNe(PCR_TEXDATA pTex) +{ + if (pTex->pScaledCache) + { + CrTdBltDataCleanupNe(pTex->pScaledCache); + CrTdRelease(pTex->pScaledCache); + pTex->pScaledCache = NULL; + } +} + +static void crTdBltDataCleanup(PCR_TEXDATA pTex) +{ + crTdBltImgFree(pTex); + + PCR_BLITTER pBlitter = pTex->pBlitter; + + if (pTex->idPBO) + { + Assert(CrBltIsEntered(pBlitter)); + pBlitter->pDispatch->DeleteBuffersARB(1, &pTex->idPBO); + pTex->idPBO = 0; + } + + if (pTex->idInvertTex) + { + Assert(CrBltIsEntered(pBlitter)); + pBlitter->pDispatch->DeleteTextures(1, &pTex->idInvertTex); + pTex->idInvertTex = 0; + } + + crTdBltSdCleanupCacheNe(pTex); +} + +/* does same as CrTdBltDataFree, and in addition cleans up */ +VBOXBLITTERDECL(int) CrTdBltDataCleanup(PCR_TEXDATA pTex) +{ + if (!pTex->Flags.Entered) + { + WARN(("tex not entered")); + return VERR_INVALID_STATE; + } + + crTdBltDataCleanup(pTex); + + return VINF_SUCCESS; +} + +VBOXBLITTERDECL(int) CrTdBltDataCleanupNe(PCR_TEXDATA pTex) +{ + bool fEntered = false; + if (pTex->idPBO || pTex->idInvertTex) + { + int rc = CrTdBltEnter(pTex); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + fEntered = true; + } + + crTdBltDataCleanup(pTex); + + if (fEntered) + CrTdBltLeave(pTex); + + return VINF_SUCCESS; +} + +/* acquire the texture data, returns the cached data in case it is cached. + * the data remains cached in the CR_TEXDATA object until it is discarded with CrTdBltDataFree or CrTdBltDataCleanup. + * */ +VBOXBLITTERDECL(int) CrTdBltDataAcquire(PCR_TEXDATA pTex, GLenum enmFormat, bool fInverted, const CR_BLITTER_IMG**ppImg) +{ + if (!pTex->Flags.Entered) + { + WARN(("tex not entered")); + return VERR_INVALID_STATE; + } + + if (pTex->Flags.DataAcquired) + { + WARN(("Data acquired already")); + return VERR_INVALID_STATE; + } + + if (pTex->Flags.DataValid && pTex->Img.enmFormat == enmFormat && !pTex->Flags.DataInverted == !fInverted) + { + Assert(pTex->Img.pvData); + *ppImg = &pTex->Img; + pTex->Flags.DataAcquired = 1; + return VINF_SUCCESS; + } + + crTdBltImgRelease(pTex); + + crTdBltCheckPBO(pTex); + + int rc; + + if (fInverted) + { + rc = crTdBltCheckInvertTex(pTex); + if (!RT_SUCCESS(rc)) + { + WARN(("crTdBltCheckInvertTex failed rc %d", rc)); + return rc; + } + + RTRECT SrcRect, DstRect; + VBOXVR_TEXTURE InvertTex; + + InvertTex = pTex->Tex; + InvertTex.hwid = pTex->idInvertTex; + + SrcRect.xLeft = 0; + SrcRect.yTop = InvertTex.height; + SrcRect.xRight = InvertTex.width; + SrcRect.yBottom = 0; + + DstRect.xLeft = 0; + DstRect.yTop = 0; + DstRect.xRight = InvertTex.width; + DstRect.yBottom = InvertTex.height; + + CrBltBlitTexTex(pTex->pBlitter, &pTex->Tex, &SrcRect, &InvertTex, &DstRect, 1, 0); + } + + rc = crTdBltImgAcquire(pTex, enmFormat, fInverted); + if (!RT_SUCCESS(rc)) + { + WARN(("crTdBltImgAcquire failed rc %d", rc)); + return rc; + } + + Assert(pTex->Img.pvData); + *ppImg = &pTex->Img; + pTex->Flags.DataAcquired = 1; + + return VINF_SUCCESS; +} + +DECLINLINE(void) crTdResize(PCR_TEXDATA pTex, const VBOXVR_TEXTURE *pVrTex) +{ + crTdBltDataCleanup(pTex); + + pTex->Tex = *pVrTex; +} + +static DECLCALLBACK(void) ctTdBltSdReleased(struct CR_TEXDATA *pTexture) +{ + PCR_BLITTER pBlitter = pTexture->pBlitter; + + int rc = CrBltEnter(pBlitter); + if (!RT_SUCCESS(rc)) + { + WARN(("CrBltEnter failed, rc %d", rc)); + return; + } + + CrTdBltDataCleanupNe(pTexture); + + pBlitter->pDispatch->DeleteTextures(1, &pTexture->Tex.hwid); + + CrBltLeave(pBlitter); + + RTMemFree(pTexture); +} + +static int ctTdBltSdCreate(PCR_BLITTER pBlitter, uint32_t width, uint32_t height, GLenum enmTarget, PCR_TEXDATA *ppScaledCache) +{ + PCR_TEXDATA pScaledCache; + + Assert(CrBltIsEntered(pBlitter)); + + *ppScaledCache = NULL; + + pScaledCache = (PCR_TEXDATA)RTMemAlloc(sizeof (*pScaledCache)); + if (!pScaledCache) + { + WARN(("RTMemAlloc failed")); + return VERR_NO_MEMORY; + } + + VBOXVR_TEXTURE Tex; + Tex.width = width; + Tex.height = height; + Tex.target = enmTarget; + Tex.hwid = crTdBltTexCreate(pBlitter, width, height, enmTarget); + if (!Tex.hwid) + { + WARN(("Tex create failed")); + RTMemFree(pScaledCache); + return VERR_GENERAL_FAILURE; + } + + CrTdInit(pScaledCache, &Tex, pBlitter, ctTdBltSdReleased); + + *ppScaledCache = pScaledCache; + + return VINF_SUCCESS; +} + +static int ctTdBltSdGet(PCR_TEXDATA pTex, uint32_t width, uint32_t height, PCR_TEXDATA *ppScaledCache) +{ + Assert(CrBltIsEntered(pTex->pBlitter)); + + PCR_TEXDATA pScaledCache; + + *ppScaledCache = NULL; + + if (!pTex->pScaledCache) + { + int rc = ctTdBltSdCreate(pTex->pBlitter, width, height, pTex->Tex.target, &pScaledCache); + if (!RT_SUCCESS(rc)) + { + WARN(("ctTdBltSdCreate failed %d", rc)); + return rc; + } + + pTex->pScaledCache = pScaledCache; + } + else + { + int cmp = pTex->pScaledCache->Tex.width - width; + if (cmp <= 0) + cmp = pTex->pScaledCache->Tex.height - height; + + if (!cmp) + pScaledCache = pTex->pScaledCache; + else if (cmp < 0) /* current cache is "less" than the requested */ + { + int rc = ctTdBltSdCreate(pTex->pBlitter, width, height, pTex->Tex.target, &pScaledCache); + if (!RT_SUCCESS(rc)) + { + WARN(("ctTdBltSdCreate failed %d", rc)); + return rc; + } + + pScaledCache->pScaledCache = pTex->pScaledCache; + pTex->pScaledCache = pScaledCache; + } + else /* cmp > 0 */ + { + int rc = ctTdBltSdGet(pTex->pScaledCache, width, height, &pScaledCache); + if (!RT_SUCCESS(rc)) + { + WARN(("ctTdBltSdGet failed %d", rc)); + return rc; + } + } + } + + Assert(pScaledCache); + +#if 0 + { + VBOXVR_TEXTURE Tex; + Tex.width = width; + Tex.height = height; + Tex.target = pTex->Tex.target; + Tex.hwid = crTdBltTexCreate(pTex, width, height); + if (!Tex.hwid) + { + WARN(("Tex create failed")); + return VERR_GENERAL_FAILURE; + } + + pTex->pBlitter->pDispatch->DeleteTextures(1, &pTex->pScaledCache->Tex.hwid); + + crTdResize(pTex->pScaledCache, &Tex); + } +#endif + + *ppScaledCache = pScaledCache; + return VINF_SUCCESS; +} + +static int ctTdBltSdGetUpdated(PCR_TEXDATA pTex, uint32_t width, uint32_t height, PCR_TEXDATA *ppScaledCache) +{ + PCR_TEXDATA pScaledCache; + + *ppScaledCache = NULL; + int rc = ctTdBltSdGet(pTex, width, height, &pScaledCache); + if (!RT_SUCCESS(rc)) + { + WARN(("ctTdBltSdGet failed %d", rc)); + return rc; + } + + Assert(width == pScaledCache->Tex.width); + Assert(height == pScaledCache->Tex.height); + + if (!pScaledCache->Flags.DataValid) + { + RTRECT SrcRect, DstRect; + + SrcRect.xLeft = 0; + SrcRect.yTop = 0; + SrcRect.xRight = pTex->Tex.width; + SrcRect.yBottom = pTex->Tex.height; + + DstRect.xLeft = 0; + DstRect.yTop = 0; + DstRect.xRight = width; + DstRect.yBottom = height; + + CrBltBlitTexTex(pTex->pBlitter, &pTex->Tex, &SrcRect, &pScaledCache->Tex, &DstRect, 1, 0); + } + + *ppScaledCache = pScaledCache; + + return VINF_SUCCESS; +} + +VBOXBLITTERDECL(int) CrTdBltDataAcquireScaled(PCR_TEXDATA pTex, GLenum enmFormat, bool fInverted, uint32_t width, uint32_t height, const CR_BLITTER_IMG**ppImg) +{ + if (pTex->Tex.width == width && pTex->Tex.height == height) + return CrTdBltDataAcquire(pTex, enmFormat, fInverted, ppImg); + + if (!pTex->Flags.Entered) + { + WARN(("tex not entered")); + return VERR_INVALID_STATE; + } + + PCR_TEXDATA pScaledCache; + + int rc = ctTdBltSdGetUpdated(pTex, width, height, &pScaledCache); + if (!RT_SUCCESS(rc)) + { + WARN(("ctTdBltSdGetUpdated failed rc %d", rc)); + return rc; + } + + rc = CrTdBltEnter(pScaledCache); + if (!RT_SUCCESS(rc)) + { + WARN(("CrTdBltEnter failed rc %d", rc)); + return rc; + } + + rc = CrTdBltDataAcquire(pScaledCache, enmFormat, fInverted, ppImg); + if (!RT_SUCCESS(rc)) + { + WARN(("CrTdBltDataAcquire failed rc %d", rc)); + CrTdBltLeave(pTex->pScaledCache); + return rc; + } + + return VINF_SUCCESS; +} + +VBOXBLITTERDECL(int) CrTdBltDataReleaseScaled(PCR_TEXDATA pTex, const CR_BLITTER_IMG *pImg) +{ + PCR_TEXDATA pScaledCache = RT_FROM_MEMBER(pImg, CR_TEXDATA, Img); + int rc = CrTdBltDataRelease(pScaledCache); + if (!RT_SUCCESS(rc)) + { + WARN(("CrTdBltDataRelease failed rc %d", rc)); + return rc; + } + + if (pScaledCache != pTex) + CrTdBltLeave(pScaledCache); + + return VINF_SUCCESS; +} + +VBOXBLITTERDECL(void) CrTdBltScaleCacheMoveTo(PCR_TEXDATA pTex, PCR_TEXDATA pDstTex) +{ + if (!pTex->pScaledCache) + return; + + crTdBltSdCleanupCacheNe(pDstTex); + + pDstTex->pScaledCache = pTex->pScaledCache; + pTex->pScaledCache = NULL; +} diff --git a/src/VBox/GuestHost/OpenGL/util/bmpscale.cpp b/src/VBox/GuestHost/OpenGL/util/bmpscale.cpp new file mode 100644 index 00000000..1b32487b --- /dev/null +++ b/src/VBox/GuestHost/OpenGL/util/bmpscale.cpp @@ -0,0 +1,319 @@ +/** @file + * Image resampling code, used for snapshot thumbnails. + */ + +/* + * Copyright (C) 2009-2011 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. + */ + +/* + * Based on gdImageCopyResampled from libgd. + * Original copyright notice follows: + + Portions copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Pierre-Alain Joye (pierre@libgd.org). + + Permission has been granted to copy, distribute and modify gd in + any context without fee, including a commercial application, + provided that this notice is present in user-accessible supporting + documentation. + + This does not affect your ownership of the derived work itself, and + the intent is to assure proper credit for the authors of gd, not to + interfere with your productive use of gd. If you have questions, + ask. "Derived works" includes all programs that utilize the + library. Credit must be given in user-accessible documentation. + + This software is provided "AS IS." The copyright holders disclaim + all warranties, either express or implied, including but not + limited to implied warranties of merchantability and fitness for a + particular purpose, with respect to this code and accompanying + documentation. + */ + +/* + * + * @todo Simplify: Offsets of images are 0,0 => no dstX, dstY, srcX, srcY; + * Screenshot has no alpha channel => no processing of alpha byte. + */ + +#include <cr_bmpscale.h> + +/* 2.0.10: cast instead of floor() yields 35% performance improvement. + Thanks to John Buckman. */ + +#define floor2(exp) ((long) exp) +/*#define floor2(exp) floor(exp)*/ + +typedef uint8_t *gdImagePtr; + +DECLINLINE(int) gdImageGetTrueColorPixel (gdImagePtr im, int x, int y, int w) +{ + return *(int32_t *)(im + y * w * 4 + x * 4); +} + +DECLINLINE(void) gdImageSetPixel (gdImagePtr im, int x, int y, int color, int cbLine) +{ + *(int32_t *)(im + y * cbLine + x * 4) = color; +} + +#define gdAlphaMax 127 +#define gdAlphaOpaque 0 +#define gdAlphaTransparent 127 +#define gdRedMax 255 +#define gdGreenMax 255 +#define gdBlueMax 255 +#define gdTrueColorGetAlpha(c) (((c) & 0x7F000000) >> 24) +#define gdTrueColorGetRed(c) (((c) & 0xFF0000) >> 16) +#define gdTrueColorGetGreen(c) (((c) & 0x00FF00) >> 8) +#define gdTrueColorGetBlue(c) ((c) & 0x0000FF) +#define gdTrueColorAlpha(r, g, b, a) (((a) << 24) + \ + ((r) << 16) + \ + ((g) << 8) + \ + (b)) + +void gdImageCopyResampled (uint8_t *dst, + uint8_t *src, + int dstX, int dstY, + int srcX, int srcY, + int dstW, int dstH, int srcW, int srcH) +{ + int x, y; + double sy1, sy2, sx1, sx2; + for (y = dstY; (y < dstY + dstH); y++) + { + sy1 = ((double) y - (double) dstY) * (double) srcH / (double) dstH; + sy2 = ((double) (y + 1) - (double) dstY) * (double) srcH / + (double) dstH; + for (x = dstX; (x < dstX + dstW); x++) + { + double sx, sy; + double spixels = 0; + double red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0; + sx1 = ((double) x - (double) dstX) * (double) srcW / dstW; + sx2 = ((double) (x + 1) - (double) dstX) * (double) srcW / dstW; + sy = sy1; + do + { + double yportion; + if (floor2 (sy) == floor2 (sy1)) + { + yportion = 1.0 - (sy - floor2 (sy)); + if (yportion > sy2 - sy1) + { + yportion = sy2 - sy1; + } + sy = floor2 (sy); + } + else if (sy == floor2 (sy2)) + { + yportion = sy2 - floor2 (sy2); + } + else + { + yportion = 1.0; + } + sx = sx1; + do + { + double xportion; + double pcontribution; + int p; + if (floor2 (sx) == floor2 (sx1)) + { + xportion = 1.0 - (sx - floor2 (sx)); + if (xportion > sx2 - sx1) + { + xportion = sx2 - sx1; + } + sx = floor2 (sx); + } + else if (sx == floor2 (sx2)) + { + xportion = sx2 - floor2 (sx2); + } + else + { + xportion = 1.0; + } + pcontribution = xportion * yportion; + /* 2.08: previously srcX and srcY were ignored. + Andrew Pattison */ + p = gdImageGetTrueColorPixel (src, + (int) sx + srcX, + (int) sy + srcY, srcW); + red += gdTrueColorGetRed (p) * pcontribution; + green += gdTrueColorGetGreen (p) * pcontribution; + blue += gdTrueColorGetBlue (p) * pcontribution; + alpha += gdTrueColorGetAlpha (p) * pcontribution; + spixels += xportion * yportion; + sx += 1.0; + } + while (sx < sx2); + sy += 1.0; + } + while (sy < sy2); + if (spixels != 0.0) + { + red /= spixels; + green /= spixels; + blue /= spixels; + alpha /= spixels; + } + /* Clamping to allow for rounding errors above */ + if (red > 255.0) + { + red = 255.0; + } + if (green > 255.0) + { + green = 255.0; + } + if (blue > 255.0) + { + blue = 255.0; + } + if (alpha > gdAlphaMax) + { + alpha = gdAlphaMax; + } + gdImageSetPixel (dst, + x, y, + gdTrueColorAlpha ((int) red, + (int) green, + (int) blue, (int) alpha), dstW * 4); + } + } +} + +/* Fast integer implementation for 32 bpp bitmap scaling. + * Use fixed point values * 16. + */ +typedef int32_t FIXEDPOINT; +#define INT_TO_FIXEDPOINT(i) (FIXEDPOINT)((i) << 4) +#define FIXEDPOINT_TO_INT(v) (int)((v) >> 4) +#define FIXEDPOINT_FLOOR(v) ((v) & ~0xF) +#define FIXEDPOINT_FRACTION(v) ((v) & 0xF) + +/* For 32 bit source only. */ +VBOXBMPSCALEDECL(void) CrBmpScale32 (uint8_t *dst, + int iDstDeltaLine, + int dstW, int dstH, + const uint8_t *src, + int iSrcDeltaLine, + int srcW, int srcH) +{ + int x, y; + + for (y = 0; y < dstH; y++) + { + FIXEDPOINT sy1 = INT_TO_FIXEDPOINT(y * srcH) / dstH; + FIXEDPOINT sy2 = INT_TO_FIXEDPOINT((y + 1) * srcH) / dstH; + + for (x = 0; x < dstW; x++) + { + FIXEDPOINT red = 0, green = 0, blue = 0; + + FIXEDPOINT sx1 = INT_TO_FIXEDPOINT(x * srcW) / dstW; + FIXEDPOINT sx2 = INT_TO_FIXEDPOINT((x + 1) * srcW) / dstW; + + FIXEDPOINT spixels = (sx2 - sx1) * (sy2 - sy1); + + FIXEDPOINT sy = sy1; + + do + { + FIXEDPOINT yportion; + if (FIXEDPOINT_FLOOR (sy) == FIXEDPOINT_FLOOR (sy1)) + { + yportion = INT_TO_FIXEDPOINT(1) - FIXEDPOINT_FRACTION(sy); + if (yportion > sy2 - sy1) + { + yportion = sy2 - sy1; + } + sy = FIXEDPOINT_FLOOR (sy); + } + else if (sy == FIXEDPOINT_FLOOR (sy2)) + { + yportion = FIXEDPOINT_FRACTION(sy2); + } + else + { + yportion = INT_TO_FIXEDPOINT(1); + } + + const uint8_t *pu8SrcLine = src + iSrcDeltaLine * FIXEDPOINT_TO_INT(sy); + FIXEDPOINT sx = sx1; + do + { + FIXEDPOINT xportion; + FIXEDPOINT pcontribution; + int p; + if (FIXEDPOINT_FLOOR (sx) == FIXEDPOINT_FLOOR (sx1)) + { + xportion = INT_TO_FIXEDPOINT(1) - FIXEDPOINT_FRACTION(sx); + if (xportion > sx2 - sx1) + { + xportion = sx2 - sx1; + } + pcontribution = xportion * yportion; + sx = FIXEDPOINT_FLOOR (sx); + } + else if (sx == FIXEDPOINT_FLOOR (sx2)) + { + xportion = FIXEDPOINT_FRACTION(sx2); + pcontribution = xportion * yportion; + } + else + { + xportion = INT_TO_FIXEDPOINT(1); + pcontribution = xportion * yportion; + } + /* Color depth specific code begin */ + p = *(uint32_t *)(pu8SrcLine + FIXEDPOINT_TO_INT(sx) * 4); + /* Color depth specific code end */ + red += gdTrueColorGetRed (p) * pcontribution; + green += gdTrueColorGetGreen (p) * pcontribution; + blue += gdTrueColorGetBlue (p) * pcontribution; + + sx += INT_TO_FIXEDPOINT(1); + } while (sx < sx2); + + sy += INT_TO_FIXEDPOINT(1); + } while (sy < sy2); + + if (spixels != 0) + { + red /= spixels; + green /= spixels; + blue /= spixels; + } + /* Clamping to allow for rounding errors above */ + if (red > 255) + { + red = 255; + } + if (green > 255) + { + green = 255; + } + if (blue > 255) + { + blue = 255; + } + gdImageSetPixel (dst, + x, y, + ( ((int) red) << 16) + (((int) green) << 8) + ((int) blue), + iDstDeltaLine); + } + } +} + diff --git a/src/VBox/GuestHost/OpenGL/util/compositor.cpp b/src/VBox/GuestHost/OpenGL/util/compositor.cpp new file mode 100644 index 00000000..f7c50bcd --- /dev/null +++ b/src/VBox/GuestHost/OpenGL/util/compositor.cpp @@ -0,0 +1,1015 @@ +/* $Id: compositor.cpp $ */ + +/** @file + * Compositor impl + */ + +/* + * Copyright (C) 2013 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_compositor.h> + +#define VBOXVR_SCR_COMPOSITOR_RECTS_UNDEFINED UINT32_MAX + + +static int crVrScrCompositorRectsAssignBuffer(PVBOXVR_SCR_COMPOSITOR pCompositor, uint32_t cRects) +{ + Assert(cRects); + + if (pCompositor->cRectsBuffer >= cRects) + { + pCompositor->cRects = cRects; + return VINF_SUCCESS; + } + + if (pCompositor->cRectsBuffer) + { + Assert(pCompositor->paSrcRects); + RTMemFree(pCompositor->paSrcRects); + pCompositor->paSrcRects = NULL; + Assert(pCompositor->paDstRects); + RTMemFree(pCompositor->paDstRects); + pCompositor->paDstRects = NULL; + Assert(pCompositor->paDstUnstretchedRects); + RTMemFree(pCompositor->paDstUnstretchedRects); + pCompositor->paDstUnstretchedRects = NULL; + } + else + { + Assert(!pCompositor->paSrcRects); + Assert(!pCompositor->paDstRects); + Assert(!pCompositor->paDstUnstretchedRects); + } + + pCompositor->paSrcRects = (PRTRECT)RTMemAlloc(sizeof (*pCompositor->paSrcRects) * cRects); + if (pCompositor->paSrcRects) + { + pCompositor->paDstRects = (PRTRECT)RTMemAlloc(sizeof (*pCompositor->paDstRects) * cRects); + if (pCompositor->paDstRects) + { + pCompositor->paDstUnstretchedRects = (PRTRECT)RTMemAlloc(sizeof (*pCompositor->paDstUnstretchedRects) * cRects); + if (pCompositor->paDstUnstretchedRects) + { + pCompositor->cRects = cRects; + pCompositor->cRectsBuffer = cRects; + return VINF_SUCCESS; + } + + RTMemFree(pCompositor->paDstRects); + pCompositor->paDstRects = NULL; + } + else + { + WARN(("RTMemAlloc failed!")); + } + RTMemFree(pCompositor->paSrcRects); + pCompositor->paSrcRects = NULL; + } + else + { + WARN(("RTMemAlloc failed!")); + } + + pCompositor->cRects = VBOXVR_SCR_COMPOSITOR_RECTS_UNDEFINED; + pCompositor->cRectsBuffer = 0; + + return VERR_NO_MEMORY; +} + +static void crVrScrCompositorRectsInvalidate(PVBOXVR_SCR_COMPOSITOR pCompositor) +{ + pCompositor->cRects = VBOXVR_SCR_COMPOSITOR_RECTS_UNDEFINED; +} + +static DECLCALLBACK(bool) crVrScrCompositorRectsCounterCb(PVBOXVR_COMPOSITOR pCompositor, PVBOXVR_COMPOSITOR_ENTRY pEntry, void *pvVisitor) +{ + uint32_t* pCounter = (uint32_t*)pvVisitor; + Assert(VBoxVrListRectsCount(&pEntry->Vr)); + *pCounter += VBoxVrListRectsCount(&pEntry->Vr); + return true; +} + +typedef struct VBOXVR_SCR_COMPOSITOR_RECTS_ASSIGNER +{ + PRTRECT paSrcRects; + PRTRECT paDstRects; + PRTRECT paDstUnstretchedRects; + uint32_t cRects; +} VBOXVR_SCR_COMPOSITOR_RECTS_ASSIGNER, *PVBOXVR_SCR_COMPOSITOR_RECTS_ASSIGNER; + +static DECLCALLBACK(bool) crVrScrCompositorRectsAssignerCb(PVBOXVR_COMPOSITOR pCCompositor, PVBOXVR_COMPOSITOR_ENTRY pCEntry, void *pvVisitor) +{ + PVBOXVR_SCR_COMPOSITOR_RECTS_ASSIGNER pData = (PVBOXVR_SCR_COMPOSITOR_RECTS_ASSIGNER)pvVisitor; + PVBOXVR_SCR_COMPOSITOR pCompositor = VBOXVR_SCR_COMPOSITOR_FROM_COMPOSITOR(pCCompositor); + PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry = VBOXVR_SCR_COMPOSITOR_ENTRY_FROM_ENTRY(pCEntry); + pEntry->paSrcRects = pData->paSrcRects; + pEntry->paDstRects = pData->paDstRects; + pEntry->paDstUnstretchedRects = pData->paDstUnstretchedRects; + uint32_t cRects = VBoxVrListRectsCount(&pCEntry->Vr); + Assert(cRects); + Assert(cRects <= pData->cRects); + int rc = VBoxVrListRectsGet(&pCEntry->Vr, cRects, pEntry->paDstUnstretchedRects); + AssertRC(rc); + + if (!pEntry->Rect.xLeft && !pEntry->Rect.yTop) + { + memcpy(pEntry->paSrcRects, pEntry->paDstUnstretchedRects, cRects * sizeof (*pEntry->paSrcRects)); + } + else + { + for (uint32_t i = 0; i < cRects; ++i) + { + pEntry->paSrcRects[i].xLeft = (int32_t)((pEntry->paDstUnstretchedRects[i].xLeft - pEntry->Rect.xLeft)); + pEntry->paSrcRects[i].yTop = (int32_t)((pEntry->paDstUnstretchedRects[i].yTop - pEntry->Rect.yTop)); + pEntry->paSrcRects[i].xRight = (int32_t)((pEntry->paDstUnstretchedRects[i].xRight - pEntry->Rect.xLeft)); + pEntry->paSrcRects[i].yBottom = (int32_t)((pEntry->paDstUnstretchedRects[i].yBottom - pEntry->Rect.yTop)); + } + } + +#ifndef IN_RING0 + if (pCompositor->StretchX != 1. || pCompositor->StretchY != 1.) + { + for (uint32_t i = 0; i < cRects; ++i) + { + if (pCompositor->StretchX != 1.) + { + pEntry->paDstRects[i].xLeft = (int32_t)(pEntry->paDstUnstretchedRects[i].xLeft * pCompositor->StretchX); + pEntry->paDstRects[i].xRight = (int32_t)(pEntry->paDstUnstretchedRects[i].xRight * pCompositor->StretchX); + } + if (pCompositor->StretchY != 1.) + { + pEntry->paDstRects[i].yTop = (int32_t)(pEntry->paDstUnstretchedRects[i].yTop * pCompositor->StretchY); + pEntry->paDstRects[i].yBottom = (int32_t)(pEntry->paDstUnstretchedRects[i].yBottom * pCompositor->StretchY); + } + } + } + else +#endif + { + memcpy(pEntry->paDstRects, pEntry->paDstUnstretchedRects, cRects * sizeof (*pEntry->paDstUnstretchedRects)); + } + +#if 0//ndef IN_RING0 + bool canZeroX = (pCompositor->StretchX < 1.); + bool canZeroY = (pCompositor->StretchY < 1.); + if (canZeroX && canZeroY) + { + /* filter out zero rectangles*/ + uint32_t iOrig, iNew; + for (iOrig = 0, iNew = 0; iOrig < cRects; ++iOrig) + { + PRTRECT pOrigRect = &pEntry->paDstRects[iOrig]; + if (pOrigRect->xLeft != pOrigRect->xRight + && pOrigRect->yTop != pOrigRect->yBottom) + continue; + + if (iNew != iOrig) + { + PRTRECT pNewRect = &pEntry->paSrcRects[iNew]; + *pNewRect = *pOrigRect; + } + + ++iNew; + } + + Assert(iNew <= iOrig); + + uint32_t cDiff = iOrig - iNew; + + if (cDiff) + { + pCompositor->cRects -= cDiff; + cRects -= cDiff; + } + } +#endif + + pEntry->cRects = cRects; + pData->paDstRects += cRects; + pData->paSrcRects += cRects; + pData->paDstUnstretchedRects += cRects; + pData->cRects -= cRects; + return true; +} + +static int crVrScrCompositorRectsCheckInit(const VBOXVR_SCR_COMPOSITOR *pcCompositor) +{ + VBOXVR_SCR_COMPOSITOR *pCompositor = const_cast<VBOXVR_SCR_COMPOSITOR*>(pcCompositor); + + if (pCompositor->cRects != VBOXVR_SCR_COMPOSITOR_RECTS_UNDEFINED) + return VINF_SUCCESS; + + uint32_t cRects = 0; + VBoxVrCompositorVisit(&pCompositor->Compositor, crVrScrCompositorRectsCounterCb, &cRects); + + if (!cRects) + { + pCompositor->cRects = 0; + return VINF_SUCCESS; + } + + int rc = crVrScrCompositorRectsAssignBuffer(pCompositor, cRects); + if (!RT_SUCCESS(rc)) + return rc; + + VBOXVR_SCR_COMPOSITOR_RECTS_ASSIGNER AssignerData; + AssignerData.paSrcRects = pCompositor->paSrcRects; + AssignerData.paDstRects = pCompositor->paDstRects; + AssignerData.paDstUnstretchedRects = pCompositor->paDstUnstretchedRects; + AssignerData.cRects = pCompositor->cRects; + VBoxVrCompositorVisit(&pCompositor->Compositor, crVrScrCompositorRectsAssignerCb, &AssignerData); + Assert(!AssignerData.cRects); + return VINF_SUCCESS; +} + + +static int crVrScrCompositorEntryRegionsAdd(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, uint32_t cRegions, const RTRECT *paRegions, VBOXVR_SCR_COMPOSITOR_ENTRY **ppReplacedScrEntry, uint32_t *pfChangedFlags) +{ + uint32_t fChangedFlags = 0; + PVBOXVR_COMPOSITOR_ENTRY pReplacedEntry; + int rc = VBoxVrCompositorEntryRegionsAdd(&pCompositor->Compositor, pEntry ? &pEntry->Ce : NULL, cRegions, paRegions, &pReplacedEntry, &fChangedFlags); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxVrCompositorEntryRegionsAdd failed, rc %d", rc)); + return rc; + } + + VBOXVR_SCR_COMPOSITOR_ENTRY *pReplacedScrEntry = VBOXVR_SCR_COMPOSITOR_ENTRY_FROM_ENTRY(pReplacedEntry); + + if (fChangedFlags & VBOXVR_COMPOSITOR_CF_REGIONS_CHANGED) + { + crVrScrCompositorRectsInvalidate(pCompositor); + } + else if (fChangedFlags & VBOXVR_COMPOSITOR_CF_ENTRY_REPLACED) + { + Assert(pReplacedScrEntry); + } + + if (fChangedFlags & VBOXVR_COMPOSITOR_CF_OTHER_ENTRIES_REGIONS_CHANGED) + { + CrVrScrCompositorEntrySetAllChanged(pCompositor, true); + } + else if ((fChangedFlags & VBOXVR_COMPOSITOR_CF_ENTRY_REGIONS_CHANGED) && pEntry) + { + CrVrScrCompositorEntrySetChanged(pEntry, true); + } + + if (pfChangedFlags) + *pfChangedFlags = fChangedFlags; + + if (ppReplacedScrEntry) + *ppReplacedScrEntry = pReplacedScrEntry; + + return VINF_SUCCESS; +} + +static int crVrScrCompositorEntryRegionsSet(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, uint32_t cRegions, const RTRECT *paRegions, bool *pfChanged) +{ + bool fChanged; + int rc = VBoxVrCompositorEntryRegionsSet(&pCompositor->Compositor, &pEntry->Ce, cRegions, paRegions, &fChanged); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxVrCompositorEntryRegionsSet failed, rc %d", rc)); + return rc; + } + + if (fChanged) + { + CrVrScrCompositorEntrySetAllChanged(pCompositor, true); + if (!CrVrScrCompositorEntryIsInList(pEntry)) + { + pEntry->cRects = 0; + pEntry->paSrcRects = NULL; + pEntry->paDstRects = NULL; + pEntry->paDstUnstretchedRects = NULL; + } + crVrScrCompositorRectsInvalidate(pCompositor); + } + + + if (pfChanged) + *pfChanged = fChanged; + return VINF_SUCCESS; +} + +static int crVrScrCompositorEntryPositionSet(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, const RTPOINT *pPos, bool *pfChanged) +{ + if (pfChanged) + *pfChanged = false; + if (pEntry && (pEntry->Rect.xLeft != pPos->x || pEntry->Rect.yTop != pPos->y)) + { + if (VBoxVrCompositorEntryIsInList(&pEntry->Ce)) + { + int rc = VBoxVrCompositorEntryRegionsTranslate(&pCompositor->Compositor, &pEntry->Ce, pPos->x - pEntry->Rect.xLeft, pPos->y - pEntry->Rect.yTop, pfChanged); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxVrCompositorEntryRegionsTranslate failed rc %d", rc)); + return rc; + } + + crVrScrCompositorRectsInvalidate(pCompositor); + } + + VBoxRectMove(&pEntry->Rect, pPos->x, pPos->y); + CrVrScrCompositorEntrySetChanged(pEntry, true); + + if (pfChanged) + *pfChanged = true; + } + return VINF_SUCCESS; +} + +static int crVrScrCompositorEntryEnsureRegionsBounds(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, bool *pfChanged) +{ + RTRECT Rect; + Rect.xLeft = RT_MAX(pCompositor->Rect.xLeft, pEntry->Rect.xLeft); + Rect.yTop = RT_MAX(pCompositor->Rect.yTop, pEntry->Rect.yTop); + Rect.xRight = RT_MIN(pCompositor->Rect.xRight, pEntry->Rect.xRight); + Rect.yBottom = RT_MIN(pCompositor->Rect.yBottom, pEntry->Rect.yBottom); + bool fChanged = false; + + if (pfChanged) + *pfChanged = false; + + int rc = CrVrScrCompositorEntryRegionsIntersect(pCompositor, pEntry, 1, &Rect, &fChanged); + if (!RT_SUCCESS(rc)) + WARN(("CrVrScrCompositorEntryRegionsIntersect failed, rc %d", rc)); + + if (pfChanged) + *pfChanged = fChanged; + return rc; +} + +VBOXVREGDECL(int) CrVrScrCompositorEntryRegionsAdd(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, bool fPosRelated, VBOXVR_SCR_COMPOSITOR_ENTRY **ppReplacedScrEntry, uint32_t *pfChangeFlags) +{ + int rc; + uint32_t fChangeFlags = 0; + bool fPosChanged = false; + RTRECT *paTranslatedRects = NULL; + if (pPos) + { + rc = crVrScrCompositorEntryPositionSet(pCompositor, pEntry, pPos, &fPosChanged); + if (!RT_SUCCESS(rc)) + { + WARN(("RegionsAdd: crVrScrCompositorEntryPositionSet failed rc %d", rc)); + return rc; + } + } + + if (fPosRelated) + { + if (!pEntry) + { + WARN(("Entry is expected to be specified for pos-related regions")); + return VERR_INVALID_PARAMETER; + } + + if (cRegions && (pEntry->Rect.xLeft || pEntry->Rect.yTop)) + { + paTranslatedRects = (RTRECT*)RTMemAlloc(sizeof (RTRECT) * cRegions); + if (!paTranslatedRects) + { + WARN(("RTMemAlloc failed")); + return VERR_NO_MEMORY; + } + memcpy (paTranslatedRects, paRegions, sizeof (RTRECT) * cRegions); + for (uint32_t i = 0; i < cRegions; ++i) + { + VBoxRectTranslate(&paTranslatedRects[i], pEntry->Rect.xLeft, pEntry->Rect.yTop); + paRegions = paTranslatedRects; + } + } + } + + rc = crVrScrCompositorEntryRegionsAdd(pCompositor, pEntry, cRegions, paRegions, ppReplacedScrEntry, &fChangeFlags); + if (!RT_SUCCESS(rc)) + { + WARN(("crVrScrCompositorEntryRegionsAdd failed, rc %d", rc)); + goto done; + } + + if ((fPosChanged || (fChangeFlags & VBOXVR_COMPOSITOR_CF_ENTRY_REGIONS_CHANGED)) && pEntry) + { + bool fAdjusted = false; + rc = crVrScrCompositorEntryEnsureRegionsBounds(pCompositor, pEntry, &fAdjusted); + if (!RT_SUCCESS(rc)) + { + WARN(("crVrScrCompositorEntryEnsureRegionsBounds failed, rc %d", rc)); + goto done; + } + + if (fAdjusted) + { + if (CrVrScrCompositorEntryIsUsed(pEntry)) + { + fChangeFlags &= ~VBOXVR_COMPOSITOR_CF_ENTRY_REPLACED; + fChangeFlags |= VBOXVR_COMPOSITOR_CF_REGIONS_CHANGED | VBOXVR_COMPOSITOR_CF_ENTRY_REGIONS_CHANGED; + } + else + { + fChangeFlags = 0; + } + } + } + + if (fChangeFlags & VBOXVR_COMPOSITOR_CF_ENTRY_REPLACED) + fPosChanged = false; + else if (ppReplacedScrEntry) + *ppReplacedScrEntry = NULL; + + if (pfChangeFlags) + { + if (fPosChanged) + { + /* means entry was in list and was moved, so regions changed */ + *pfChangeFlags = VBOXVR_COMPOSITOR_CF_REGIONS_CHANGED | VBOXVR_COMPOSITOR_CF_ENTRY_REGIONS_CHANGED | VBOXVR_COMPOSITOR_CF_OTHER_ENTRIES_REGIONS_CHANGED; + } + else + *pfChangeFlags = fChangeFlags; + } + +done: + + if (paTranslatedRects) + RTMemFree(paTranslatedRects); + + return rc; +} + +VBOXVREGDECL(int) CrVrScrCompositorEntryRectSet(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, const RTRECT *pRect) +{ + if (!memcmp(&pEntry->Rect, pRect, sizeof (*pRect))) + { + return VINF_SUCCESS; + } + RTPOINT Point = {pRect->xLeft, pRect->yTop}; + bool fChanged = false; + int rc = crVrScrCompositorEntryPositionSet(pCompositor, pEntry, &Point, &fChanged); + if (!RT_SUCCESS(rc)) + { + WARN(("crVrScrCompositorEntryPositionSet failed %d", rc)); + return rc; + } + + pEntry->Rect = *pRect; + + if (!CrVrScrCompositorEntryIsUsed(pEntry)) + return VINF_SUCCESS; + + rc = crVrScrCompositorEntryEnsureRegionsBounds(pCompositor, pEntry, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("crVrScrCompositorEntryEnsureRegionsBounds failed, rc %d", rc)); + return rc; + } + + return VINF_SUCCESS; +} + +VBOXVREGDECL(int) CrVrScrCompositorEntryTexAssign(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, CR_TEXDATA *pTex) +{ + if (pEntry->pTex == pTex) + return VINF_SUCCESS; + + if (pEntry->pTex) + CrTdRelease(pEntry->pTex); + if (pTex) + CrTdAddRef(pTex); + pEntry->pTex = pTex; + return VINF_SUCCESS; +} + +VBOXVREGDECL(int) CrVrScrCompositorEntryRegionsSet(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, bool fPosRelated, bool *pfChanged) +{ + /* @todo: the fChanged sate calculation is really rough now, this is enough for now though */ + bool fChanged = false, fPosChanged = false; + bool fWasInList = CrVrScrCompositorEntryIsInList(pEntry); + RTRECT *paTranslatedRects = NULL; + int rc = CrVrScrCompositorEntryRemove(pCompositor, pEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("RegionsSet: CrVrScrCompositorEntryRemove failed rc %d", rc)); + return rc; + } + + if (pPos) + { + rc = crVrScrCompositorEntryPositionSet(pCompositor, pEntry, pPos, &fPosChanged); + if (!RT_SUCCESS(rc)) + { + WARN(("RegionsSet: crVrScrCompositorEntryPositionSet failed rc %d", rc)); + return rc; + } + } + + if (fPosRelated) + { + if (!pEntry) + { + WARN(("Entry is expected to be specified for pos-related regions")); + return VERR_INVALID_PARAMETER; + } + + if (cRegions && (pEntry->Rect.xLeft || pEntry->Rect.yTop)) + { + paTranslatedRects = (RTRECT*)RTMemAlloc(sizeof (RTRECT) * cRegions); + if (!paTranslatedRects) + { + WARN(("RTMemAlloc failed")); + return VERR_NO_MEMORY; + } + memcpy (paTranslatedRects, paRegions, sizeof (RTRECT) * cRegions); + for (uint32_t i = 0; i < cRegions; ++i) + { + VBoxRectTranslate(&paTranslatedRects[i], pEntry->Rect.xLeft, pEntry->Rect.yTop); + paRegions = paTranslatedRects; + } + } + } + + rc = crVrScrCompositorEntryRegionsSet(pCompositor, pEntry, cRegions, paRegions, &fChanged); + if (!RT_SUCCESS(rc)) + { + WARN(("crVrScrCompositorEntryRegionsSet failed, rc %d", rc)); + return rc; + } + + if (fChanged && CrVrScrCompositorEntryIsUsed(pEntry)) + { + rc = crVrScrCompositorEntryEnsureRegionsBounds(pCompositor, pEntry, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("crVrScrCompositorEntryEnsureRegionsBounds failed, rc %d", rc)); + return rc; + } + } + + if (pfChanged) + *pfChanged = fPosChanged || fChanged || fWasInList; + + return VINF_SUCCESS; +} + +VBOXVREGDECL(int) CrVrScrCompositorEntryListIntersect(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, const VBOXVR_LIST *pList2, bool *pfChanged) +{ + bool fChanged = false; + int rc = VBoxVrCompositorEntryListIntersect(&pCompositor->Compositor, &pEntry->Ce, pList2, &fChanged); + if (!RT_SUCCESS(rc)) + { + WARN(("RegionsIntersect: VBoxVrCompositorEntryRegionsIntersect failed rc %d", rc)); + return rc; + } + + if (fChanged) + { + CrVrScrCompositorEntrySetChanged(pEntry, true); + crVrScrCompositorRectsInvalidate(pCompositor); + } + + if (pfChanged) + *pfChanged = fChanged; + + return VINF_SUCCESS; +} + +VBOXVREGDECL(int) CrVrScrCompositorEntryRegionsIntersect(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, uint32_t cRegions, const RTRECT *paRegions, bool *pfChanged) +{ + bool fChanged = false; + int rc = VBoxVrCompositorEntryRegionsIntersect(&pCompositor->Compositor, &pEntry->Ce, cRegions, paRegions, &fChanged); + if (!RT_SUCCESS(rc)) + { + WARN(("RegionsIntersect: VBoxVrCompositorEntryRegionsIntersect failed rc %d", rc)); + return rc; + } + + if (fChanged) + crVrScrCompositorRectsInvalidate(pCompositor); + + if (pfChanged) + *pfChanged = fChanged; + + return VINF_SUCCESS; +} + +VBOXVREGDECL(int) CrVrScrCompositorEntryListIntersectAll(PVBOXVR_SCR_COMPOSITOR pCompositor, const VBOXVR_LIST *pList2, bool *pfChanged) +{ + VBOXVR_SCR_COMPOSITOR_ITERATOR Iter; + CrVrScrCompositorIterInit(pCompositor, &Iter); + PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry; + int rc = VINF_SUCCESS; + bool fChanged = false; + + while ((pEntry = CrVrScrCompositorIterNext(&Iter)) != NULL) + { + bool fTmpChanged = false; + int tmpRc = CrVrScrCompositorEntryListIntersect(pCompositor, pEntry, pList2, &fTmpChanged); + if (RT_SUCCESS(tmpRc)) + { + fChanged |= fTmpChanged; + } + else + { + WARN(("CrVrScrCompositorEntryRegionsIntersect failed, rc %d", tmpRc)); + rc = tmpRc; + } + } + + if (pfChanged) + *pfChanged = fChanged; + + return rc; +} + +VBOXVREGDECL(int) CrVrScrCompositorEntryRegionsIntersectAll(PVBOXVR_SCR_COMPOSITOR pCompositor, uint32_t cRegions, const RTRECT *paRegions, bool *pfChanged) +{ + VBOXVR_SCR_COMPOSITOR_ITERATOR Iter; + CrVrScrCompositorIterInit(pCompositor, &Iter); + PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry; + int rc = VINF_SUCCESS; + bool fChanged = false; + + while ((pEntry = CrVrScrCompositorIterNext(&Iter)) != NULL) + { + bool fTmpChanged = false; + int tmpRc = CrVrScrCompositorEntryRegionsIntersect(pCompositor, pEntry, cRegions, paRegions, &fTmpChanged); + if (RT_SUCCESS(tmpRc)) + { + fChanged |= fTmpChanged; + } + else + { + WARN(("CrVrScrCompositorEntryRegionsIntersect failed, rc %d", tmpRc)); + rc = tmpRc; + } + } + + if (pfChanged) + *pfChanged = fChanged; + + return rc; +} + +VBOXVREGDECL(int) CrVrScrCompositorEntryPosSet(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, const RTPOINT *pPos) +{ + int rc = crVrScrCompositorEntryPositionSet(pCompositor, pEntry, pPos, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("RegionsSet: crVrScrCompositorEntryPositionSet failed rc %d", rc)); + return rc; + } + + rc = crVrScrCompositorEntryEnsureRegionsBounds(pCompositor, pEntry, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("RegionsSet: crVrScrCompositorEntryEnsureRegionsBounds failed rc %d", rc)); + return rc; + } + + return VINF_SUCCESS; +} + +/* regions are valid until the next CrVrScrCompositor call */ +VBOXVREGDECL(int) CrVrScrCompositorEntryRegionsGet(const VBOXVR_SCR_COMPOSITOR *pCompositor, const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry, uint32_t *pcRegions, const RTRECT **ppaSrcRegions, const RTRECT **ppaDstRegions, const RTRECT **ppaDstUnstretchedRects) +{ + if (CrVrScrCompositorEntryIsUsed(pEntry)) + { + int rc = crVrScrCompositorRectsCheckInit(pCompositor); + if (!RT_SUCCESS(rc)) + { + WARN(("crVrScrCompositorRectsCheckInit failed, rc %d", rc)); + return rc; + } + } + + Assert(pCompositor->cRects != VBOXVR_SCR_COMPOSITOR_RECTS_UNDEFINED); + + *pcRegions = pEntry->cRects; + if (ppaSrcRegions) + *ppaSrcRegions = pEntry->paSrcRects; + if (ppaDstRegions) + *ppaDstRegions = pEntry->paDstRects; + if (ppaDstUnstretchedRects) + *ppaDstUnstretchedRects = pEntry->paDstUnstretchedRects; + + return VINF_SUCCESS; +} + +VBOXVREGDECL(uint32_t) CrVrScrCompositorEntryFlagsCombinedGet(const VBOXVR_SCR_COMPOSITOR *pCompositor, const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry) +{ + return CRBLT_FOP_COMBINE(pCompositor->fFlags, pEntry->fFlags); +} + +VBOXVREGDECL(void) CrVrScrCompositorEntryFlagsSet(PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, uint32_t fFlags) +{ + if (pEntry->fFlags == fFlags) + return; + + pEntry->fFlags = fFlags; + CrVrScrCompositorEntrySetChanged(pEntry, true); +} + +static void crVrScrCompositorEntryDataCleanup(PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry) +{ + pEntry->cRects = 0; + pEntry->paSrcRects = NULL; + pEntry->paDstRects = NULL; + pEntry->paDstUnstretchedRects = NULL; +} + +static void crVrScrCompositorEntryDataCopy(PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, PVBOXVR_SCR_COMPOSITOR_ENTRY pToEntry) +{ + pToEntry->cRects = pEntry->cRects; + pToEntry->paSrcRects = pEntry->paSrcRects; + pToEntry->paDstRects = pEntry->paDstRects; + pToEntry->paDstUnstretchedRects = pEntry->paDstUnstretchedRects; + crVrScrCompositorEntryDataCleanup(pEntry); +} + +VBOXVREGDECL(int) CrVrScrCompositorEntryRemove(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry) +{ + if (!VBoxVrCompositorEntryRemove(&pCompositor->Compositor, &pEntry->Ce)) + return VINF_SUCCESS; + + CrVrScrCompositorEntrySetChanged(pEntry, true); + crVrScrCompositorEntryDataCleanup(pEntry); + + crVrScrCompositorRectsInvalidate(pCompositor); + return VINF_SUCCESS; +} + +VBOXVREGDECL(bool) CrVrScrCompositorEntryReplace(PVBOXVR_SCR_COMPOSITOR pCompositor, PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry, PVBOXVR_SCR_COMPOSITOR_ENTRY pNewEntry) +{ + Assert(!CrVrScrCompositorEntryIsUsed(pNewEntry)); + + if (!VBoxVrCompositorEntryReplace(&pCompositor->Compositor, &pEntry->Ce, &pNewEntry->Ce)) + return false; + + CrVrScrCompositorEntrySetChanged(pEntry, true); + crVrScrCompositorEntryDataCopy(pEntry, pNewEntry); + CrVrScrCompositorEntrySetChanged(pNewEntry, true); + + return true; +} + +static DECLCALLBACK(void) crVrScrCompositorEntryReleasedCB(const struct VBOXVR_COMPOSITOR *pCompositor, PVBOXVR_COMPOSITOR_ENTRY pEntry, PVBOXVR_COMPOSITOR_ENTRY pReplacingEntry) +{ + PVBOXVR_SCR_COMPOSITOR_ENTRY pCEntry = VBOXVR_SCR_COMPOSITOR_ENTRY_FROM_ENTRY(pEntry); + + CrVrScrCompositorEntrySetChanged(pCEntry, true); + + Assert(!CrVrScrCompositorEntryIsInList(pCEntry)); + + if (pReplacingEntry) + { + PVBOXVR_SCR_COMPOSITOR_ENTRY pCReplacingEntry = VBOXVR_SCR_COMPOSITOR_ENTRY_FROM_ENTRY(pReplacingEntry); + Assert(CrVrScrCompositorEntryIsInList(pCReplacingEntry)); + pCReplacingEntry->cRects = pCEntry->cRects; + pCReplacingEntry->paSrcRects = pCEntry->paSrcRects; + pCReplacingEntry->paDstRects = pCEntry->paDstRects; + pCReplacingEntry->paDstUnstretchedRects = pCEntry->paDstUnstretchedRects; + } + + if (pCEntry->pfnEntryReleased) + { + PVBOXVR_SCR_COMPOSITOR_ENTRY pCReplacingEntry = pReplacingEntry ? VBOXVR_SCR_COMPOSITOR_ENTRY_FROM_ENTRY(pReplacingEntry) : NULL; + PVBOXVR_SCR_COMPOSITOR pCConpositor = VBOXVR_SCR_COMPOSITOR_FROM_COMPOSITOR(pCompositor); + pCEntry->pfnEntryReleased(pCConpositor, pCEntry, pCReplacingEntry); + } +} + +VBOXVREGDECL(int) CrVrScrCompositorRectSet(PVBOXVR_SCR_COMPOSITOR pCompositor, const RTRECT *pRect, bool *pfChanged) +{ + if (!memcmp(&pCompositor->Rect, pRect, sizeof (pCompositor->Rect))) + { + if (pfChanged) + *pfChanged = false; + return VINF_SUCCESS; + } + + pCompositor->Rect = *pRect; + + VBOXVR_SCR_COMPOSITOR_ITERATOR Iter; + CrVrScrCompositorIterInit(pCompositor, &Iter); + PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry; + while ((pEntry = CrVrScrCompositorIterNext(&Iter)) != NULL) + { + int rc = crVrScrCompositorEntryEnsureRegionsBounds(pCompositor, pEntry, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("crVrScrCompositorEntryEnsureRegionsBounds failed, rc %d", rc)); + return rc; + } + } + + return VINF_SUCCESS; +} + +VBOXVREGDECL(void) CrVrScrCompositorInit(PVBOXVR_SCR_COMPOSITOR pCompositor, const RTRECT *pRect) +{ + memset(pCompositor, 0, sizeof (*pCompositor)); + VBoxVrCompositorInit(&pCompositor->Compositor, crVrScrCompositorEntryReleasedCB); + pCompositor->fFlags = CRBLT_F_LINEAR | CRBLT_F_INVERT_YCOORDS; + if (pRect) + pCompositor->Rect = *pRect; +#ifndef IN_RING0 + pCompositor->StretchX = 1.0; + pCompositor->StretchY = 1.0; +#endif +} + +VBOXVREGDECL(void) CrVrScrCompositorRegionsClear(PVBOXVR_SCR_COMPOSITOR pCompositor, bool *pfChanged) +{ + /* set changed flag first, while entries are in the list and we have them */ + CrVrScrCompositorEntrySetAllChanged(pCompositor, true); + VBoxVrCompositorRegionsClear(&pCompositor->Compositor, pfChanged); + crVrScrCompositorRectsInvalidate(pCompositor); +} + +VBOXVREGDECL(void) CrVrScrCompositorClear(PVBOXVR_SCR_COMPOSITOR pCompositor) +{ + CrVrScrCompositorRegionsClear(pCompositor, NULL); + if (pCompositor->paDstRects) + { + RTMemFree(pCompositor->paDstRects); + pCompositor->paDstRects = NULL; + } + if (pCompositor->paSrcRects) + { + RTMemFree(pCompositor->paSrcRects); + pCompositor->paSrcRects = NULL; + } + if (pCompositor->paDstUnstretchedRects) + { + RTMemFree(pCompositor->paDstUnstretchedRects); + pCompositor->paDstUnstretchedRects = NULL; + } + + pCompositor->cRects = 0; + pCompositor->cRectsBuffer = 0; +} + +VBOXVREGDECL(void) CrVrScrCompositorEntrySetAllChanged(PVBOXVR_SCR_COMPOSITOR pCompositor, bool fChanged) +{ + VBOXVR_SCR_COMPOSITOR_ITERATOR CIter; + PVBOXVR_SCR_COMPOSITOR_ENTRY pCurEntry; + CrVrScrCompositorIterInit(pCompositor, &CIter); + + while ((pCurEntry = CrVrScrCompositorIterNext(&CIter)) != NULL) + { + CrVrScrCompositorEntrySetChanged(pCurEntry, fChanged); + } +} + +#ifndef IN_RING0 +VBOXVREGDECL(void) CrVrScrCompositorSetStretching(PVBOXVR_SCR_COMPOSITOR pCompositor, float StretchX, float StretchY) +{ + if (pCompositor->StretchX == StretchX && pCompositor->StretchY == StretchY) + return; + + pCompositor->StretchX = StretchX; + pCompositor->StretchY = StretchY; + crVrScrCompositorRectsInvalidate(pCompositor); + CrVrScrCompositorEntrySetAllChanged(pCompositor, true); +} +#endif + +/* regions are valid until the next CrVrScrCompositor call */ +VBOXVREGDECL(int) CrVrScrCompositorRegionsGet(const VBOXVR_SCR_COMPOSITOR *pCompositor, uint32_t *pcRegions, const RTRECT **ppaSrcRegions, const RTRECT **ppaDstRegions, const RTRECT **ppaDstUnstretchedRects) +{ + int rc = crVrScrCompositorRectsCheckInit(pCompositor); + if (!RT_SUCCESS(rc)) + { + WARN(("crVrScrCompositorRectsCheckInit failed, rc %d", rc)); + return rc; + } + + Assert(pCompositor->cRects != VBOXVR_SCR_COMPOSITOR_RECTS_UNDEFINED); + + *pcRegions = pCompositor->cRects; + if (ppaSrcRegions) + *ppaSrcRegions = pCompositor->paSrcRects; + if (ppaDstRegions) + *ppaDstRegions = pCompositor->paDstRects; + if (ppaDstUnstretchedRects) + *ppaDstUnstretchedRects = pCompositor->paDstUnstretchedRects; + + return VINF_SUCCESS; +} + +typedef struct VBOXVR_SCR_COMPOSITOR_VISITOR_CB +{ + PFNVBOXVRSCRCOMPOSITOR_VISITOR pfnVisitor; + void *pvVisitor; +} VBOXVR_SCR_COMPOSITOR_VISITOR_CB, *PVBOXVR_SCR_COMPOSITOR_VISITOR_CB; + +static DECLCALLBACK(bool) crVrScrCompositorVisitCb(PVBOXVR_COMPOSITOR pCCompositor, PVBOXVR_COMPOSITOR_ENTRY pCEntry, void *pvVisitor) +{ + PVBOXVR_SCR_COMPOSITOR_VISITOR_CB pData = (PVBOXVR_SCR_COMPOSITOR_VISITOR_CB)pvVisitor; + PVBOXVR_SCR_COMPOSITOR pCompositor = VBOXVR_SCR_COMPOSITOR_FROM_COMPOSITOR(pCCompositor); + PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry = VBOXVR_SCR_COMPOSITOR_ENTRY_FROM_ENTRY(pCEntry); + return pData->pfnVisitor(pCompositor, pEntry, pData->pvVisitor); +} + +VBOXVREGDECL(void) CrVrScrCompositorVisit(PVBOXVR_SCR_COMPOSITOR pCompositor, PFNVBOXVRSCRCOMPOSITOR_VISITOR pfnVisitor, void *pvVisitor) +{ + VBOXVR_SCR_COMPOSITOR_VISITOR_CB Data; + Data.pfnVisitor = pfnVisitor; + Data.pvVisitor = pvVisitor; + VBoxVrCompositorVisit(&pCompositor->Compositor, crVrScrCompositorVisitCb, &Data); +} + +VBOXVREGDECL(int) CrVrScrCompositorClone(const VBOXVR_SCR_COMPOSITOR *pCompositor, PVBOXVR_SCR_COMPOSITOR pDstCompositor, PFNVBOXVR_SCR_COMPOSITOR_ENTRY_FOR pfnEntryFor, void* pvEntryFor) +{ + /* for simplicity just copy from one to another */ + CrVrScrCompositorInit(pDstCompositor, CrVrScrCompositorRectGet(pCompositor)); + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + CrVrScrCompositorConstIterInit(pCompositor, &CIter); + int rc = VINF_SUCCESS; + uint32_t cRects; + const RTRECT *pRects; + + while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL) + { + /* get source rects, that will be non-stretched and entry pos - pased */ + rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRects, NULL, NULL, &pRects); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorEntryRegionsGet failed, rc %d", rc)); + return rc; + } + + PVBOXVR_SCR_COMPOSITOR_ENTRY pDstEntry = pfnEntryFor(pEntry, pvEntryFor); + if (!pDstEntry) + { + WARN(("pfnEntryFor failed")); + return VERR_INVALID_STATE; + } + + rc = CrVrScrCompositorEntryRegionsSet(pDstCompositor, pDstEntry, NULL, cRects, pRects, false, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorEntryRegionsSet failed, rc %d", rc)); + return rc; + } + } + + return rc; +} + +VBOXVREGDECL(int) CrVrScrCompositorIntersectList(PVBOXVR_SCR_COMPOSITOR pCompositor, const VBOXVR_LIST *pVr, bool *pfChanged) +{ + VBOXVR_SCR_COMPOSITOR_ITERATOR CIter; + PVBOXVR_SCR_COMPOSITOR_ENTRY pEntry; + CrVrScrCompositorIterInit(pCompositor, &CIter); + int rc = VINF_SUCCESS; + bool fChanged = false; + + while ((pEntry = CrVrScrCompositorIterNext(&CIter)) != NULL) + { + bool fCurChanged = false; + + rc = CrVrScrCompositorEntryListIntersect(pCompositor, pEntry, pVr, &fCurChanged); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorEntryRegionsSet failed, rc %d", rc)); + break; + } + + fChanged |= fCurChanged; + } + + if (pfChanged) + *pfChanged = fChanged; + + return rc; +} + +VBOXVREGDECL(int) CrVrScrCompositorIntersectedList(const VBOXVR_SCR_COMPOSITOR *pCompositor, const VBOXVR_LIST *pVr, PVBOXVR_SCR_COMPOSITOR pDstCompositor, PFNVBOXVR_SCR_COMPOSITOR_ENTRY_FOR pfnEntryFor, void* pvEntryFor, bool *pfChanged) +{ + int rc = CrVrScrCompositorClone(pCompositor, pDstCompositor, pfnEntryFor, pvEntryFor); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorClone failed, rc %d", rc)); + return rc; + } + + rc = CrVrScrCompositorIntersectList(pDstCompositor, pVr, pfChanged); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorIntersectList failed, rc %d", rc)); + CrVrScrCompositorClear(pDstCompositor); + return rc; + } + + return VINF_SUCCESS; +} + diff --git a/src/VBox/GuestHost/OpenGL/util/dll.c b/src/VBox/GuestHost/OpenGL/util/dll.c index a3245a36..dbc61759 100644 --- a/src/VBox/GuestHost/OpenGL/util/dll.c +++ b/src/VBox/GuestHost/OpenGL/util/dll.c @@ -18,6 +18,10 @@ #include <dlfcn.h> #endif +#ifdef WINDOWS +#include <Shlwapi.h> +#endif + #ifdef DARWIN #include <Carbon/Carbon.h> @@ -147,7 +151,6 @@ int get_dll_type( const char *name ) { #endif - /* * Open the named shared library. * If resolveGlobal is non-zero, unresolved symbols can be satisfied by @@ -162,14 +165,77 @@ CRDLL *crDLLOpen( const char *dllname, int resolveGlobal ) { CRDLL *dll; char *dll_err; +#if defined(WINDOWS) + WCHAR szwPath[MAX_PATH]; + UINT cwcPath = 0; + + (void) resolveGlobal; + +# ifndef CR_NO_GL_SYSTEM_PATH + if (PathIsRelative(dllname)) + { + size_t cName = strlen(dllname) + 1; +# ifdef IN_GUEST + cwcPath = GetSystemDirectoryW(szwPath, RT_ELEMENTS(szwPath)); + if (!cwcPath || cwcPath >= MAX_PATH) + { + DWORD winEr = GetLastError(); + crError("GetSystemDirectoryW failed err %d", winEr); + SetLastError(winEr); + return NULL; + } +# else + WCHAR * pszwSlashFile; + cwcPath = GetModuleFileNameW(NULL, szwPath, RT_ELEMENTS(szwPath)); + if (!cwcPath || cwcPath >= MAX_PATH) + { + DWORD winEr = GetLastError(); + crError("GetModuleFileNameW failed err %d", winEr); + SetLastError(winEr); + return NULL; + } + + pszwSlashFile = wcsrchr(szwPath, L'\\'); + if (!pszwSlashFile) + { + crError("failed to match file name"); + SetLastError(ERROR_PATH_NOT_FOUND); + return NULL; + } + + cwcPath = pszwSlashFile - szwPath; +# endif + + if (cwcPath + 1 + cName > MAX_PATH) + { + crError("invalid path specified"); + SetLastError(ERROR_FILENAME_EXCED_RANGE); + return NULL; + } + szwPath[cwcPath] = '\\'; + ++cwcPath; + } + + if (!MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, dllname, -1, &szwPath[cwcPath], MAX_PATH - cwcPath)) + { + DWORD winEr = GetLastError(); + crError("MultiByteToWideChar failed err %d", winEr); + SetLastError(winEr); + return NULL; + } +# endif // CR_NO_GL_SYSTEM_PATH +#endif dll = (CRDLL *) crAlloc( sizeof( CRDLL ) ); dll->name = crStrdup( dllname ); #if defined(WINDOWS) - (void) resolveGlobal; - dll->hinstLib = LoadLibrary( dllname ); - dll_err = NULL; + dll->hinstLib = LoadLibraryW( szwPath ); + if (!dll->hinstLib) + { + crError("failed to load dll %s", dllname); + } + dll_err = NULL; #elif defined(DARWIN) /* XXX \todo Get better error handling in here */ dll->type = get_dll_type( dllname ); diff --git a/src/VBox/GuestHost/OpenGL/util/error.c b/src/VBox/GuestHost/OpenGL/util/error.c index 4a8254b3..48fbc577 100644 --- a/src/VBox/GuestHost/OpenGL/util/error.c +++ b/src/VBox/GuestHost/OpenGL/util/error.c @@ -125,7 +125,7 @@ static void __crCheckAustralia(void) static void outputChromiumMessage( FILE *output, char *str ) { - fprintf( output, "%s%s%s%s\n", str, + fprintf( output, "%s%s%s%s\n", str, swedish_chef ? " BORK BORK BORK!" : "", canada ? ", eh?" : "", australia ? ", mate!" : "" @@ -219,7 +219,7 @@ DECLEXPORT(void) crError(const char *format, ... ) MessageBox( NULL, txt, "Chromium Error", MB_OK ); } else - { + { #endif va_end( args ); #ifdef WINDOWS @@ -241,10 +241,13 @@ DECLEXPORT(void) crError(const char *format, ... ) } void crEnableWarnings(int onOff) -{ +{ warnings_enabled = onOff; } +#ifdef DEBUG_misha +# undef crWarning +#endif DECLEXPORT(void) crWarning(const char *format, ... ) { if (warnings_enabled) { @@ -268,7 +271,7 @@ DECLEXPORT(void) crWarning(const char *format, ... ) #endif va_end( args ); -#if defined(WINDOWS) && defined(DEBUG) && !defined(IN_GUEST) +#if defined(WINDOWS) && defined(DEBUG) && !defined(IN_GUEST) && defined(DEBUG_misha) DebugBreak(); #endif } @@ -373,13 +376,13 @@ DECLEXPORT(void) crDebug(const char *format, ... ) if (crStrlen(fnamePrefix) < sizeof (str) - sizeof (pname) - 20) { crGetProcName(pname, 1024); - sprintf(str, "%s_%s_%u.txt", fnamePrefix, pname, + sprintf(str, #ifdef RT_OS_WINDOWS - GetCurrentProcessId() + "%s_%s_%u.txt", fnamePrefix, pname, GetCurrentProcessId() #else - crGetPID() + "%s_%s_%lu.txt", fnamePrefix, pname, crGetPID() #endif - ); + ); fname = &str[0]; } } @@ -399,7 +402,7 @@ DECLEXPORT(void) crDebug(const char *format, ... ) output = fopen( fname, "w" ); if (!output) { - crError( "Couldn't open debug log %s", fname ); + crError( "Couldn't open debug log %s", fname ); } } else @@ -495,18 +498,16 @@ DECLEXPORT(void) crDebug(const char *format, ... ) outputChromiumMessage( output, txt ); #else if (!output -# ifndef DEBUG_misha +#ifndef DEBUG_misha || output==stderr -# endif +#endif ) { LogRel(("%s\n", txt)); } else { -# ifndef DEBUG_misha LogRel(("%s\n", txt)); -# endif outputChromiumMessage(output, txt); } #endif diff --git a/src/VBox/GuestHost/OpenGL/util/hash.c b/src/VBox/GuestHost/OpenGL/util/hash.c index c91c4ad0..95f3ab65 100644 --- a/src/VBox/GuestHost/OpenGL/util/hash.c +++ b/src/VBox/GuestHost/OpenGL/util/hash.c @@ -9,20 +9,25 @@ #include "cr_mem.h" #include "cr_error.h" +#include <iprt/list.h> + #define CR_MAXUINT ((GLuint) 0xFFFFFFFF) +#define CR_HASH_ID_MIN ((GLuint)1) +#define CR_HASH_ID_MAX CR_MAXUINT #define CR_NUM_BUCKETS 1047 typedef struct FreeElemRec { + RTLISTNODE Node; GLuint min; GLuint max; - struct FreeElemRec *next; - struct FreeElemRec *prev; } FreeElem; -typedef struct CRHashIdPoolRec { - FreeElem *freeList; -} CRHashIdPool; +struct CRHashIdPool { + RTLISTNODE freeList; + GLuint min; + GLuint max; +}; typedef struct CRHashNode { unsigned long key; @@ -40,86 +45,124 @@ struct CRHashTable { }; -static CRHashIdPool *crAllocHashIdPool( void ) +CRHashIdPool *crAllocHashIdPoolEx( GLuint min, GLuint max ) { - CRHashIdPool *pool = (CRHashIdPool *) crCalloc(sizeof(CRHashIdPool)); - pool->freeList = (FreeElem *) crCalloc(sizeof(FreeElem)); - pool->freeList->min = 1; - pool->freeList->max = CR_MAXUINT; - pool->freeList->next = NULL; - pool->freeList->prev = NULL; + CRHashIdPool *pool; + FreeElem *elem; + if (min < CR_HASH_ID_MIN || max > CR_HASH_ID_MAX || min >= max) + { + crWarning("invalid min man vals"); + return NULL; + } + pool = (CRHashIdPool *) crCalloc(sizeof(CRHashIdPool)); + elem = (FreeElem *) crCalloc(sizeof(FreeElem)); + RTListInit(&pool->freeList); + elem->min = min; + elem->max = max; + RTListAppend(&pool->freeList, &elem->Node); + pool->min = min; + pool->max = max; return pool; } -static void crFreeHashIdPool( CRHashIdPool *pool ) +CRHashIdPool *crAllocHashIdPool( void ) +{ + return crAllocHashIdPoolEx( CR_HASH_ID_MIN, CR_HASH_ID_MAX ); +} + +void crFreeHashIdPool( CRHashIdPool *pool ) { FreeElem *i, *next; - for (i = pool->freeList; i; i = next) + RTListForEachSafe(&pool->freeList, i, next, FreeElem, Node) { - next = i->next; crFree(i); } + crFree(pool); } +#ifdef DEBUG_misha +static void crHashIdPoolDbgCheckConsistency(CRHashIdPool *pool) +{ + FreeElem *i; + GLuint min = 0; + + /* null is a special case, it is always treated as allocated */ + Assert(!crHashIdPoolIsIdFree(pool, 0)); + + /* first ensure entries have correct values */ + RTListForEach(&pool->freeList, i, FreeElem, Node) + { + Assert(i->min >= pool->min); + Assert(i->max <= pool->max); + Assert(i->min < i->max); + } + + /* now ensure entries do not intersect */ + /* and that they are sorted */ + RTListForEach(&pool->freeList, i, FreeElem, Node) + { + Assert(min < i->min); + min = i->max; + } +} + +static void crHashIdPoolDbgCheckUsed( const CRHashIdPool *pool, GLuint start, GLuint count, GLboolean fUsed ) +{ + GLuint i; + CRASSERT(count); + CRASSERT(start >= pool->min); + CRASSERT(start + count <= pool->max); + CRASSERT(start + count > start); + for (i = 0; i < count; ++i) + { + Assert(!fUsed == !!crHashIdPoolIsIdFree( pool, start + i )); + } +} + +# define CR_HASH_IDPOOL_DBG_CHECK_USED(_p, _start, _count, _used) do { \ + crHashIdPoolDbgCheckConsistency((_p)); \ + crHashIdPoolDbgCheckUsed( (_p), (_start), (_count), (_used) ); \ + } while (0) + +# define CR_HASH_IDPOOL_DBG_CHECK_CONSISTENCY(_p) do { crHashIdPoolDbgCheckConsistency((_p)); } while (0) +#else +# define CR_HASH_IDPOOL_DBG_CHECK_USED(_p, _start, _count, _used) do { } while (0) +# define CR_HASH_IDPOOL_DBG_CHECK_CONSISTENCY(_p) do { } while (0) +#endif + /* * Allocate a block of <count> IDs. Return index of first one. * Return 0 if we fail. */ -static GLuint crHashIdPoolAllocBlock( CRHashIdPool *pool, GLuint count ) +GLuint crHashIdPoolAllocBlock( CRHashIdPool *pool, GLuint count ) { - FreeElem *f; + FreeElem *f, *next; GLuint ret; CRASSERT(count > 0); - - f = pool->freeList; - while (f) + RTListForEachSafe(&pool->freeList, f, next, FreeElem, Node) { - if (f->max - f->min + 1 >= (GLuint) count) + Assert(f->max > f->min); + if (f->max - f->min >= (GLuint) count) { /* found a sufficiently large enough block */ ret = f->min; f->min += count; - if (f->min == f->max) { - /* remove this block from linked list */ - if (f == pool->freeList) - { - /* remove from head */ - pool->freeList = pool->freeList->next; - pool->freeList->prev = NULL; - } - else - { - /* remove from elsewhere */ - f->prev->next = f->next; - f->next->prev = f->prev; - } + RTListNodeRemove(&f->Node); crFree(f); } -#ifdef DEBUG - /* make sure the IDs really are allocated */ - { - GLuint i; - for (i = 0; i < count; i++) - { - //CRASSERT(crHashIdPoolIsIdUsed(pool, ret + i)); - } - } -#endif - + CR_HASH_IDPOOL_DBG_CHECK_USED(pool, ret, count, GL_TRUE); return ret; } - else { - f = f->next; - } } /* failed to find free block */ - crDebug("crHashIdPoolAllocBlock failed"); + crWarning("crHashIdPoolAllocBlock failed"); + CR_HASH_IDPOOL_DBG_CHECK_CONSISTENCY(pool); return 0; } @@ -127,107 +170,91 @@ static GLuint crHashIdPoolAllocBlock( CRHashIdPool *pool, GLuint count ) /* * Free a block of <count> IDs starting at <first>. */ -static void crHashIdPoolFreeBlock( CRHashIdPool *pool, GLuint first, GLuint count ) +void crHashIdPoolFreeBlock( CRHashIdPool *pool, GLuint first, GLuint count ) { - FreeElem *i; - FreeElem *newelem; - - /*********************************/ - /* Add the name to the freeList */ - /* Find the bracketing sequences */ + FreeElem *f; + GLuint last; + GLuint newMax; + FreeElem *cur, *curNext; - for (i = pool->freeList; i && i->next && i->next->min < first; i = i->next) + /* null is a special case, it is always treated as allocated */ + if (!first) { - /* EMPTY BODY */ + Assert(!crHashIdPoolIsIdFree(pool, 0)); + ++first; + --count; + if (!count) + return; } - /* j will always be valid */ - if (!i) { - return; - } - if (!i->next && i->max == first) { - return; - } + last = first + count; + CRASSERT(count > 0); + CRASSERT(last > first); + CRASSERT(first >= pool->min); + CRASSERT(last <= pool->max); - /* Case: j:(~,first-1) */ - if (i->max + 1 == first) + /* the id list is sorted, first find a place to insert */ + RTListForEach(&pool->freeList, f, FreeElem, Node) { - i->max += count; - if (i->next && i->max+1 >= i->next->min) + Assert(f->max > f->min); + + if (f->max < first) + continue; + + if (f->min > last) { - /* Collapse */ - i->next->min = i->min; - i->next->prev = i->prev; - if (i->prev) - { - i->prev->next = i->next; - } - if (i == pool->freeList) - { - pool->freeList = i->next; - } - crFree(i); + /* we are here because first is > than prevEntry->max + * otherwise the previous loop iterations should handle that */ + FreeElem *elem = (FreeElem *) crCalloc(sizeof(FreeElem)); + elem->min = first; + elem->max = last; + RTListNodeInsertBefore(&f->Node, &elem->Node); + CR_HASH_IDPOOL_DBG_CHECK_USED(pool, first, count, GL_FALSE); + return; } - return; - } - /* Case: j->next: (first+1, ~) */ - if (i->next && i->next->min - count == first) - { - i->next->min -= count; - if (i->max + 1 >= i->next->min) + /* now we have f->min <= last and f->max >= first, + * so we have either intersection */ + + if (f->min > first) + f->min = first; /* first is guaranteed not to touch any prev regions */ + + newMax = last; + + if (f->max >= last) { - /* Collapse */ - i->next->min = i->min; - i->next->prev = i->prev; - if (i->prev) - { - i->prev->next = i->next; - } - if (i == pool->freeList) - { - pool->freeList = i->next; - } - crFree(i); + CR_HASH_IDPOOL_DBG_CHECK_USED(pool, first, count, GL_FALSE); + return; } - return; - } - /* Case: j: (first+1, ~) j->next: null */ - if (!i->next && i->min - count == first) - { - i->min -= count; - return; - } + for (cur = RTListNodeGetNext(&f->Node, FreeElem, Node), + curNext = RT_FROM_MEMBER(cur->Node.pNext, FreeElem, Node); + !RTListNodeIsDummy(&pool->freeList, cur, FreeElem, Node); + cur = curNext, + curNext = RT_FROM_MEMBER((cur)->Node.pNext, FreeElem, Node) ) + { + if (cur->min > last) + break; - /* allocate a new FreeElem node */ - newelem = (FreeElem *) crCalloc(sizeof(FreeElem)); - newelem->min = first; - newelem->max = first + count - 1; + newMax = cur->max; + RTListNodeRemove(&cur->Node); + crFree(cur); - /* Case: j: (~,first-(2+)) j->next: (first+(2+), ~) or null */ - if (first > i->max) - { - newelem->prev = i; - newelem->next = i->next; - if (i->next) - { - i->next->prev = newelem; + if (newMax >= last) + break; } - i->next = newelem; - return; - } - /* Case: j: (first+(2+), ~) */ - /* Can only happen if j = t->freeList! */ - if (i == pool->freeList && i->min > first) - { - newelem->next = i; - newelem->prev = i->prev; - i->prev = newelem; - pool->freeList = newelem; + f->max = newMax; + CR_HASH_IDPOOL_DBG_CHECK_USED(pool, first, count, GL_FALSE); return; } + + /* we are here because either the list is empty or because all list rande elements have smaller values */ + f = (FreeElem *) crCalloc(sizeof(FreeElem)); + f->min = first; + f->max = last; + RTListAppend(&pool->freeList, &f->Node); + CR_HASH_IDPOOL_DBG_CHECK_USED(pool, first, count, GL_FALSE); } @@ -235,68 +262,116 @@ static void crHashIdPoolFreeBlock( CRHashIdPool *pool, GLuint first, GLuint coun /* * Mark the given Id as being allocated. */ -static void crHashIdPoolAllocId( CRHashIdPool *pool, GLuint id ) +GLboolean crHashIdPoolAllocId( CRHashIdPool *pool, GLuint id ) { - FreeElem *f; + FreeElem *f, *next; + + if (!id) + { + /* null is a special case, it is always treated as allocated */ + Assert(!crHashIdPoolIsIdFree(pool, 0)); + return GL_FALSE; + } + +// Assert(id != 2); - f = pool->freeList; - while (f) + RTListForEachSafe(&pool->freeList, f, next, FreeElem, Node) { - if (id >= f->min && id <= f->max) + if (f->max <= id) + continue; + if (f->min > id) { - /* found the block */ - if (id == f->min) + CR_HASH_IDPOOL_DBG_CHECK_USED(pool, id, 1, GL_TRUE); + return GL_FALSE; + } + + /* f->min <= id && f->max > id */ + if (id > f->min) + { + if (id + 1 < f->max) { - f->min++; + FreeElem *elem = (FreeElem *) crCalloc(sizeof(FreeElem)); + elem->min = id + 1; + elem->max = f->max; + RTListNodeInsertAfter(&f->Node, &elem->Node); } - else if (id == f->max) + f->max = id; + + CR_HASH_IDPOOL_DBG_CHECK_USED(pool, id, 1, GL_TRUE); + } + else + { + Assert(id == f->min); + if (id + 1 < f->max) { - f->max--; + f->min = id + 1; + CR_HASH_IDPOOL_DBG_CHECK_USED(pool, id, 1, GL_TRUE); } else { - /* somewhere in the middle - split the block */ - FreeElem *newelem = (FreeElem *) crCalloc(sizeof(FreeElem)); - newelem->min = id + 1; - newelem->max = f->max; - f->max = id - 1; - newelem->next = f->next; - if (f->next) - f->next->prev = newelem; - newelem->prev = f; - f->next = newelem; + RTListNodeRemove(&f->Node); + crFree(f); + CR_HASH_IDPOOL_DBG_CHECK_USED(pool, id, 1, GL_TRUE); } - return; } - f = f->next; + + CR_HASH_IDPOOL_DBG_CHECK_USED(pool, id, 1, GL_TRUE); + return GL_TRUE; } /* if we get here, the ID was already allocated - that's OK */ + CR_HASH_IDPOOL_DBG_CHECK_USED(pool, id, 1, GL_TRUE); + return GL_FALSE; } /* * Determine if the given id is free. Return GL_TRUE if so. */ -static GLboolean crHashIdPoolIsIdFree( const CRHashIdPool *pool, GLuint id ) +GLboolean crHashIdPoolIsIdFree( const CRHashIdPool *pool, GLuint id ) { - FreeElem *i; + FreeElem *f; + CRASSERT(id <= pool->max); - /* First find which region it fits in */ - for (i = pool->freeList; i && !(i->min <= id && id <= i->max); i=i->next) + RTListForEach(&pool->freeList, f, FreeElem, Node) { - /* EMPTY BODY */ - } - - if (i) + if (f->max <= id) + continue; + if (f->min > id) + return GL_FALSE; return GL_TRUE; - else - return GL_FALSE; + } + return GL_FALSE; } +void crHashIdWalkKeys( CRHashIdPool *pool, CRHashIdWalkKeys walkFunc , void *data) +{ + FreeElem *prev = NULL, *f; + + RTListForEach(&pool->freeList, f, FreeElem, Node) + { + if (prev) + { + Assert(prev->max < f->min); + walkFunc(prev->max+1, f->min - prev->max, data); + } + else if (f->min > pool->min) + { + walkFunc(pool->min, f->min - pool->min, data); + } + + prev = f; + } + Assert(prev->max <= pool->max); -CRHashTable *crAllocHashtable( void ) + if (prev->max < pool->max) + { + walkFunc(prev->max+1, pool->max - prev->max, data); + } +} + +CRHashTable *crAllocHashtableEx( GLuint min, GLuint max ) { int i; CRHashTable *hash = (CRHashTable *) crCalloc( sizeof( CRHashTable )) ; @@ -305,13 +380,18 @@ CRHashTable *crAllocHashtable( void ) { hash->buckets[i] = NULL; } - hash->idPool = crAllocHashIdPool(); + hash->idPool = crAllocHashIdPoolEx( min, max ); #ifdef CHROMIUM_THREADSAFE crInitMutex(&hash->mutex); #endif return hash; } +CRHashTable *crAllocHashtable( void ) +{ + return crAllocHashtableEx(CR_HASH_ID_MIN, CR_HASH_ID_MAX); +} + void crFreeHashtable( CRHashTable *hash, CRHashtableCallback deleteFunc ) { int i; @@ -366,17 +446,11 @@ void crHashtableUnlock(CRHashTable *h) #endif } -void crHashtableWalk( CRHashTable *hash, CRHashtableWalkCallback walkFunc , void *dataPtr2) +void crHashtableWalkUnlocked( CRHashTable *hash, CRHashtableWalkCallback walkFunc , void *dataPtr2) { int i; CRHashNode *entry, *next; - if (!hash) - return; - -#ifdef CHROMIUM_THREADSAFE - crLockMutex(&hash->mutex); -#endif for (i = 0; i < CR_NUM_BUCKETS; i++) { entry = hash->buckets[i]; @@ -390,6 +464,17 @@ void crHashtableWalk( CRHashTable *hash, CRHashtableWalkCallback walkFunc , void entry = next; } } +} + +void crHashtableWalk( CRHashTable *hash, CRHashtableWalkCallback walkFunc , void *dataPtr2) +{ + if (!hash) + return; + +#ifdef CHROMIUM_THREADSAFE + crLockMutex(&hash->mutex); +#endif + crHashtableWalkUnlocked(hash, walkFunc , dataPtr2); #ifdef CHROMIUM_THREADSAFE crUnlockMutex(&hash->mutex); #endif @@ -417,6 +502,30 @@ void crHashtableAdd( CRHashTable *h, unsigned long key, void *data ) #endif } +GLboolean crHashtableAllocRegisterKey( CRHashTable *h, GLuint key) +{ + GLboolean fAllocated; +#ifdef CHROMIUM_THREADSAFE + crLockMutex(&h->mutex); +#endif + fAllocated = crHashIdPoolAllocId (h->idPool, key); +#ifdef CHROMIUM_THREADSAFE + crUnlockMutex(&h->mutex); +#endif + return fAllocated; +} + +void crHashtableWalkKeys( CRHashTable *h, CRHashIdWalkKeys walkFunc , void *data) +{ +#ifdef CHROMIUM_THREADSAFE + crLockMutex(&h->mutex); +#endif + crHashIdWalkKeys(h->idPool, walkFunc , data); +#ifdef CHROMIUM_THREADSAFE + crUnlockMutex(&h->mutex); +#endif +} + GLuint crHashtableAllocKeys( CRHashTable *h, GLsizei range) { GLuint res; @@ -426,6 +535,14 @@ GLuint crHashtableAllocKeys( CRHashTable *h, GLsizei range) crLockMutex(&h->mutex); #endif res = crHashIdPoolAllocBlock (h->idPool, range); +#ifdef DEBUG_misha + Assert(res); + for (i = 0; i < range; ++i) + { + void *search = crHashtableSearch( h, res+i ); + Assert(!search); + } +#endif #ifdef CHROMIUM_THREADSAFE crUnlockMutex(&h->mutex); #endif @@ -446,23 +563,20 @@ void crHashtableDelete( CRHashTable *h, unsigned long key, CRHashtableCallback d break; beftemp = temp; } - if ( !temp ) { -#ifdef CHROMIUM_THREADSAFE - crUnlockMutex(&h->mutex); -#endif - return; /* not an error */ - } - if ( beftemp ) - beftemp->next = temp->next; - else - h->buckets[index] = temp->next; - h->num_elements--; - if (temp->data && deleteFunc) { - (*deleteFunc)( temp->data ); - } - - crFree( temp ); + if ( temp ) + { + if ( beftemp ) + beftemp->next = temp->next; + else + h->buckets[index] = temp->next; + h->num_elements--; + if (temp->data && deleteFunc) { + (*deleteFunc)( temp->data ); + } + crFree( temp ); + } + crHashIdPoolFreeBlock( h->idPool, key, 1 ); #ifdef CHROMIUM_THREADSAFE crUnlockMutex(&h->mutex); diff --git a/src/VBox/GuestHost/OpenGL/util/htable.cpp b/src/VBox/GuestHost/OpenGL/util/htable.cpp new file mode 100644 index 00000000..bb9434c7 --- /dev/null +++ b/src/VBox/GuestHost/OpenGL/util/htable.cpp @@ -0,0 +1,195 @@ +/* $Id: htable.cpp $ */ + +/** @file + * uint32_t handle to void simple table impl + */ + +/* + * Copyright (C) 2013 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 <iprt/cdefs.h> +#include <iprt/asm.h> +#include "cr_spu.h" +#include "cr_vreg.h" + +#include "cr_htable.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 <iprt/cdefs.h> +#include <iprt/types.h> +#include <iprt/mem.h> +#include <iprt/err.h> + +VBOXHTABLEDECL(int) CrHTableCreate(PCRHTABLE pTbl, uint32_t cSize) +{ + memset(pTbl, 0, sizeof (*pTbl)); + if (!cSize) + return VINF_SUCCESS; + pTbl->paData = (void**)RTMemAllocZ(sizeof (pTbl->paData[0]) * cSize); + if (pTbl->paData) + { + pTbl->cSize = cSize; + return VINF_SUCCESS; + } + WARN(("RTMemAllocZ failed!")); + return VERR_NO_MEMORY; +} + +VBOXHTABLEDECL(void) CrHTableDestroy(PCRHTABLE pTbl) +{ + if (!pTbl->paData) + return; + + RTMemFree(pTbl->paData); +} + +int crHTableRealloc(PCRHTABLE pTbl, uint32_t cNewSize) +{ + Assert(cNewSize > pTbl->cSize); + if (cNewSize > pTbl->cSize) + { + void **pvNewData = (void**)RTMemAllocZ(sizeof (pTbl->paData[0]) * cNewSize); + if (!pvNewData) + { + WARN(("RTMemAllocZ failed for size (%d)", (int)(sizeof (pTbl->paData[0]) * cNewSize))); + return VERR_NO_MEMORY; + } + memcpy(pvNewData, pTbl->paData, sizeof (pTbl->paData[0]) * pTbl->cSize); + RTMemFree(pTbl->paData); + pTbl->iNext2Search = pTbl->cSize; + pTbl->cSize = cNewSize; + pTbl->paData = pvNewData; + return VINF_SUCCESS; + } + else if (cNewSize >= pTbl->cData) + { + WARN(("not implemented")); + return VERR_NOT_IMPLEMENTED; + } + WARN(("invalid parameter")); + return VERR_INVALID_PARAMETER; + +} + +VBOXHTABLEDECL(int) CrHTableRealloc(PCRHTABLE pTbl, uint32_t cNewSize) +{ + return crHTableRealloc(pTbl, cNewSize); +} + +VBOXHTABLEDECL(void) CrHTableEmpty(PCRHTABLE pTbl) +{ + pTbl->cData = 0; + pTbl->iNext2Search = 0; + if (pTbl->cSize) + memset(pTbl->paData, 0, sizeof (pTbl->paData[0]) * pTbl->cSize); +} + +static void* crHTablePutToSlot(PCRHTABLE pTbl, uint32_t iSlot, void* pvData) +{ + Assert(pvData); + void* pvOld = pTbl->paData[iSlot]; + pTbl->paData[iSlot] = pvData; + if (!pvOld) + ++pTbl->cData; + Assert(pTbl->cData <= pTbl->cSize); + return pvOld; +} + +VBOXHTABLEDECL(int) CrHTablePutToSlot(PCRHTABLE pTbl, CRHTABLE_HANDLE hHandle, void* pvData) +{ + if (!pvData) + { + AssertMsgFailed(("pvData is NULL\n")); + return VERR_INVALID_PARAMETER; + } + uint32_t iIndex = crHTableHandle2Index(hHandle); + if (iIndex >= pTbl->cSize) + { + int rc = crHTableRealloc(pTbl, iIndex + RT_MAX(10, pTbl->cSize/4)); + if (!RT_SUCCESS(rc)) + { + WARN(("crHTableRealloc failed rc %d", rc)); + return CRHTABLE_HANDLE_INVALID; + } + } + + crHTablePutToSlot(pTbl, iIndex, pvData); + + return VINF_SUCCESS; +} + +VBOXHTABLEDECL(CRHTABLE_HANDLE) CrHTablePut(PCRHTABLE pTbl, void* pvData) +{ + if (!pvData) + { + AssertMsgFailed(("pvData is NULL\n")); + return VERR_INVALID_PARAMETER; + } + + if (pTbl->cSize == pTbl->cData) + { + int rc = crHTableRealloc(pTbl, pTbl->cSize + RT_MAX(10, pTbl->cSize/4)); + if (!RT_SUCCESS(rc)) + { + WARN(("crHTableRealloc failed rc %d", rc)); + return CRHTABLE_HANDLE_INVALID; + } + } + for (uint32_t i = pTbl->iNext2Search; ; ++i, i %= pTbl->cSize) + { + Assert(i < pTbl->cSize); + if (!pTbl->paData[i]) + { + void *pvOld = crHTablePutToSlot(pTbl, i, pvData); + Assert(!pvOld); + pTbl->iNext2Search = i+1; + pTbl->iNext2Search %= pTbl->cSize; + return crHTableIndex2Handle(i); + } + } + WARN(("should not be here")); + return CRHTABLE_HANDLE_INVALID; +} + +VBOXHTABLEDECL(void*) CrHTableRemove(PCRHTABLE pTbl, CRHTABLE_HANDLE hHandle) +{ + uint32_t iIndex = crHTableHandle2Index(hHandle); + Assert(iIndex < pTbl->cSize); + if (iIndex < pTbl->cSize) + { + void* pvData = pTbl->paData[iIndex]; + if (pvData) + { + pTbl->paData[iIndex] = NULL; + --pTbl->cData; + Assert(pTbl->cData <= pTbl->cSize); + pTbl->iNext2Search = iIndex; + } + return pvData; + } + WARN(("invalid handle supplied %d", hHandle)); + return NULL; +} + +VBOXHTABLEDECL(void*) CrHTableGet(PCRHTABLE pTbl, CRHTABLE_HANDLE hHandle) +{ + uint32_t iIndex = crHTableHandle2Index(hHandle); + if (iIndex < pTbl->cSize) + return pTbl->paData[iIndex]; + LOG(("invalid handle supplied %d", hHandle)); + return NULL; +} diff --git a/src/VBox/GuestHost/OpenGL/util/net.c b/src/VBox/GuestHost/OpenGL/util/net.c index cc2966f9..72d9f7ae 100644 --- a/src/VBox/GuestHost/OpenGL/util/net.c +++ b/src/VBox/GuestHost/OpenGL/util/net.c @@ -993,7 +993,7 @@ crNetRecvFlowControl( CRConnection *conn, CRMessageFlowControl *msg, conn->InstantReclaim( conn, (CRMessage *) msg ); } - +#ifdef IN_GUEST /** * Called by the main receive function when we get a CR_MESSAGE_WRITEBACK * message. Writeback is used to implement glGet*() functions. @@ -1026,7 +1026,7 @@ crNetRecvReadback( CRMessageReadback *rb, unsigned int len ) (*writeback)--; crMemcpy( dest_ptr, ((char *)rb) + sizeof(*rb), payload_len ); } - +#endif /** * This is used by the SPUs that do packing (such as Pack, Tilesort and @@ -1104,13 +1104,21 @@ crNetDefaultRecv( CRConnection *conn, CRMessage *msg, unsigned int len ) } break; case CR_MESSAGE_READ_PIXELS: - crError( "Can't handle read pixels" ); + WARN(( "Can't handle read pixels" )); return; case CR_MESSAGE_WRITEBACK: +#ifdef IN_GUEST crNetRecvWriteback( &(pRealMsg->writeback) ); +#else + WARN(("CR_MESSAGE_WRITEBACK not expected\n")); +#endif return; case CR_MESSAGE_READBACK: +#ifdef IN_GUEST crNetRecvReadback( &(pRealMsg->readback), len ); +#else + WARN(("CR_MESSAGE_READBACK not expected\n")); +#endif return; case CR_MESSAGE_CRUT: /* nothing */ @@ -1128,10 +1136,10 @@ crNetDefaultRecv( CRConnection *conn, CRMessage *msg, unsigned int len ) { char string[128]; crBytesToString( string, sizeof(string), msg, len ); - crError("crNetDefaultRecv: received a bad message: type=%d buf=[%s]\n" + WARN(("crNetDefaultRecv: received a bad message: type=%d buf=[%s]\n" "Did you add a new message type and forget to tell " "crNetDefaultRecv() about it?\n", - msg->header.type, string ); + msg->header.type, string )); } } diff --git a/src/VBox/GuestHost/OpenGL/util/pixel.c b/src/VBox/GuestHost/OpenGL/util/pixel.c index c153dee2..5bea3e1f 100644 --- a/src/VBox/GuestHost/OpenGL/util/pixel.c +++ b/src/VBox/GuestHost/OpenGL/util/pixel.c @@ -11,6 +11,8 @@ #include <stdio.h> #include <math.h> +#include <iprt/string.h> + #if defined(WINDOWS) # include <float.h> # define isnan(x) _isnan(x) @@ -1691,7 +1693,9 @@ void crPixelCopy3D( GLsizei width, GLsizei height, GLsizei depth, /*@todo this should be implemented properly*/ +#ifndef DEBUG_misha crWarning( "crPixelCopy3D: simply crMemcpy'ing from srcPtr to dstPtr" ); +#endif if (dstFormat != srcFormat) crWarning( "crPixelCopy3D: formats don't match!" ); if (dstType != srcType) @@ -1841,3 +1845,19 @@ void crDumpNamedTGA(const char* fname, GLint w, GLint h, GLvoid *data) fclose(out); } + +void crDumpNamedTGAV(GLint w, GLint h, GLvoid *data, const char* fname, va_list va) +{ + char szName[4096]; + RTStrPrintfV(szName, sizeof(szName), fname, va); + crDumpNamedTGA(szName, w, h, data); +} + +void crDumpNamedTGAF(GLint w, GLint h, GLvoid *data, const char* fname, ...) +{ + va_list va; + int rc; + va_start(va, fname); + crDumpNamedTGAV(w, h, data, fname, va); + va_end(va); +} diff --git a/src/VBox/GuestHost/OpenGL/util/string.c b/src/VBox/GuestHost/OpenGL/util/string.c index 75df10bc..0d7fff0b 100644 --- a/src/VBox/GuestHost/OpenGL/util/string.c +++ b/src/VBox/GuestHost/OpenGL/util/string.c @@ -6,6 +6,7 @@ #include "cr_mem.h" #include "cr_string.h" +#include "cr_error.h" #include <string.h> #include <stdio.h> @@ -408,3 +409,125 @@ int crIsDigit(char c) { return c >= '0' && c <= '9'; } + + +static int crStrParseGlSubver(const char * ver, const char ** pNext, bool bSpacePrefixAllowed) +{ + const char * initVer = ver; + 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 of version string */ + *pNext = NULL; + break; + } + else + { + crWarning("error parsing version %s", initVer); + val = -1; + break; + } + } + + return val; +} + +int crStrParseGlVersion(const char * ver) +{ + const char * initVer = ver; + int tmp; + int iVer = crStrParseGlSubver(ver, &ver, true); + if(iVer <= 0) + { + crWarning("parsing major version returned %d, '%s'", iVer, initVer); + return iVer; + } + + if (iVer > CR_GLVERSION_MAX_MAJOR) + { + crWarning("major version %d is bigger than the max supported %#x, this is somewhat not expected, failing", iVer, CR_GLVERSION_MAX_MAJOR); + return -1; + } + + iVer <<= CR_GLVERSION_OFFSET_MAJOR; + if(!ver) + { + crDebug("no minor version supplied"); + goto done; + } + + tmp = crStrParseGlSubver(ver, &ver, false); + if (tmp < 0) + { + crWarning("parsing minor version failed, '%s'", initVer); + return -1; + } + + if (tmp > CR_GLVERSION_MAX_MINOR) + { + crWarning("minor version %d is bigger than the max supported %#x, this is somewhat not expected, failing", iVer, CR_GLVERSION_MAX_MAJOR); + return -1; + } + + iVer |= tmp << CR_GLVERSION_OFFSET_MINOR; + + if (!ver) + { + crDebug("no build version supplied"); + goto done; + } + + tmp = crStrParseGlSubver(ver, &ver, false); + if (tmp < 0) + { + crWarning("parsing build version failed, '%s', using 0", initVer); + tmp = 0; + } + + if (tmp > CR_GLVERSION_MAX_BUILD) + { + crWarning("build version %d is bigger than the max supported, using max supported val %#x", tmp, CR_GLVERSION_MAX_BUILD); + tmp = CR_GLVERSION_MAX_MAJOR; + } + + iVer |= tmp << CR_GLVERSION_OFFSET_BUILD; + +done: + crDebug("returning version %#x for string '%s'", iVer, initVer); + + return iVer; +} diff --git a/src/VBox/GuestHost/OpenGL/util/util.def b/src/VBox/GuestHost/OpenGL/util/util.def index d4b6cd64..4fa97058 100644 --- a/src/VBox/GuestHost/OpenGL/util/util.def +++ b/src/VBox/GuestHost/OpenGL/util/util.def @@ -82,7 +82,10 @@ crHashtableSearch crHashtableReplace crHashtableNumElements crHashtableWalk +crHashtableWalkUnlocked +crHashtableAllocRegisterKey crAllocHashtable +crAllocHashtableEx crFreeHashtable crHashtableGetDataKey crDetermineEndianness @@ -145,7 +148,6 @@ crGetCurrentDir crHashtableAllocKeys crHashtableDeleteBlock crHashtableIsKeyUsed -crHashtableWalk crMatrixInit crMatrixInitFromString crMatrixInitFromFloats diff --git a/src/VBox/GuestHost/OpenGL/util/vboxhgcm.c b/src/VBox/GuestHost/OpenGL/util/vboxhgcm.c index fbf35304..d5d057d9 100644 --- a/src/VBox/GuestHost/OpenGL/util/vboxhgcm.c +++ b/src/VBox/GuestHost/OpenGL/util/vboxhgcm.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -195,9 +195,6 @@ typedef enum { CR_VBOXHGCM_USERALLOCATED, CR_VBOXHGCM_MEMORY, CR_VBOXHGCM_MEMORY_BIG -#ifdef RT_OS_WINDOWS - ,CR_VBOXHGCM_DDRAW_SURFACE -#endif #if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST) ,CR_VBOXHGCM_UHGSMI_BUFFER #endif @@ -220,9 +217,6 @@ typedef struct CRVBOXHGCMBUFFER { PVBOXUHGSMI_BUFFER pBuffer; #endif }; -#ifdef RT_OS_WINDOWS - LPDIRECTDRAWSURFACE pDDS; -#endif } CRVBOXHGCMBUFFER; #ifndef RT_OS_WINDOWS @@ -705,92 +699,6 @@ static void *_crVBoxHGCMAlloc(CRConnection *conn) (void *) g_crvboxhgcm.bufpool, (unsigned int)sizeof(CRVBOXHGCMBUFFER) + conn->buffer_size); -#if defined(IN_GUEST) && defined(RT_OS_WINDOWS) - /* Try to start DDRAW on guest side */ - if (!g_crvboxhgcm.pDirectDraw && 0) - { - HRESULT hr; - - hr = DirectDrawCreate(NULL, &g_crvboxhgcm.pDirectDraw, NULL); - if (hr != DD_OK) - { - crWarning("Failed to create DirectDraw interface (%x)\n", hr); - g_crvboxhgcm.pDirectDraw = NULL; - } - else - { - hr = IDirectDraw_SetCooperativeLevel(g_crvboxhgcm.pDirectDraw, NULL, DDSCL_NORMAL); - if (hr != DD_OK) - { - crWarning("Failed to SetCooperativeLevel (%x)\n", hr); - IDirectDraw_Release(g_crvboxhgcm.pDirectDraw); - g_crvboxhgcm.pDirectDraw = NULL; - } - crDebug("Created DirectDraw and set CooperativeLevel successfully\n"); - } - } - - /* Try to allocate buffer via DDRAW */ - if (g_crvboxhgcm.pDirectDraw) - { - DDSURFACEDESC ddsd; - HRESULT hr; - LPDIRECTDRAWSURFACE lpDDS; - - memset(&ddsd, 0, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); - - /* @todo DDSCAPS_VIDEOMEMORY ain't working for some reason - * also, it would be better to request dwLinearSize but it fails too - * ddsd.dwLinearSize = sizeof(CRVBOXHGCMBUFFER) + conn->buffer_size; - */ - - ddsd.dwFlags = DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT; - ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; - /* use 1 byte per pixel format */ - ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat); - ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; - ddsd.ddpfPixelFormat.dwRGBBitCount = 8; - ddsd.ddpfPixelFormat.dwRBitMask = 0xFF; - ddsd.ddpfPixelFormat.dwGBitMask = 0; - ddsd.ddpfPixelFormat.dwBBitMask = 0; - /* request given buffer size, rounded to 1k */ - ddsd.dwWidth = 1024; - ddsd.dwHeight = (sizeof(CRVBOXHGCMBUFFER) + conn->buffer_size + ddsd.dwWidth-1)/ddsd.dwWidth; - - hr = IDirectDraw_CreateSurface(g_crvboxhgcm.pDirectDraw, &ddsd, &lpDDS, NULL); - if (hr != DD_OK) - { - crWarning("Failed to create DirectDraw surface (%x)\n", hr); - } - else - { - crDebug("Created DirectDraw surface (%x)\n", lpDDS); - - hr = IDirectDrawSurface_Lock(lpDDS, NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR, NULL); - if (hr != DD_OK) - { - crWarning("Failed to lock DirectDraw surface (%x)\n", hr); - IDirectDrawSurface_Release(lpDDS); - } - else - { - uint32_t cbLocked; - cbLocked = (ddsd.dwFlags & DDSD_LINEARSIZE) ? ddsd.dwLinearSize : ddsd.lPitch*ddsd.dwHeight; - - crDebug("Locked %d bytes DirectDraw surface\n", cbLocked); - - buf = (CRVBOXHGCMBUFFER *) ddsd.lpSurface; - CRASSERT(buf); - buf->magic = CR_VBOXHGCM_BUFFER_MAGIC; - buf->kind = CR_VBOXHGCM_DDRAW_SURFACE; - buf->allocated = cbLocked; - buf->pDDS = lpDDS; - } - } - } -#endif - /* We're either on host side, or we failed to allocate DDRAW buffer */ if (!buf) { @@ -800,9 +708,6 @@ static void *_crVBoxHGCMAlloc(CRConnection *conn) buf->magic = CR_VBOXHGCM_BUFFER_MAGIC; buf->kind = CR_VBOXHGCM_MEMORY; buf->allocated = conn->buffer_size; -#ifdef RT_OS_WINDOWS - buf->pDDS = NULL; -#endif } } @@ -944,18 +849,9 @@ crVBoxHGCMWriteReadExact(CRConnection *conn, const void *buf, unsigned int len, parms.hdr.u32Function = SHCRGL_GUEST_FN_WRITE_READ; parms.hdr.cParms = SHCRGL_CPARMS_WRITE_READ; - //if (bufferKind != CR_VBOXHGCM_DDRAW_SURFACE) - { - parms.pBuffer.type = VMMDevHGCMParmType_LinAddr_In; - parms.pBuffer.u.Pointer.size = len; - parms.pBuffer.u.Pointer.u.linearAddr = (uintptr_t) buf; - } - /*else ///@todo it fails badly, have to check why. bird: This fails because buf isn't a physical address? - { - parms.pBuffer.type = VMMDevHGCMParmType_PhysAddr; - parms.pBuffer.u.Pointer.size = len; - parms.pBuffer.u.Pointer.u.physAddr = (uintptr_t) buf; - }*/ + parms.pBuffer.type = VMMDevHGCMParmType_LinAddr_In; + parms.pBuffer.u.Pointer.size = len; + parms.pBuffer.u.Pointer.u.linearAddr = (uintptr_t) buf; CRASSERT(!conn->pBuffer); //make sure there's no data to process parms.pWriteback.type = VMMDevHGCMParmType_LinAddr_Out; @@ -1202,9 +1098,6 @@ static void _crVBoxHGCMFree(CRConnection *conn, void *buf) switch (hgcm_buffer->kind) { case CR_VBOXHGCM_MEMORY: -#ifdef RT_OS_WINDOWS - case CR_VBOXHGCM_DDRAW_SURFACE: -#endif #ifdef CHROMIUM_THREADSAFE crLockMutex(&g_crvboxhgcm.mutex); #endif @@ -1283,7 +1176,7 @@ static void _crVBoxHGCMReceiveMessage(CRConnection *conn) else { /* we should NEVER have redir_ptr disabled with HGSMI command now */ - CRASSERT(!conn->CmdData.pCmd); + CRASSERT(!conn->CmdData.pvCmd); if ( len <= conn->buffer_size ) { /* put in pre-allocated buffer */ @@ -1298,9 +1191,6 @@ static void _crVBoxHGCMReceiveMessage(CRConnection *conn) hgcm_buffer->magic = CR_VBOXHGCM_BUFFER_MAGIC; hgcm_buffer->kind = CR_VBOXHGCM_MEMORY_BIG; hgcm_buffer->allocated = sizeof(CRVBOXHGCMBUFFER) + len; -# ifdef RT_OS_WINDOWS - hgcm_buffer->pDDS = NULL; -# endif } hgcm_buffer->len = len; @@ -1998,25 +1888,30 @@ _crVBoxHGSMIWriteReadExact(CRConnection *conn, PCRVBOXHGSMI_CLIENT pClient, void else if (VERR_BUFFER_OVERFLOW == rc) { VBOXUHGSMI_BUFFER_TYPE_FLAGS Flags = {0}; - PVBOXUHGSMI_BUFFER pOldBuf = pClient->pHGBuffer; + PVBOXUHGSMI_BUFFER pNewBuf; CRASSERT(!pClient->pvHGBuffer); CRASSERT(cbWriteback>pClient->pHGBuffer->cbBuffer); crDebug("Reallocating host buffer from %d to %d bytes", conn->cbHostBufferAllocated, cbWriteback); - rc = pClient->pHgsmi->pfnBufferCreate(pClient->pHgsmi, CRVBOXHGSMI_PAGE_ALIGN(cbWriteback), Flags, &pClient->pHGBuffer); + rc = pClient->pHgsmi->pfnBufferCreate(pClient->pHgsmi, CRVBOXHGSMI_PAGE_ALIGN(cbWriteback), Flags, &pNewBuf); if (RT_SUCCESS(rc)) { - rc = pOldBuf->pfnDestroy(pOldBuf); + rc = pClient->pHGBuffer->pfnDestroy(pClient->pHGBuffer); CRASSERT(RT_SUCCESS(rc)); + pClient->pHGBuffer = pNewBuf; + _crVBoxHGSMIReadExact(conn, pClient/*, cbWriteback*/); } else { crWarning("_crVBoxHGSMIWriteReadExact: pfnBufferCreate(%d) failed!", CRVBOXHGSMI_PAGE_ALIGN(cbWriteback)); - crFree(conn->pHostBuffer); - conn->cbHostBufferAllocated = cbWriteback; - conn->pHostBuffer = crAlloc(conn->cbHostBufferAllocated); + if (conn->cbHostBufferAllocated < cbWriteback) + { + crFree(conn->pHostBuffer); + conn->cbHostBufferAllocated = cbWriteback; + conn->pHostBuffer = crAlloc(conn->cbHostBufferAllocated); + } crVBoxHGCMReadExact(conn, NULL, cbWriteback); } } @@ -2415,9 +2310,6 @@ void crVBoxHGCMInit(CRNetReceiveFuncList *rfl, CRNetCloseFuncList *cfl, unsigned /* Callback function used to free buffer pool entries */ void crVBoxHGCMBufferFree(void *data) { -#ifdef RT_OS_WINDOWS - LPDIRECTDRAWSURFACE lpDDS; -#endif CRVBOXHGCMBUFFER *hgcm_buffer = (CRVBOXHGCMBUFFER *) data; CRASSERT(hgcm_buffer->magic == CR_VBOXHGCM_BUFFER_MAGIC); @@ -2427,15 +2319,6 @@ void crVBoxHGCMBufferFree(void *data) case CR_VBOXHGCM_MEMORY: crFree( hgcm_buffer ); break; -#ifdef RT_OS_WINDOWS - case CR_VBOXHGCM_DDRAW_SURFACE: - lpDDS = hgcm_buffer->pDDS; - CRASSERT(lpDDS); - IDirectDrawSurface_Unlock(lpDDS, NULL); - IDirectDrawSurface_Release(lpDDS); - crDebug("DDraw surface freed (%x)\n", lpDDS); - break; -#endif case CR_VBOXHGCM_MEMORY_BIG: crFree( hgcm_buffer ); break; diff --git a/src/VBox/GuestHost/OpenGL/util/vboxhgsmi.c b/src/VBox/GuestHost/OpenGL/util/vboxhgsmi.c index 60d5ce21..3da3496a 100644 --- a/src/VBox/GuestHost/OpenGL/util/vboxhgsmi.c +++ b/src/VBox/GuestHost/OpenGL/util/vboxhgsmi.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/GuestHost/OpenGL/util/vreg.cpp b/src/VBox/GuestHost/OpenGL/util/vreg.cpp new file mode 100644 index 00000000..e2cea3b3 --- /dev/null +++ b/src/VBox/GuestHost/OpenGL/util/vreg.cpp @@ -0,0 +1,1692 @@ +/* $Id: vreg.cpp $ */ + +/** @file + * Visible Regions processing API implementation + */ + +/* + * Copyright (C) 2012 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_vreg.h> +#include <iprt/err.h> +#include <iprt/assert.h> +#include <iprt/asm.h> + +#include <cr_error.h> + +#ifdef DEBUG_misha +# define VBOXVDBG_VR_LAL_DISABLE +#endif + +#ifndef IN_RING0 +#include <iprt/memcache.h> +#ifndef VBOXVDBG_VR_LAL_DISABLE +static RTMEMCACHE g_VBoxVrLookasideList; +#define vboxVrRegLaAlloc(_c) RTMemCacheAlloc((_c)) +#define vboxVrRegLaFree(_c, _e) RTMemCacheFree((_c), (_e)) +DECLINLINE(int) vboxVrLaCreate(RTMEMCACHE *pCache, size_t cbElement) +{ + int rc = RTMemCacheCreate(pCache, cbElement, + 0, /* size_t cbAlignment */ + UINT32_MAX, /* uint32_t cMaxObjects */ + NULL, /* PFNMEMCACHECTOR pfnCtor*/ + NULL, /* PFNMEMCACHEDTOR pfnDtor*/ + NULL, /* void *pvUser*/ + 0 /* uint32_t fFlags*/ + ); + if (!RT_SUCCESS(rc)) + { + WARN(("RTMemCacheCreate failed rc %d", rc)); + return rc; + } + return VINF_SUCCESS; +} +#define vboxVrLaDestroy(_c) RTMemCacheDestroy((_c)) +#endif +#else +# ifdef RT_OS_WINDOWS +# ifdef PAGE_SIZE +# undef PAGE_SIZE +# endif +# ifdef PAGE_SHIFT +# undef PAGE_SHIFT +# endif +# define VBOX_WITH_WORKAROUND_MISSING_PACK +# if (_MSC_VER >= 1400) && !defined(VBOX_WITH_PATCHED_DDK) +# define _InterlockedExchange _InterlockedExchange_StupidDDKVsCompilerCrap +# define _InterlockedExchangeAdd _InterlockedExchangeAdd_StupidDDKVsCompilerCrap +# define _InterlockedCompareExchange _InterlockedCompareExchange_StupidDDKVsCompilerCrap +# define _InterlockedAddLargeStatistic _InterlockedAddLargeStatistic_StupidDDKVsCompilerCrap +# define _interlockedbittestandset _interlockedbittestandset_StupidDDKVsCompilerCrap +# define _interlockedbittestandreset _interlockedbittestandreset_StupidDDKVsCompilerCrap +# define _interlockedbittestandset64 _interlockedbittestandset64_StupidDDKVsCompilerCrap +# define _interlockedbittestandreset64 _interlockedbittestandreset64_StupidDDKVsCompilerCrap +# pragma warning(disable : 4163) +# ifdef VBOX_WITH_WORKAROUND_MISSING_PACK +# pragma warning(disable : 4103) +# endif +# include <ntddk.h> +# pragma warning(default : 4163) +# ifdef VBOX_WITH_WORKAROUND_MISSING_PACK +# pragma pack() +# pragma warning(default : 4103) +# endif +# undef _InterlockedExchange +# undef _InterlockedExchangeAdd +# undef _InterlockedCompareExchange +# undef _InterlockedAddLargeStatistic +# undef _interlockedbittestandset +# undef _interlockedbittestandreset +# undef _interlockedbittestandset64 +# undef _interlockedbittestandreset64 +# else +# include <ntddk.h> +# endif +#ifndef VBOXVDBG_VR_LAL_DISABLE +static LOOKASIDE_LIST_EX g_VBoxVrLookasideList; +#define vboxVrRegLaAlloc(_c) ExAllocateFromLookasideListEx(&(_c)) +#define vboxVrRegLaFree(_c, _e) ExFreeToLookasideListEx(&(_c), (_e)) +#define VBOXWDDMVR_MEMTAG 'vDBV' +DECLINLINE(int) vboxVrLaCreate(LOOKASIDE_LIST_EX *pCache, size_t cbElement) +{ + NTSTATUS Status = ExInitializeLookasideListEx(pCache, + NULL, /* PALLOCATE_FUNCTION_EX Allocate */ + NULL, /* PFREE_FUNCTION_EX Free */ + NonPagedPool, + 0, /* ULONG Flags */ + cbElement, + VBOXWDDMVR_MEMTAG, + 0 /* USHORT Depth - reserved, must be null */ + ); + if (!NT_SUCCESS(Status)) + { + WARN(("ExInitializeLookasideListEx failed, Status (0x%x)", Status)); + return VERR_GENERAL_FAILURE; + } + + return VINF_SUCCESS; +} +#define vboxVrLaDestroy(_c) ExDeleteLookasideListEx(&(_c)) +#endif +# else +# error "port me!" +# endif +#endif + +static volatile int32_t g_cVBoxVrInits = 0; + +static PVBOXVR_REG vboxVrRegCreate() +{ +#ifndef VBOXVDBG_VR_LAL_DISABLE + PVBOXVR_REG pReg = (PVBOXVR_REG)vboxVrRegLaAlloc(g_VBoxVrLookasideList); + if (!pReg) + { + WARN(("ExAllocateFromLookasideListEx failed!")); + } + return pReg; +#else + return (PVBOXVR_REG)RTMemAlloc(sizeof (VBOXVR_REG)); +#endif +} + +static void vboxVrRegTerm(PVBOXVR_REG pReg) +{ +#ifndef VBOXVDBG_VR_LAL_DISABLE + vboxVrRegLaFree(g_VBoxVrLookasideList, pReg); +#else + RTMemFree(pReg); +#endif +} + +VBOXVREGDECL(void) VBoxVrListClear(PVBOXVR_LIST pList) +{ + PVBOXVR_REG pReg, pRegNext; + + RTListForEachSafe(&pList->ListHead, pReg, pRegNext, VBOXVR_REG, ListEntry) + { + vboxVrRegTerm(pReg); + } + VBoxVrListInit(pList); +} + +/* moves list data to pDstList and empties the pList */ +VBOXVREGDECL(void) VBoxVrListMoveTo(PVBOXVR_LIST pList, PVBOXVR_LIST pDstList) +{ + *pDstList = *pList; + pDstList->ListHead.pNext->pPrev = &pDstList->ListHead; + pDstList->ListHead.pPrev->pNext = &pDstList->ListHead; + VBoxVrListInit(pList); +} + +#define VBOXVR_MEMTAG 'vDBV' + +VBOXVREGDECL(int) VBoxVrInit() +{ + int32_t cNewRefs = ASMAtomicIncS32(&g_cVBoxVrInits); + Assert(cNewRefs >= 1); + Assert(cNewRefs == 1); /* <- debugging */ + if (cNewRefs > 1) + return VINF_SUCCESS; + +#ifndef VBOXVDBG_VR_LAL_DISABLE + int rc = vboxVrLaCreate(&g_VBoxVrLookasideList, sizeof (VBOXVR_REG)); + if (!RT_SUCCESS(rc)) + { + WARN(("ExInitializeLookasideListEx failed, rc (%d)", rc)); + return rc; + } +#endif + + return VINF_SUCCESS; +} + +VBOXVREGDECL(void) VBoxVrTerm() +{ + int32_t cNewRefs = ASMAtomicDecS32(&g_cVBoxVrInits); + Assert(cNewRefs >= 0); + if (cNewRefs > 0) + return; + +#ifndef VBOXVDBG_VR_LAL_DISABLE + vboxVrLaDestroy(g_VBoxVrLookasideList); +#endif +} + +typedef DECLCALLBACK(int) FNVBOXVR_CB_COMPARATOR(const VBOXVR_REG *pReg1, const VBOXVR_REG *pReg2); +typedef FNVBOXVR_CB_COMPARATOR *PFNVBOXVR_CB_COMPARATOR; + +static DECLCALLBACK(int) vboxVrRegNonintersectedComparator(const RTRECT* pRect1, const RTRECT* pRect2) +{ + Assert(!VBoxRectIsIntersect(pRect1, pRect2)); + if (pRect1->yTop != pRect2->yTop) + return pRect1->yTop - pRect2->yTop; + return pRect1->xLeft - pRect2->xLeft; +} + +#ifdef DEBUG_misha +static void vboxVrDbgListDoVerify(PVBOXVR_LIST pList) +{ + PVBOXVR_REG pReg1, pReg2; + RTListForEach(&pList->ListHead, pReg1, VBOXVR_REG, ListEntry) + { + Assert(!VBoxRectIsZero(&pReg1->Rect)); + for (RTLISTNODE *pEntry2 = pReg1->ListEntry.pNext; pEntry2 != &pList->ListHead; pEntry2 = pEntry2->pNext) + { + pReg2 = PVBOXVR_REG_FROM_ENTRY(pEntry2); + Assert(vboxVrRegNonintersectedComparator(&pReg1->Rect, &pReg2->Rect) < 0); + } + } +} + +#define vboxVrDbgListVerify vboxVrDbgListDoVerify +#else +#define vboxVrDbgListVerify(_p) do {} while (0) +#endif + +static int vboxVrListUniteIntersection(PVBOXVR_LIST pList, PVBOXVR_LIST pIntersection); + +#define VBOXVR_INVALID_COORD (~0U) + +DECLINLINE(void) vboxVrListRegAdd(PVBOXVR_LIST pList, PVBOXVR_REG pReg, PRTLISTNODE pPlace, bool fAfter) +{ + if (fAfter) + RTListPrepend(pPlace, &pReg->ListEntry); + else + RTListAppend(pPlace, &pReg->ListEntry); + ++pList->cEntries; + vboxVrDbgListVerify(pList); +} + +DECLINLINE(void) vboxVrListRegRemove(PVBOXVR_LIST pList, PVBOXVR_REG pReg) +{ + RTListNodeRemove(&pReg->ListEntry); + --pList->cEntries; + vboxVrDbgListVerify(pList); +} + +static void vboxVrListRegAddOrder(PVBOXVR_LIST pList, PRTLISTNODE pMemberEntry, PVBOXVR_REG pReg) +{ + do + { + if (pMemberEntry != &pList->ListHead) + { + PVBOXVR_REG pMemberReg = PVBOXVR_REG_FROM_ENTRY(pMemberEntry); + if (vboxVrRegNonintersectedComparator(&pMemberReg->Rect, &pReg->Rect) < 0) + { + pMemberEntry = pMemberEntry->pNext; + continue; + } + } + vboxVrListRegAdd(pList, pReg, pMemberEntry, false); + break; + } while (1); +} + +static void vboxVrListAddNonintersected(PVBOXVR_LIST pList1, PVBOXVR_LIST pList2) +{ + PRTLISTNODE pEntry1 = pList1->ListHead.pNext; + + for (PRTLISTNODE pEntry2 = pList2->ListHead.pNext; pEntry2 != &pList2->ListHead; pEntry2 = pList2->ListHead.pNext) + { + PVBOXVR_REG pReg2 = PVBOXVR_REG_FROM_ENTRY(pEntry2); + do { + if (pEntry1 != &pList1->ListHead) + { + PVBOXVR_REG pReg1 = PVBOXVR_REG_FROM_ENTRY(pEntry1); + if (vboxVrRegNonintersectedComparator(&pReg1->Rect, &pReg2->Rect) < 0) + { + pEntry1 = pEntry1->pNext; + continue; + } + } + vboxVrListRegRemove(pList2, pReg2); + vboxVrListRegAdd(pList1, pReg2, pEntry1, false); + break; + } while (1); + } + + Assert(VBoxVrListIsEmpty(pList2)); +} + +static int vboxVrListRegIntersectSubstNoJoin(PVBOXVR_LIST pList1, PVBOXVR_REG pReg1, const RTRECT * pRect2) +{ + uint32_t topLim = VBOXVR_INVALID_COORD; + uint32_t bottomLim = VBOXVR_INVALID_COORD; + RTLISTNODE List; + PVBOXVR_REG pBottomReg = NULL; +#ifdef DEBUG_misha + RTRECT tmpRect = pReg1->Rect; + vboxVrDbgListVerify(pList1); +#endif + Assert(!VBoxRectIsZero(pRect2)); + + RTListInit(&List); + + Assert(VBoxRectIsIntersect(&pReg1->Rect, pRect2)); + + if (pReg1->Rect.yTop < pRect2->yTop) + { + Assert(pRect2->yTop < pReg1->Rect.yBottom); + PVBOXVR_REG pRegResult = vboxVrRegCreate(); + pRegResult->Rect.yTop = pReg1->Rect.yTop; + pRegResult->Rect.xLeft = pReg1->Rect.xLeft; + pRegResult->Rect.yBottom = pRect2->yTop; + pRegResult->Rect.xRight = pReg1->Rect.xRight; + topLim = pRect2->yTop; + RTListAppend(&List, &pRegResult->ListEntry); + } + + if (pReg1->Rect.yBottom > pRect2->yBottom) + { + Assert(pRect2->yBottom > pReg1->Rect.yTop); + PVBOXVR_REG pRegResult = vboxVrRegCreate(); + pRegResult->Rect.yTop = pRect2->yBottom; + pRegResult->Rect.xLeft = pReg1->Rect.xLeft; + pRegResult->Rect.yBottom = pReg1->Rect.yBottom; + pRegResult->Rect.xRight = pReg1->Rect.xRight; + bottomLim = pRect2->yBottom; + pBottomReg = pRegResult; + } + + if (pReg1->Rect.xLeft < pRect2->xLeft) + { + Assert(pRect2->xLeft < pReg1->Rect.xRight); + PVBOXVR_REG pRegResult = vboxVrRegCreate(); + pRegResult->Rect.yTop = topLim == VBOXVR_INVALID_COORD ? pReg1->Rect.yTop : topLim; + pRegResult->Rect.xLeft = pReg1->Rect.xLeft; + pRegResult->Rect.yBottom = bottomLim == VBOXVR_INVALID_COORD ? pReg1->Rect.yBottom : bottomLim; + pRegResult->Rect.xRight = pRect2->xLeft; + RTListAppend(&List, &pRegResult->ListEntry); + } + + if (pReg1->Rect.xRight > pRect2->xRight) + { + Assert(pRect2->xRight > pReg1->Rect.xLeft); + PVBOXVR_REG pRegResult = vboxVrRegCreate(); + pRegResult->Rect.yTop = topLim == VBOXVR_INVALID_COORD ? pReg1->Rect.yTop : topLim; + pRegResult->Rect.xLeft = pRect2->xRight; + pRegResult->Rect.yBottom = bottomLim == VBOXVR_INVALID_COORD ? pReg1->Rect.yBottom : bottomLim; + pRegResult->Rect.xRight = pReg1->Rect.xRight; + RTListAppend(&List, &pRegResult->ListEntry); + } + + if (pBottomReg) + RTListAppend(&List, &pBottomReg->ListEntry); + + PRTLISTNODE pMemberEntry = pReg1->ListEntry.pNext; + vboxVrListRegRemove(pList1, pReg1); + vboxVrRegTerm(pReg1); + + if (RTListIsEmpty(&List)) + return VINF_SUCCESS; /* the region is covered by the pRect2 */ + + PRTLISTNODE pEntry = List.pNext, pNext; + for (; pEntry != &List; pEntry = pNext) + { + pNext = pEntry->pNext; + PVBOXVR_REG pReg = PVBOXVR_REG_FROM_ENTRY(pEntry); + + vboxVrListRegAddOrder(pList1, pMemberEntry, pReg); + pMemberEntry = pEntry->pNext; /* the following elements should go after the given pEntry since they are ordered already */ + } + return VINF_SUCCESS; +} + +/* @returns Entry to be used for continuing the rectangles iterations being made currently on the callback call. + * ListHead is returned to break the current iteration + * @param ppNext specifies next reg entry to be used for iteration. the default is pReg1->ListEntry.pNext */ +typedef DECLCALLBACK(PRTLISTNODE) FNVBOXVR_CB_INTERSECTED_VISITOR(PVBOXVR_LIST pList1, PVBOXVR_REG pReg1, const RTRECT * pRect2, void *pvContext, PRTLISTNODE *ppNext); +typedef FNVBOXVR_CB_INTERSECTED_VISITOR *PFNVBOXVR_CB_INTERSECTED_VISITOR; + +static void vboxVrListVisitIntersected(PVBOXVR_LIST pList1, uint32_t cRects, const RTRECT *aRects, PFNVBOXVR_CB_INTERSECTED_VISITOR pfnVisitor, void* pvVisitor) +{ + PRTLISTNODE pEntry1 = pList1->ListHead.pNext; + PRTLISTNODE pNext1; + uint32_t iFirst2 = 0; + + for (; pEntry1 != &pList1->ListHead; pEntry1 = pNext1) + { + pNext1 = pEntry1->pNext; + PVBOXVR_REG pReg1 = PVBOXVR_REG_FROM_ENTRY(pEntry1); + for (uint32_t i = iFirst2; i < cRects; ++i) + { + const RTRECT *pRect2 = &aRects[i]; + if (VBoxRectIsZero(pRect2)) + continue; + + if (!VBoxRectIsIntersect(&pReg1->Rect, pRect2)) + continue; + + /* the visitor can modify the list 1, apply necessary adjustments after it */ + pEntry1 = pfnVisitor (pList1, pReg1, pRect2, pvVisitor, &pNext1); + if (pEntry1 == &pList1->ListHead) + break; + else + pReg1 = PVBOXVR_REG_FROM_ENTRY(pEntry1); + } + } +} + +/* @returns Entry to be iterated next. ListHead is returned to break the iteration + * + */ +typedef DECLCALLBACK(PRTLISTNODE) FNVBOXVR_CB_NONINTERSECTED_VISITOR(PVBOXVR_LIST pList1, PVBOXVR_REG pReg1, void *pvContext); +typedef FNVBOXVR_CB_NONINTERSECTED_VISITOR *PFNVBOXVR_CB_NONINTERSECTED_VISITOR; + +static void vboxVrListVisitNonintersected(PVBOXVR_LIST pList1, uint32_t cRects, const RTRECT *aRects, PFNVBOXVR_CB_NONINTERSECTED_VISITOR pfnVisitor, void* pvVisitor) +{ + PRTLISTNODE pEntry1 = pList1->ListHead.pNext; + PRTLISTNODE pNext1; + uint32_t iFirst2 = 0; + + for (; pEntry1 != &pList1->ListHead; pEntry1 = pNext1) + { + PVBOXVR_REG pReg1 = PVBOXVR_REG_FROM_ENTRY(pEntry1); + uint32_t i = iFirst2; + for (; i < cRects; ++i) + { + const RTRECT *pRect2 = &aRects[i]; + if (VBoxRectIsZero(pRect2)) + continue; + + if (VBoxRectIsIntersect(&pReg1->Rect, pRect2)) + break; + } + + if (i == cRects) + pNext1 = pfnVisitor(pList1, pReg1, pvVisitor); + else + pNext1 = pEntry1->pNext; + } +} + +static void vboxVrListJoinRectsHV(PVBOXVR_LIST pList, bool fHorizontal) +{ + PRTLISTNODE pNext1, pNext2; + + for (PRTLISTNODE pEntry1 = pList->ListHead.pNext; pEntry1 != &pList->ListHead; pEntry1 = pNext1) + { + PVBOXVR_REG pReg1 = PVBOXVR_REG_FROM_ENTRY(pEntry1); + pNext1 = pEntry1->pNext; + for (PRTLISTNODE pEntry2 = pEntry1->pNext; pEntry2 != &pList->ListHead; pEntry2 = pNext2) + { + PVBOXVR_REG pReg2 = PVBOXVR_REG_FROM_ENTRY(pEntry2); + pNext2 = pEntry2->pNext; + if (fHorizontal) + { + if (pReg1->Rect.yTop == pReg2->Rect.yTop) + { + if (pReg1->Rect.xRight == pReg2->Rect.xLeft) + { + /* join rectangles */ + vboxVrListRegRemove(pList, pReg2); + if (pReg1->Rect.yBottom > pReg2->Rect.yBottom) + { + int32_t oldRight1 = pReg1->Rect.xRight; + int32_t oldBottom1 = pReg1->Rect.yBottom; + pReg1->Rect.xRight = pReg2->Rect.xRight; + pReg1->Rect.yBottom = pReg2->Rect.yBottom; + + vboxVrDbgListVerify(pList); + + pReg2->Rect.xLeft = pReg1->Rect.xLeft; + pReg2->Rect.yTop = pReg1->Rect.yBottom; + pReg2->Rect.xRight = oldRight1; + pReg2->Rect.yBottom = oldBottom1; + vboxVrListRegAddOrder(pList, pReg1->ListEntry.pNext, pReg2); + /* restart the pNext1 & pNext2 since regs are splitted into smaller ones in y dimension + * and thus can match one of the previous rects */ + pNext1 = pList->ListHead.pNext; + break; + } + else if (pReg1->Rect.yBottom < pReg2->Rect.yBottom) + { + pReg1->Rect.xRight = pReg2->Rect.xRight; + vboxVrDbgListVerify(pList); + pReg2->Rect.yTop = pReg1->Rect.yBottom; + vboxVrListRegAddOrder(pList, pReg1->ListEntry.pNext, pReg2); + /* restart the pNext1 & pNext2 since regs are splitted into smaller ones in y dimension + * and thus can match one of the previous rects */ + pNext1 = pList->ListHead.pNext; + break; + } + else + { + pReg1->Rect.xRight = pReg2->Rect.xRight; + vboxVrDbgListVerify(pList); + /* reset the pNext1 since it could be the pReg2 being destroyed */ + pNext1 = pEntry1->pNext; + /* pNext2 stays the same since it is pReg2->ListEntry.pNext, which is kept intact */ + vboxVrRegTerm(pReg2); + } + } + continue; + } + else if (pReg1->Rect.yBottom == pReg2->Rect.yBottom) + { + Assert(pReg1->Rect.yTop < pReg2->Rect.yTop); /* <- since pReg1 > pReg2 && pReg1->Rect.yTop != pReg2->Rect.yTop*/ + if (pReg1->Rect.xRight == pReg2->Rect.xLeft) + { + /* join rectangles */ + vboxVrListRegRemove(pList, pReg2); + + pReg1->Rect.yBottom = pReg2->Rect.yTop; + vboxVrDbgListVerify(pList); + pReg2->Rect.xLeft = pReg1->Rect.xLeft; + + vboxVrListRegAddOrder(pList, pNext2, pReg2); + + /* restart the pNext1 & pNext2 since regs are splitted into smaller ones in y dimension + * and thus can match one of the previous rects */ + pNext1 = pList->ListHead.pNext; + break; + } + else if (pReg1->Rect.xLeft == pReg2->Rect.xRight) + { + /* join rectangles */ + vboxVrListRegRemove(pList, pReg2); + + pReg1->Rect.yBottom = pReg2->Rect.yTop; + vboxVrDbgListVerify(pList); + pReg2->Rect.xRight = pReg1->Rect.xRight; + + vboxVrListRegAddOrder(pList, pNext2, pReg2); + + /* restart the pNext1 & pNext2 since regs are splitted into smaller ones in y dimension + * and thus can match one of the previous rects */ + pNext1 = pList->ListHead.pNext; + break; + } + continue; + } + } + else + { + if (pReg1->Rect.yBottom == pReg2->Rect.yTop) + { + if (pReg1->Rect.xLeft == pReg2->Rect.xLeft) + { + if (pReg1->Rect.xRight == pReg2->Rect.xRight) + { + /* join rects */ + vboxVrListRegRemove(pList, pReg2); + + pReg1->Rect.yBottom = pReg2->Rect.yBottom; + vboxVrDbgListVerify(pList); + + /* reset the pNext1 since it could be the pReg2 being destroyed */ + pNext1 = pEntry1->pNext; + /* pNext2 stays the same since it is pReg2->ListEntry.pNext, which is kept intact */ + vboxVrRegTerm(pReg2); + continue; + } + /* no more to be done for for pReg1 */ + break; + } + else if (pReg1->Rect.xRight > pReg2->Rect.xLeft) + { + /* no more to be done for for pReg1 */ + break; + } + + continue; + } + else if (pReg1->Rect.yBottom < pReg2->Rect.yTop) + { + /* no more to be done for for pReg1 */ + break; + } + } + } + } +} + +static void vboxVrListJoinRects(PVBOXVR_LIST pList) +{ + vboxVrListJoinRectsHV(pList, true); + vboxVrListJoinRectsHV(pList, false); +} + +typedef struct VBOXVR_CBDATA_SUBST +{ + int rc; + bool fChanged; +} VBOXVR_CBDATA_SUBST, *PVBOXVR_CBDATA_SUBST; + +static DECLCALLBACK(PRTLISTNODE) vboxVrListSubstNoJoinCb(PVBOXVR_LIST pList, PVBOXVR_REG pReg1, const RTRECT *pRect2, void *pvContext, PRTLISTNODE *ppNext) +{ + PVBOXVR_CBDATA_SUBST pData = (PVBOXVR_CBDATA_SUBST)pvContext; + /* store the prev to get the new pNext out of it*/ + PRTLISTNODE pPrev = pReg1->ListEntry.pPrev; + pData->fChanged = true; + + Assert(VBoxRectIsIntersect(&pReg1->Rect, pRect2)); + + /* NOTE: the pReg1 will be invalid after the vboxVrListRegIntersectSubstNoJoin call!!! */ + int rc = vboxVrListRegIntersectSubstNoJoin(pList, pReg1, pRect2); + if (RT_SUCCESS(rc)) + { + *ppNext = pPrev->pNext; + return &pList->ListHead; + } + WARN(("vboxVrListRegIntersectSubstNoJoin failed!")); + Assert(!RT_SUCCESS(rc)); + pData->rc = rc; + *ppNext = &pList->ListHead; + return &pList->ListHead; +} + +static int vboxVrListSubstNoJoin(PVBOXVR_LIST pList, uint32_t cRects, const RTRECT * aRects, bool *pfChanged) +{ + if (pfChanged) + *pfChanged = false; + + if (VBoxVrListIsEmpty(pList)) + return VINF_SUCCESS; + + VBOXVR_CBDATA_SUBST Data; + Data.rc = VINF_SUCCESS; + Data.fChanged = false; + + vboxVrListVisitIntersected(pList, cRects, aRects, vboxVrListSubstNoJoinCb, &Data); + if (!RT_SUCCESS(Data.rc)) + { + WARN(("vboxVrListVisitIntersected failed!")); + return Data.rc; + } + + if (pfChanged) + *pfChanged = Data.fChanged; + + return VINF_SUCCESS; +} + +#if 0 +static const RTRECT * vboxVrRectsOrder(uint32_t cRects, const RTRECT * aRects) +{ +#ifdef DEBUG + { + for (uint32_t i = 0; i < cRects; ++i) + { + RTRECT *pRectI = &aRects[i]; + for (uint32_t j = i + 1; j < cRects; ++j) + { + RTRECT *pRectJ = &aRects[j]; + Assert(!VBoxRectIsIntersect(pRectI, pRectJ)); + } + } + } +#endif + + RTRECT * pRects = (RTRECT *)aRects; + /* check if rects are ordered already */ + for (uint32_t i = 0; i < cRects - 1; ++i) + { + RTRECT *pRect1 = &pRects[i]; + RTRECT *pRect2 = &pRects[i+1]; + if (vboxVrRegNonintersectedComparator(pRect1, pRect2) < 0) + continue; + + WARN(("rects are unoreded!")); + + if (pRects == aRects) + { + pRects = (RTRECT *)RTMemAlloc(sizeof (RTRECT) * cRects); + if (!pRects) + { + WARN(("RTMemAlloc failed!")); + return NULL; + } + + memcpy(pRects, aRects, sizeof (RTRECT) * cRects); + } + + Assert(pRects != aRects); + + int j = (int)i - 1; + do { + RTRECT Tmp = *pRect1; + *pRect1 = *pRect2; + *pRect2 = Tmp; + + if (j < 0) + break; + + if (vboxVrRegNonintersectedComparator(pRect1, pRect1-1) > 0) + break; + + pRect2 = pRect1--; + --j; + } while (1); + } + + return pRects; +} +#endif + +VBOXVREGDECL(void) VBoxVrListTranslate(PVBOXVR_LIST pList, int32_t x, int32_t y) +{ + for (PRTLISTNODE pEntry1 = pList->ListHead.pNext; pEntry1 != &pList->ListHead; pEntry1 = pEntry1->pNext) + { + PVBOXVR_REG pReg1 = PVBOXVR_REG_FROM_ENTRY(pEntry1); + VBoxRectTranslate(&pReg1->Rect, x, y); + } +} + +static DECLCALLBACK(PRTLISTNODE) vboxVrListIntersectNoJoinNonintersectedCb(PVBOXVR_LIST pList1, PVBOXVR_REG pReg1, void *pvContext) +{ + VBOXVR_CBDATA_SUBST *pData = (VBOXVR_CBDATA_SUBST*)pvContext; + + PRTLISTNODE pNext = pReg1->ListEntry.pNext; + + vboxVrDbgListVerify(pList1); + + vboxVrListRegRemove(pList1, pReg1); + vboxVrRegTerm(pReg1); + + vboxVrDbgListVerify(pList1); + + pData->fChanged = true; + + return pNext; +} + +static DECLCALLBACK(PRTLISTNODE) vboxVrListIntersectNoJoinIntersectedCb(PVBOXVR_LIST pList1, PVBOXVR_REG pReg1, const RTRECT *pRect2, void *pvContext, PRTLISTNODE *ppNext) +{ + PVBOXVR_CBDATA_SUBST pData = (PVBOXVR_CBDATA_SUBST)pvContext; + pData->fChanged = true; + + vboxVrDbgListVerify(pList1); + + PRTLISTNODE pMemberEntry = pReg1->ListEntry.pNext; + + Assert(VBoxRectIsIntersect(&pReg1->Rect, pRect2)); + Assert(!VBoxRectIsZero(pRect2)); + + vboxVrListRegRemove(pList1, pReg1); + VBoxRectIntersect(&pReg1->Rect, pRect2); + Assert(!VBoxRectIsZero(&pReg1->Rect)); + + vboxVrListRegAddOrder(pList1, pMemberEntry, pReg1); + + vboxVrDbgListVerify(pList1); + + return &pReg1->ListEntry; +} + +static int vboxVrListIntersectNoJoin(PVBOXVR_LIST pList, const VBOXVR_LIST *pList2, bool *pfChanged) +{ + bool fChanged = false; + *pfChanged = false; + + if (VBoxVrListIsEmpty(pList)) + return VINF_SUCCESS; + + if (VBoxVrListIsEmpty(pList2)) + { + if (pfChanged) + *pfChanged = true; + + VBoxVrListClear(pList); + return VINF_SUCCESS; + } + + PRTLISTNODE pNext1; + + for (PRTLISTNODE pEntry1 = pList->ListHead.pNext; pEntry1 != &pList->ListHead; pEntry1 = pNext1) + { + pNext1 = pEntry1->pNext; + PVBOXVR_REG pReg1 = PVBOXVR_REG_FROM_ENTRY(pEntry1); + RTRECT RegRect1 = pReg1->Rect; + PRTLISTNODE pMemberEntry = pReg1->ListEntry.pNext; + + for (const RTLISTNODE *pEntry2 = pList2->ListHead.pNext; pEntry2 != &pList2->ListHead; pEntry2 = pEntry2->pNext) + { + const VBOXVR_REG *pReg2 = PVBOXVR_REG_FROM_ENTRY(pEntry2); + const RTRECT *pRect2 = &pReg2->Rect; + + if (!VBoxRectIsIntersect(&RegRect1, pRect2)) + continue; + + if (pReg1) + { + if (VBoxRectCovers(pRect2, &RegRect1)) + { + /* no change */ + + /* zero up the pReg1 to mark it as intersected (see the code after this inner loop) */ + pReg1 = NULL; + + if (!VBoxRectCmp(pRect2, &RegRect1)) + break; /* and we can break the iteration here */ + } + else + { + /*just to ensure the VBoxRectCovers is true for equal rects */ + Assert(VBoxRectCmp(pRect2, &RegRect1)); + + /* @todo: this can have false-alarming sometimes if the separated rects will then be joind into the original rect, + * so far this should not be a problem for VReg clients, so keep it this way for now */ + fChanged = true; + + /* re-use the reg entry */ + vboxVrListRegRemove(pList, pReg1); + VBoxRectIntersect(&pReg1->Rect, pRect2); + Assert(!VBoxRectIsZero(&pReg1->Rect)); + + vboxVrListRegAddOrder(pList, pMemberEntry, pReg1); + pReg1 = NULL; + } + } + else + { + Assert(fChanged); /* <- should be set by the if branch above */ + PVBOXVR_REG pReg = vboxVrRegCreate(); + if (!pReg) + { + WARN(("vboxVrRegCreate failed!")); + return VERR_NO_MEMORY; + } + VBoxRectIntersected(&RegRect1, pRect2, &pReg->Rect); + Assert(!VBoxRectIsZero(&pReg->Rect)); + vboxVrListRegAddOrder(pList, pList->ListHead.pNext, pReg); + } + } + + if (pReg1) + { + /* the region has no intersections, remove it */ + vboxVrListRegRemove(pList, pReg1); + vboxVrRegTerm(pReg1); + fChanged = true; + } + } + + *pfChanged = fChanged; + return VINF_SUCCESS; +} + +VBOXVREGDECL(int) VBoxVrListIntersect(PVBOXVR_LIST pList, const VBOXVR_LIST *pList2, bool *pfChanged) +{ + if (pfChanged) + *pfChanged = false; + + int rc = vboxVrListIntersectNoJoin(pList, pList2, pfChanged); + if (!RT_SUCCESS(rc)) + { + WARN(("vboxVrListSubstNoJoin failed!")); + return rc; + } + + if (*pfChanged) + { + vboxVrListJoinRects(pList); + } + + return rc; +} + +VBOXVREGDECL(int) VBoxVrListRectsIntersect(PVBOXVR_LIST pList, uint32_t cRects, const RTRECT * aRects, bool *pfChanged) +{ + if (pfChanged) + *pfChanged = false; + + if (VBoxVrListIsEmpty(pList)) + return VINF_SUCCESS; + + if (!cRects) + { + if (pfChanged) + *pfChanged = true; + + VBoxVrListClear(pList); + return VINF_SUCCESS; + } + + /* we perform intersection using lists because the algorythm axpects the rects to be non-intersected, + * which list guaranties to us */ + + VBOXVR_LIST TmpList; + VBoxVrListInit(&TmpList); + + int rc = VBoxVrListRectsAdd(&TmpList, cRects, aRects, NULL); + if (RT_SUCCESS(rc)) + { + rc = VBoxVrListIntersect(pList, &TmpList, pfChanged); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxVrListIntersect failed! rc %d", rc)); + } + } + else + { + WARN(("VBoxVrListRectsAdd failed, rc %d", rc)); + } + VBoxVrListClear(&TmpList); + + return rc; +} + +VBOXVREGDECL(int) VBoxVrListRectsSubst(PVBOXVR_LIST pList, uint32_t cRects, const RTRECT * aRects, bool *pfChanged) +{ +#if 0 + const RTRECT * pRects = vboxVrRectsOrder(cRects, aRects); + if (!pRects) + { + WARN(("vboxVrRectsOrder failed!")); + return VERR_NO_MEMORY; + } +#endif + + bool fChanged = false; + + int rc = vboxVrListSubstNoJoin(pList, cRects, aRects, &fChanged); + if (!RT_SUCCESS(rc)) + { + WARN(("vboxVrListSubstNoJoin failed!")); + goto done; + } + + if (fChanged) + goto done; + + vboxVrListJoinRects(pList); + +done: +#if 0 + if (pRects != aRects) + RTMemFree(pRects); +#endif + + if (pfChanged) + *pfChanged = fChanged; + + return rc; +} + +VBOXVREGDECL(int) VBoxVrListRectsSet(PVBOXVR_LIST pList, uint32_t cRects, const RTRECT * aRects, bool *pfChanged) +{ + if (pfChanged) + *pfChanged = false; + + if (!cRects && VBoxVrListIsEmpty(pList)) + { + return VINF_SUCCESS; + } + + /* @todo: fChanged will have false alarming here, fix if needed */ + VBoxVrListClear(pList); + + int rc = VBoxVrListRectsAdd(pList, cRects, aRects, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxVrListRectsSet failed rc %d", rc)); + return rc; + } + + if (pfChanged) + *pfChanged = true; + + return VINF_SUCCESS; +} + +VBOXVREGDECL(int) VBoxVrListRectsAdd(PVBOXVR_LIST pList, uint32_t cRects, const RTRECT * aRects, bool *pfChanged) +{ + uint32_t cCovered = 0; + + if (pfChanged) + *pfChanged = false; + +#if 0 +#ifdef DEBUG + { + for (uint32_t i = 0; i < cRects; ++i) + { + RTRECT *pRectI = &aRects[i]; + for (uint32_t j = i + 1; j < cRects; ++j) + { + RTRECT *pRectJ = &aRects[j]; + Assert(!VBoxRectIsIntersect(pRectI, pRectJ)); + } + } + } +#endif +#endif + + /* early sort out the case when there are no new rects */ + for (uint32_t i = 0; i < cRects; ++i) + { + if (VBoxRectIsZero(&aRects[i])) + { + cCovered++; + continue; + } + + for (PRTLISTNODE pEntry1 = pList->ListHead.pNext; pEntry1 != &pList->ListHead; pEntry1 = pEntry1->pNext) + { + PVBOXVR_REG pReg1 = PVBOXVR_REG_FROM_ENTRY(pEntry1); + + if (VBoxRectCovers(&pReg1->Rect, &aRects[i])) + { + cCovered++; + break; + } + } + } + + if (cCovered == cRects) + return VINF_SUCCESS; + + /* rects are not covered, need to go the slow way */ + + VBOXVR_LIST DiffList; + VBoxVrListInit(&DiffList); + RTRECT * pListRects = NULL; + uint32_t cAllocatedRects = 0; + bool fNeedRectreate = true; + bool fChanged = false; + int rc = VINF_SUCCESS; + + for (uint32_t i = 0; i < cRects; ++i) + { + if (VBoxRectIsZero(&aRects[i])) + continue; + + PVBOXVR_REG pReg = vboxVrRegCreate(); + if (!pReg) + { + WARN(("vboxVrRegCreate failed!")); + rc = VERR_NO_MEMORY; + break; + } + pReg->Rect = aRects[i]; + + uint32_t cListRects = VBoxVrListRectsCount(pList); + if (!cListRects) + { + vboxVrListRegAdd(pList, pReg, &pList->ListHead, false); + fChanged = true; + continue; + } + else + { + Assert(VBoxVrListIsEmpty(&DiffList)); + vboxVrListRegAdd(&DiffList, pReg, &DiffList.ListHead, false); + } + + if (cAllocatedRects < cListRects) + { + cAllocatedRects = cListRects + cRects; + Assert(fNeedRectreate); + if (pListRects) + RTMemFree(pListRects); + pListRects = (RTRECT *)RTMemAlloc(sizeof (RTRECT) * cAllocatedRects); + if (!pListRects) + { + WARN(("RTMemAlloc failed!")); + rc = VERR_NO_MEMORY; + break; + } + } + + + if (fNeedRectreate) + { + rc = VBoxVrListRectsGet(pList, cListRects, pListRects); + Assert(rc == VINF_SUCCESS); + fNeedRectreate = false; + } + + bool fDummyChanged = false; + rc = vboxVrListSubstNoJoin(&DiffList, cListRects, pListRects, &fDummyChanged); + if (!RT_SUCCESS(rc)) + { + WARN(("vboxVrListSubstNoJoin failed!")); + rc = VERR_NO_MEMORY; + break; + } + + if (!VBoxVrListIsEmpty(&DiffList)) + { + vboxVrListAddNonintersected(pList, &DiffList); + fNeedRectreate = true; + fChanged = true; + } + + Assert(VBoxVrListIsEmpty(&DiffList)); + } + + if (pListRects) + RTMemFree(pListRects); + + Assert(VBoxVrListIsEmpty(&DiffList) || rc != VINF_SUCCESS); + VBoxVrListClear(&DiffList); + + if (fChanged) + vboxVrListJoinRects(pList); + + if (pfChanged) + *pfChanged = fChanged; + + return VINF_SUCCESS; +} + +VBOXVREGDECL(int) VBoxVrListRectsGet(PVBOXVR_LIST pList, uint32_t cRects, RTRECT * aRects) +{ + if (cRects < VBoxVrListRectsCount(pList)) + return VERR_BUFFER_OVERFLOW; + + uint32_t i = 0; + for (PRTLISTNODE pEntry1 = pList->ListHead.pNext; pEntry1 != &pList->ListHead; pEntry1 = pEntry1->pNext, ++i) + { + PVBOXVR_REG pReg1 = PVBOXVR_REG_FROM_ENTRY(pEntry1); + aRects[i] = pReg1->Rect; + } + return VINF_SUCCESS; +} + +VBOXVREGDECL(int) VBoxVrListCmp(const VBOXVR_LIST *pList1, const VBOXVR_LIST *pList2) +{ + int cTmp = pList1->cEntries - pList2->cEntries; + if (cTmp) + return cTmp; + + PVBOXVR_REG pReg1, pReg2; + + for (pReg1 = RTListNodeGetNext(&pList1->ListHead, VBOXVR_REG, ListEntry), + pReg2 = RTListNodeGetNext(&pList2->ListHead, VBOXVR_REG, ListEntry); + !RTListNodeIsDummy(&pList1->ListHead, pReg1, VBOXVR_REG, ListEntry); + pReg1 = RT_FROM_MEMBER(pReg1->ListEntry.pNext, VBOXVR_REG, ListEntry), + pReg2 = RT_FROM_MEMBER(pReg2->ListEntry.pNext, VBOXVR_REG, ListEntry)) + { + Assert(!RTListNodeIsDummy(&pList2->ListHead, pReg2, VBOXVR_REG, ListEntry)); + cTmp = VBoxRectCmp(&pReg1->Rect, &pReg2->Rect); + if (cTmp) + return cTmp; + } + Assert(RTListNodeIsDummy(&pList2->ListHead, pReg2, VBOXVR_REG, ListEntry)); + return 0; +} + +VBOXVREGDECL(int) VBoxVrListClone(const VBOXVR_LIST *pList, VBOXVR_LIST *pDstList) +{ + VBoxVrListInit(pDstList); + const VBOXVR_REG *pReg; + RTListForEach(&pList->ListHead, pReg, const VBOXVR_REG, ListEntry) + { + PVBOXVR_REG pDstReg = vboxVrRegCreate(); + if (!pDstReg) + { + WARN(("vboxVrRegLaAlloc failed")); + VBoxVrListClear(pDstList); + return VERR_NO_MEMORY; + } + pDstReg->Rect = pReg->Rect; + vboxVrListRegAdd(pDstList, pDstReg, &pDstList->ListHead, true /*bool fAfter*/); + } + + Assert(pDstList->cEntries == pList->cEntries); + + return VINF_SUCCESS; +} + +VBOXVREGDECL(void) VBoxVrCompositorInit(PVBOXVR_COMPOSITOR pCompositor, PFNVBOXVRCOMPOSITOR_ENTRY_RELEASED pfnEntryReleased) +{ + RTListInit(&pCompositor->List); + pCompositor->pfnEntryReleased = pfnEntryReleased; +} + +VBOXVREGDECL(void) VBoxVrCompositorRegionsClear(PVBOXVR_COMPOSITOR pCompositor, bool *pfChanged) +{ + bool fChanged = false; + PVBOXVR_COMPOSITOR_ENTRY pEntry, pEntryNext; + RTListForEachSafe(&pCompositor->List, pEntry, pEntryNext, VBOXVR_COMPOSITOR_ENTRY, Node) + { + VBoxVrCompositorEntryRemove(pCompositor, pEntry); + fChanged = true; + } + + if (pfChanged) + *pfChanged = fChanged; +} + +VBOXVREGDECL(void) VBoxVrCompositorClear(PVBOXVR_COMPOSITOR pCompositor) +{ + VBoxVrCompositorRegionsClear(pCompositor, NULL); +} + +DECLINLINE(void) vboxVrCompositorEntryRelease(PVBOXVR_COMPOSITOR pCompositor, PVBOXVR_COMPOSITOR_ENTRY pEntry, PVBOXVR_COMPOSITOR_ENTRY pReplacingEntry) +{ + if (--pEntry->cRefs) + { + Assert(pEntry->cRefs < UINT32_MAX/2); + return; + } + + Assert(!VBoxVrCompositorEntryIsInList(pEntry)); + + if (pCompositor->pfnEntryReleased) + pCompositor->pfnEntryReleased(pCompositor, pEntry, pReplacingEntry); +} + +DECLINLINE(void) vboxVrCompositorEntryAddRef(PVBOXVR_COMPOSITOR_ENTRY pEntry) +{ + ++pEntry->cRefs; +} + +DECLINLINE(void) vboxVrCompositorEntryAdd(PVBOXVR_COMPOSITOR pCompositor, PVBOXVR_COMPOSITOR_ENTRY pEntry) +{ + RTListPrepend(&pCompositor->List, &pEntry->Node); + vboxVrCompositorEntryAddRef(pEntry); +} + +DECLINLINE(void) vboxVrCompositorEntryRemove(PVBOXVR_COMPOSITOR pCompositor, PVBOXVR_COMPOSITOR_ENTRY pEntry, PVBOXVR_COMPOSITOR_ENTRY pReplacingEntry) +{ + RTListNodeRemove(&pEntry->Node); + vboxVrCompositorEntryRelease(pCompositor, pEntry, pReplacingEntry); +} + +static void vboxVrCompositorEntryReplace(PVBOXVR_COMPOSITOR pCompositor, PVBOXVR_COMPOSITOR_ENTRY pEntry, PVBOXVR_COMPOSITOR_ENTRY pReplacingEntry) +{ + VBoxVrListMoveTo(&pEntry->Vr, &pReplacingEntry->Vr); + + pReplacingEntry->Node = pEntry->Node; + pReplacingEntry->Node.pNext->pPrev = &pReplacingEntry->Node; + pReplacingEntry->Node.pPrev->pNext = &pReplacingEntry->Node; + pEntry->Node.pNext = NULL; + pEntry->Node.pPrev = NULL; + + vboxVrCompositorEntryAddRef(pReplacingEntry); + vboxVrCompositorEntryRelease(pCompositor, pEntry, pReplacingEntry); +} + + + +VBOXVREGDECL(void) VBoxVrCompositorEntryInit(PVBOXVR_COMPOSITOR_ENTRY pEntry) +{ + VBoxVrListInit(&pEntry->Vr); + pEntry->cRefs = 0; +} + +VBOXVREGDECL(bool) VBoxVrCompositorEntryRemove(PVBOXVR_COMPOSITOR pCompositor, PVBOXVR_COMPOSITOR_ENTRY pEntry) +{ + if (!VBoxVrCompositorEntryIsInList(pEntry)) + return false; + + vboxVrCompositorEntryAddRef(pEntry); + + VBoxVrListClear(&pEntry->Vr); + vboxVrCompositorEntryRemove(pCompositor, pEntry, NULL); + vboxVrCompositorEntryRelease(pCompositor, pEntry, NULL); + return true; +} + +VBOXVREGDECL(bool) VBoxVrCompositorEntryReplace(PVBOXVR_COMPOSITOR pCompositor, PVBOXVR_COMPOSITOR_ENTRY pEntry, PVBOXVR_COMPOSITOR_ENTRY pNewEntry) +{ + if (!VBoxVrCompositorEntryIsInList(pEntry)) + return false; + + vboxVrCompositorEntryReplace(pCompositor, pEntry, pNewEntry); + + return true; +} + +static int vboxVrCompositorEntryRegionsSubst(PVBOXVR_COMPOSITOR pCompositor, PVBOXVR_COMPOSITOR_ENTRY pEntry, uint32_t cRects, const RTRECT * paRects, bool *pfChanged) +{ + bool fChanged; + vboxVrCompositorEntryAddRef(pEntry); + + int rc = VBoxVrListRectsSubst(&pEntry->Vr, cRects, paRects, &fChanged); + if (RT_SUCCESS(rc)) + { + if (VBoxVrListIsEmpty(&pEntry->Vr)) + { + Assert(fChanged); + vboxVrCompositorEntryRemove(pCompositor, pEntry, NULL); + } + if (pfChanged) + *pfChanged = false; + } + else + WARN(("VBoxVrListRectsSubst failed, rc %d", rc)); + + vboxVrCompositorEntryRelease(pCompositor, pEntry, NULL); + return rc; +} + +VBOXVREGDECL(int) VBoxVrCompositorEntryRegionsAdd(PVBOXVR_COMPOSITOR pCompositor, PVBOXVR_COMPOSITOR_ENTRY pEntry, uint32_t cRects, const RTRECT *paRects, PVBOXVR_COMPOSITOR_ENTRY *ppReplacedEntry, uint32_t *pfChangeFlags) +{ + bool fOthersChanged = false, fCurChanged = false, fEntryChanged = false, fEntryWasInList = false; + PVBOXVR_COMPOSITOR_ENTRY pCur, pNext, pReplacedEntry = NULL; + int rc = VINF_SUCCESS; + + if (pEntry) + vboxVrCompositorEntryAddRef(pEntry); + + if (!cRects) + { + if (pfChangeFlags) + *pfChangeFlags = 0; + if (pEntry) + vboxVrCompositorEntryRelease(pCompositor, pEntry, NULL); + return VINF_SUCCESS; + } + + if (pEntry) + { + fEntryWasInList = VBoxVrCompositorEntryIsInList(pEntry); + rc = VBoxVrListRectsAdd(&pEntry->Vr, cRects, paRects, &fEntryChanged); + if (RT_SUCCESS(rc)) + { + if (VBoxVrListIsEmpty(&pEntry->Vr)) + { +// WARN(("Empty rectangles passed in, is it expected?")); + if (pfChangeFlags) + *pfChangeFlags = 0; + vboxVrCompositorEntryRelease(pCompositor, pEntry, NULL); + return VINF_SUCCESS; + } + } + else + { + WARN(("VBoxVrListRectsAdd failed, rc %d", rc)); + vboxVrCompositorEntryRelease(pCompositor, pEntry, NULL); + return rc; + } + + Assert(!VBoxVrListIsEmpty(&pEntry->Vr)); + } + else + { + fEntryChanged = true; + } + + RTListForEachSafe(&pCompositor->List, pCur, pNext, VBOXVR_COMPOSITOR_ENTRY, Node) + { + Assert(!VBoxVrListIsEmpty(&pCur->Vr)); + if (pCur != pEntry) + { + if (pEntry && !VBoxVrListCmp(&pCur->Vr, &pEntry->Vr)) + { + VBoxVrListClear(&pCur->Vr); + pReplacedEntry = pCur; + vboxVrCompositorEntryAddRef(pReplacedEntry); + vboxVrCompositorEntryRemove(pCompositor, pCur, pEntry); + if (ppReplacedEntry) + *ppReplacedEntry = pReplacedEntry; + break; + } + else + { + rc = vboxVrCompositorEntryRegionsSubst(pCompositor, pCur, cRects, paRects, &fCurChanged); + if (RT_SUCCESS(rc)) + fOthersChanged |= fCurChanged; + else + { + WARN(("vboxVrCompositorEntryRegionsSubst failed, rc %d", rc)); + return rc; + } + } + } + } + + AssertRC(rc); + + if (pEntry) + { + if (!fEntryWasInList) + { + Assert(!VBoxVrListIsEmpty(&pEntry->Vr)); + vboxVrCompositorEntryAdd(pCompositor, pEntry); + } + vboxVrCompositorEntryRelease(pCompositor, pEntry, NULL); + } + + uint32_t fFlags = 0; + if (fOthersChanged) + { + Assert(!pReplacedEntry); + fFlags = VBOXVR_COMPOSITOR_CF_ENTRY_REGIONS_CHANGED | VBOXVR_COMPOSITOR_CF_REGIONS_CHANGED | VBOXVR_COMPOSITOR_CF_OTHER_ENTRIES_REGIONS_CHANGED; + } + else if (pReplacedEntry) + { + vboxVrCompositorEntryRelease(pCompositor, pReplacedEntry, pEntry); + Assert(fEntryChanged); + fFlags = VBOXVR_COMPOSITOR_CF_ENTRY_REGIONS_CHANGED | VBOXVR_COMPOSITOR_CF_ENTRY_REPLACED; + } + else if (fEntryChanged) + { + Assert(!pReplacedEntry); + fFlags = VBOXVR_COMPOSITOR_CF_ENTRY_REGIONS_CHANGED | VBOXVR_COMPOSITOR_CF_REGIONS_CHANGED; + } + else + { + Assert(!pReplacedEntry); + } + + if (!fEntryWasInList) + Assert(fEntryChanged); + + if (pfChangeFlags) + *pfChangeFlags = fFlags; + + return VINF_SUCCESS; +} + +VBOXVREGDECL(int) VBoxVrCompositorEntryRegionsSubst(PVBOXVR_COMPOSITOR pCompositor, PVBOXVR_COMPOSITOR_ENTRY pEntry, uint32_t cRects, const RTRECT * paRects, bool *pfChanged) +{ + if (!pEntry) + { + WARN(("VBoxVrCompositorEntryRegionsSubst called with zero entry, unsupported!")); + if (pfChanged) + *pfChanged = false; + return VERR_INVALID_PARAMETER; + } + + vboxVrCompositorEntryAddRef(pEntry); + + if (VBoxVrListIsEmpty(&pEntry->Vr)) + { + if (pfChanged) + *pfChanged = false; + vboxVrCompositorEntryRelease(pCompositor, pEntry, NULL); + return VINF_SUCCESS; + } + + int rc = vboxVrCompositorEntryRegionsSubst(pCompositor, pEntry, cRects, paRects, pfChanged); + if (!RT_SUCCESS(rc)) + WARN(("pfChanged failed, rc %d", rc)); + + vboxVrCompositorEntryRelease(pCompositor, pEntry, NULL); + + return rc; +} + +VBOXVREGDECL(int) VBoxVrCompositorEntryRegionsSet(PVBOXVR_COMPOSITOR pCompositor, PVBOXVR_COMPOSITOR_ENTRY pEntry, uint32_t cRects, const RTRECT *paRects, bool *pfChanged) +{ + if (!pEntry) + { + WARN(("VBoxVrCompositorEntryRegionsSet called with zero entry, unsupported!")); + if (pfChanged) + *pfChanged = false; + return VERR_INVALID_PARAMETER; + } + + vboxVrCompositorEntryAddRef(pEntry); + + bool fChanged = false, fCurChanged = false; + uint32_t fChangeFlags = 0; + int rc; + fCurChanged = VBoxVrCompositorEntryRemove(pCompositor, pEntry); + fChanged |= fCurChanged; + + rc = VBoxVrCompositorEntryRegionsAdd(pCompositor, pEntry, cRects, paRects, NULL, &fChangeFlags); + if (RT_SUCCESS(rc)) + { + fChanged |= !!fChangeFlags; + if (pfChanged) + *pfChanged = fChanged; + } + else + WARN(("VBoxVrCompositorEntryRegionsAdd failed, rc %d", rc)); + + vboxVrCompositorEntryRelease(pCompositor, pEntry, NULL); + + return VINF_SUCCESS; +} + +VBOXVREGDECL(int) VBoxVrCompositorEntryListIntersect(PVBOXVR_COMPOSITOR pCompositor, PVBOXVR_COMPOSITOR_ENTRY pEntry, const VBOXVR_LIST *pList2, bool *pfChanged) +{ + int rc = VINF_SUCCESS; + bool fChanged = false; + + vboxVrCompositorEntryAddRef(pEntry); + + if (VBoxVrCompositorEntryIsInList(pEntry)) + { + rc = VBoxVrListIntersect(&pEntry->Vr, pList2, &fChanged); + if (RT_SUCCESS(rc)) + { + if (VBoxVrListIsEmpty(&pEntry->Vr)) + { + Assert(fChanged); + vboxVrCompositorEntryRemove(pCompositor, pEntry, NULL); + } + } + else + { + WARN(("VBoxVrListRectsIntersect failed, rc %d", rc)); + } + } + + if (pfChanged) + *pfChanged = fChanged; + + vboxVrCompositorEntryRelease(pCompositor, pEntry, NULL); + + return rc; +} + +VBOXVREGDECL(int) VBoxVrCompositorEntryRegionsIntersect(PVBOXVR_COMPOSITOR pCompositor, PVBOXVR_COMPOSITOR_ENTRY pEntry, uint32_t cRects, const RTRECT *paRects, bool *pfChanged) +{ + int rc = VINF_SUCCESS; + bool fChanged = false; + + vboxVrCompositorEntryAddRef(pEntry); + + if (VBoxVrCompositorEntryIsInList(pEntry)) + { + rc = VBoxVrListRectsIntersect(&pEntry->Vr, cRects, paRects, &fChanged); + if (RT_SUCCESS(rc)) + { + if (VBoxVrListIsEmpty(&pEntry->Vr)) + { + Assert(fChanged); + vboxVrCompositorEntryRemove(pCompositor, pEntry, NULL); + } + } + else + { + WARN(("VBoxVrListRectsIntersect failed, rc %d", rc)); + } + } + + if (pfChanged) + *pfChanged = fChanged; + + vboxVrCompositorEntryRelease(pCompositor, pEntry, NULL); + + return rc; +} + +VBOXVREGDECL(int) VBoxVrCompositorEntryListIntersectAll(PVBOXVR_COMPOSITOR pCompositor, const VBOXVR_LIST *pList2, bool *pfChanged) +{ + VBOXVR_COMPOSITOR_ITERATOR Iter; + VBoxVrCompositorIterInit(pCompositor, &Iter); + PVBOXVR_COMPOSITOR_ENTRY pEntry; + int rc = VINF_SUCCESS; + bool fChanged = false; + + while ((pEntry = VBoxVrCompositorIterNext(&Iter)) != NULL) + { + bool fTmpChanged = false; + int tmpRc = VBoxVrCompositorEntryListIntersect(pCompositor, pEntry, pList2, &fTmpChanged); + if (RT_SUCCESS(tmpRc)) + { + fChanged |= fChanged; + } + else + { + WARN(("VBoxVrCompositorEntryRegionsIntersect failed, rc %d", tmpRc)); + rc = tmpRc; + } + } + + if (pfChanged) + *pfChanged = fChanged; + + return rc; +} + +VBOXVREGDECL(int) VBoxVrCompositorEntryRegionsIntersectAll(PVBOXVR_COMPOSITOR pCompositor, uint32_t cRegions, const RTRECT *paRegions, bool *pfChanged) +{ + VBOXVR_COMPOSITOR_ITERATOR Iter; + VBoxVrCompositorIterInit(pCompositor, &Iter); + PVBOXVR_COMPOSITOR_ENTRY pEntry; + int rc = VINF_SUCCESS; + bool fChanged = false; + + while ((pEntry = VBoxVrCompositorIterNext(&Iter)) != NULL) + { + bool fTmpChanged = false; + int tmpRc = VBoxVrCompositorEntryRegionsIntersect(pCompositor, pEntry, cRegions, paRegions, &fTmpChanged); + if (RT_SUCCESS(tmpRc)) + { + fChanged |= fChanged; + } + else + { + WARN(("VBoxVrCompositorEntryRegionsIntersect failed, rc %d", tmpRc)); + rc = tmpRc; + } + } + + if (pfChanged) + *pfChanged = fChanged; + + return rc; +} + +VBOXVREGDECL(int) VBoxVrCompositorEntryRegionsTranslate(PVBOXVR_COMPOSITOR pCompositor, PVBOXVR_COMPOSITOR_ENTRY pEntry, int32_t x, int32_t y, bool *pfChanged) +{ + if (!pEntry) + { + WARN(("VBoxVrCompositorEntryRegionsTranslate called with zero entry, unsupported!")); + if (pfChanged) + *pfChanged = false; + return VERR_INVALID_PARAMETER; + } + + vboxVrCompositorEntryAddRef(pEntry); + + if ((!x && !y) + || !VBoxVrCompositorEntryIsInList(pEntry)) + { + if (pfChanged) + *pfChanged = false; + + vboxVrCompositorEntryRelease(pCompositor, pEntry, NULL); + return VINF_SUCCESS; + } + + VBoxVrListTranslate(&pEntry->Vr, x, y); + + Assert(!VBoxVrListIsEmpty(&pEntry->Vr)); + + PVBOXVR_COMPOSITOR_ENTRY pCur; + uint32_t cRects = 0; + RTRECT *paRects = NULL; + int rc = VINF_SUCCESS; + RTListForEach(&pCompositor->List, pCur, VBOXVR_COMPOSITOR_ENTRY, Node) + { + Assert(!VBoxVrListIsEmpty(&pCur->Vr)); + + if (pCur == pEntry) + continue; + + if (!paRects) + { + cRects = VBoxVrListRectsCount(&pEntry->Vr); + Assert(cRects); + paRects = (RTRECT*)RTMemAlloc(cRects * sizeof (RTRECT)); + if (!paRects) + { + WARN(("RTMemAlloc failed!")); + rc = VERR_NO_MEMORY; + break; + } + + rc = VBoxVrListRectsGet(&pEntry->Vr, cRects, paRects); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxVrListRectsGet failed! rc %d", rc)); + break; + } + } + + rc = vboxVrCompositorEntryRegionsSubst(pCompositor, pCur, cRects, paRects, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("vboxVrCompositorEntryRegionsSubst failed! rc %d", rc)); + break; + } + } + + if (pfChanged) + *pfChanged = true; + + if (paRects) + RTMemFree(paRects); + + vboxVrCompositorEntryRelease(pCompositor, pEntry, NULL); + + return rc; +} + +VBOXVREGDECL(void) VBoxVrCompositorVisit(PVBOXVR_COMPOSITOR pCompositor, PFNVBOXVRCOMPOSITOR_VISITOR pfnVisitor, void *pvVisitor) +{ + PVBOXVR_COMPOSITOR_ENTRY pEntry, pEntryNext; + RTListForEachSafe(&pCompositor->List, pEntry, pEntryNext, VBOXVR_COMPOSITOR_ENTRY, Node) + { + if (!pfnVisitor(pCompositor, pEntry, pvVisitor)) + return; + } +} |
