summaryrefslogtreecommitdiffstats
path: root/src/VBox/GuestHost/OpenGL/util/vreg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/GuestHost/OpenGL/util/vreg.cpp')
-rw-r--r--src/VBox/GuestHost/OpenGL/util/vreg.cpp1702
1 files changed, 1702 insertions, 0 deletions
diff --git a/src/VBox/GuestHost/OpenGL/util/vreg.cpp b/src/VBox/GuestHost/OpenGL/util/vreg.cpp
new file mode 100644
index 00000000..0c3c4afd
--- /dev/null
+++ b/src/VBox/GuestHost/OpenGL/util/vreg.cpp
@@ -0,0 +1,1702 @@
+/* $Id: vreg.cpp $ */
+/** @file
+ * Visible Regions processing API implementation
+ */
+
+/*
+ * Copyright (C) 2012-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef IN_VMSVGA3D
+# include "../include/cr_vreg.h"
+# define WARN AssertMsgFailed
+#else
+# include <cr_vreg.h>
+# include <cr_error.h>
+#endif
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/asm.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(PRTMEMCACHE phCache, size_t cbElement)
+{
+ int rc = RTMemCacheCreate(phCache,
+ cbElement,
+ 0 /* cbAlignment */,
+ UINT32_MAX /* cMaxObjects */,
+ NULL /* pfnCtor*/,
+ NULL /* pfnDtor*/,
+ NULL /* pvUser*/,
+ 0 /* fFlags*/);
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("RTMemCacheCreate failed rc %d", rc));
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+# define vboxVrLaDestroy(_c) RTMemCacheDestroy((_c))
+# endif /* !VBOXVDBG_VR_LAL_DISABLE */
+
+#else /* IN_RING0 */
+# ifdef RT_OS_WINDOWS
+# undef PAGE_SIZE
+# undef PAGE_SHIFT
+# include <iprt/nt/ntddk.h>
+# 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 /* !RT_OS_WINDOWS */
+# error "port me!"
+# endif /* !RT_OS_WINDOWS */
+#endif /* IN_RING0 */
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define VBOXVR_INVALID_COORD (~0U)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static volatile int32_t g_cVBoxVrInits = 0;
+
+
+static PVBOXVR_REG vboxVrRegCreate(void)
+{
+#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);
+}
+
+VBOXVREGDECL(int) VBoxVrInit(void)
+{
+ 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(void)
+{
+ 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(PCVBOXVR_REG pReg1, PCVBOXVR_REG pReg2);
+typedef FNVBOXVR_CB_COMPARATOR *PFNVBOXVR_CB_COMPARATOR;
+
+static DECLCALLBACK(int) vboxVrRegNonintersectedComparator(PCRTRECT pRect1, PCRTRECT 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(_p) vboxVrDbgListDoVerify(_p)
+#else
+# define vboxVrDbgListVerify(_p) do {} while (0)
+#endif
+
+
+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)
+{
+ for (;;)
+ {
+ 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;
+ }
+}
+
+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);
+ for (;;)
+ {
+ 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;
+ }
+ }
+
+ Assert(VBoxVrListIsEmpty(pList2));
+}
+
+static int vboxVrListRegIntersectSubstNoJoin(PVBOXVR_LIST pList1, PVBOXVR_REG pReg1, PCRTRECT 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 pNext;
+ PRTLISTNODE pEntry = List.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,
+ PCRTRECT pRect2, void *pvContext, PRTLISTNODE *ppNext);
+typedef FNVBOXVR_CB_INTERSECTED_VISITOR *PFNVBOXVR_CB_INTERSECTED_VISITOR;
+
+static void vboxVrListVisitIntersected(PVBOXVR_LIST pList1, uint32_t cRects, PCRTRECT 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)
+ {
+ PCRTRECT 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;
+ pReg1 = PVBOXVR_REG_FROM_ENTRY(pEntry1);
+ }
+ }
+}
+
+#if 0 /* unused */
+/**
+ * @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, PCRTRECT 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)
+ {
+ PCRTRECT 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;
+ }
+}
+#endif /* unused */
+
+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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ if (pReg1->Rect.xRight > pReg2->Rect.xLeft)
+ {
+ /* no more to be done for for pReg1 */
+ break;
+ }
+
+ continue;
+ }
+
+ 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;
+typedef VBOXVR_CBDATA_SUBST *PVBOXVR_CBDATA_SUBST;
+
+static DECLCALLBACK(PRTLISTNODE) vboxVrListSubstNoJoinCb(PVBOXVR_LIST pList, PVBOXVR_REG pReg1, PCRTRECT 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, PCRTRECT 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 PCRTRECT vboxVrRectsOrder(uint32_t cRects, PCRTRECT aRects)
+{
+#ifdef VBOX_STRICT
+ for (uint32_t i = 0; i < cRects; ++i)
+ {
+ PRTRECT pRectI = &aRects[i];
+ for (uint32_t j = i + 1; j < cRects; ++j)
+ {
+ PRTRECT pRectJ = &aRects[j];
+ Assert(!VBoxRectIsIntersect(pRectI, pRectJ));
+ }
+ }
+#endif
+
+ PRTRECT pRects = (PRTRECT)aRects;
+ /* check if rects are ordered already */
+ for (uint32_t i = 0; i < cRects - 1; ++i)
+ {
+ PRTRECT pRect1 = &pRects[i];
+ PRTRECT pRect2 = &pRects[i+1];
+ if (vboxVrRegNonintersectedComparator(pRect1, pRect2) < 0)
+ continue;
+
+ WARN(("rects are unoreded!"));
+
+ if (pRects == aRects)
+ {
+ pRects = (PRTRECT)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;
+ for (;;)
+ {
+ RTRECT Tmp = *pRect1;
+ *pRect1 = *pRect2;
+ *pRect2 = Tmp;
+
+ if (j < 0)
+ break;
+
+ if (vboxVrRegNonintersectedComparator(pRect1, pRect1-1) > 0)
+ break;
+
+ pRect2 = pRect1--;
+ --j;
+ }
+ }
+
+ 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);
+ }
+}
+
+#if 0 /* unused */
+
+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, PCRTRECT pRect2,
+ void *pvContext, PPRTLISTNODE ppNext)
+{
+ RT_NOREF1(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;
+}
+
+#endif /* unused */
+
+static int vboxVrListIntersectNoJoin(PVBOXVR_LIST pList, PCVBOXVR_LIST pList2, bool *pfChanged)
+{
+ bool fChanged = false;
+ if (pfChanged)
+ *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)
+ {
+ PCVBOXVR_REG pReg2 = PVBOXVR_REG_FROM_ENTRY(pEntry2);
+ PCRTRECT 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, PCVBOXVR_LIST pList2, bool *pfChanged)
+{
+ bool fChanged = false;
+
+ int rc = vboxVrListIntersectNoJoin(pList, pList2, &fChanged);
+ if (pfChanged)
+ *pfChanged = fChanged;
+
+ if (!RT_SUCCESS(rc))
+ {
+ WARN(("vboxVrListSubstNoJoin failed!"));
+ return rc;
+ }
+
+ if (fChanged)
+ {
+ vboxVrListJoinRects(pList);
+ }
+
+ return rc;
+}
+
+VBOXVREGDECL(int) VBoxVrListRectsIntersect(PVBOXVR_LIST pList, uint32_t cRects, PCRTRECT 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, PCRTRECT aRects, bool *pfChanged)
+{
+#if 0
+ PCRTRECT 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, PCRTRECT 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, PCRTRECT aRects, bool *pfChanged)
+{
+ uint32_t cCovered = 0;
+
+ if (pfChanged)
+ *pfChanged = false;
+
+#if 0
+#ifdef VBOX_STRICT
+ for (uint32_t i = 0; i < cRects; ++i)
+ {
+ PRTRECT pRectI = &aRects[i];
+ for (uint32_t j = i + 1; j < cRects; ++j)
+ {
+ PRTRECT 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);
+ PRTRECT 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;
+ }
+ 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(PCVBOXVR_LIST pList, PVBOXVR_LIST pDstList)
+{
+ VBoxVrListInit(pDstList);
+ PCVBOXVR_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, PCRTRECT 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, PCRTRECT paRects, PVBOXVR_COMPOSITOR_ENTRY *ppReplacedEntry,
+ uint32_t *pfChangeFlags)
+{
+ bool fOthersChanged = false;
+ bool fCurChanged = false;
+ bool fEntryChanged = false;
+ bool fEntryWasInList = false;
+ PVBOXVR_COMPOSITOR_ENTRY pCur;
+ PVBOXVR_COMPOSITOR_ENTRY pNext;
+ PVBOXVR_COMPOSITOR_ENTRY 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;
+ }
+
+ 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, PCRTRECT 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, PCRTRECT 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,
+ PCVBOXVR_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, PCRTRECT 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, PCVBOXVR_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 |= fTmpChanged;
+ else
+ {
+ WARN(("VBoxVrCompositorEntryRegionsIntersect failed, rc %d", tmpRc));
+ rc = tmpRc;
+ }
+ }
+
+ if (pfChanged)
+ *pfChanged = fChanged;
+
+ return rc;
+}
+
+VBOXVREGDECL(int) VBoxVrCompositorEntryRegionsIntersectAll(PVBOXVR_COMPOSITOR pCompositor, uint32_t cRegions, PCRTRECT 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 |= fTmpChanged;
+ 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;
+ PRTRECT 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;
+ }
+}
+