diff --git a/src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11-output.cpp b/src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11-output.cpp index 4df07108bba..bea8e637e2a 100644 --- a/src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11-output.cpp +++ b/src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11-output.cpp @@ -30,6 +30,7 @@ #include #include +#include #ifdef RT_OS_WINDOWS # include @@ -53,10 +54,12 @@ typedef struct DXOUTPUTTARGETMETHODS DECLR3CALLBACKMEMBER(int, pfnDXOutputTargetConvert,(VMSVGAOUTPUTTARGET *pOutputTarget, ID3D11DeviceContext1 *pDeviceContext, ID3D11ShaderResourceView *pSrcSrv, - UINT srcW, UINT srcH)); + UINT srcW, UINT srcH, + SVGASignedRect const &dirtyRect)); DECLR3CALLBACKMEMBER(int, pfnDXOutputTargetReadback,(VMSVGAOUTPUTTARGET *pOutputTarget, ID3D11DeviceContext1 *pDeviceContext, - SVGASignedRect const &updateRect)); + SVGASignedRect const &updateRect, + bool *pfChanged)); } DXOUTPUTTARGETMETHODS; /* Generic OT object with target specific data and virtual methods. */ @@ -134,15 +137,30 @@ static DECLCALLBACK(void) dxOutputTargetDestroy_B8G8R8X8_I(VMSVGAOUTPUTTARGET *p static DECLCALLBACK(int) dxOutputTargetConvert_B8G8R8X8_I(VMSVGAOUTPUTTARGET *pOutputTarget, ID3D11DeviceContext1 *pDeviceContext, ID3D11ShaderResourceView *pSrcSrv, - UINT srcW, UINT srcH) + UINT srcW, UINT srcH, + SVGASignedRect const &dirtyRect) { - RT_NOREF(srcW, srcH); - DXOUTPUTTARGET_B8G8R8X8_I *pThis = (DXOUTPUTTARGET_B8G8R8X8_I *)pOutputTarget->pHwOutputTarget; + SVGASignedRect clipRect = dirtyRect; + SVGASignedRect const boundRect = { 0, 0, (int32_t)srcW, (int32_t)srcH }; + vmsvgaR3ClipRect(&boundRect, &clipRect); + if (clipRect.left >= clipRect.right || clipRect.top >= clipRect.bottom) + return VINF_SUCCESS; + + D3D11_BOX srcBox; + srcBox.left = (UINT)clipRect.left; + srcBox.top = (UINT)clipRect.top; + srcBox.front = 0; + srcBox.right = (UINT)clipRect.right; + srcBox.bottom = (UINT)clipRect.bottom; + srcBox.back = 1; + ID3D11Resource *pSrcResource = NULL; pSrcSrv->GetResource(&pSrcResource); - pDeviceContext->CopySubresourceRegion(pThis->pStagingTexture, 0, 0, 0, 0, pSrcResource, 0, NULL); + pDeviceContext->CopySubresourceRegion(pThis->pStagingTexture, 0, + (UINT)clipRect.left, (UINT)clipRect.top, 0, + pSrcResource, 0, &srcBox); D3D_RELEASE(pSrcResource); return VINF_SUCCESS; @@ -151,9 +169,11 @@ static DECLCALLBACK(int) dxOutputTargetConvert_B8G8R8X8_I(VMSVGAOUTPUTTARGET *pO static DECLCALLBACK(int) dxOutputTargetReadback_B8G8R8X8_I(VMSVGAOUTPUTTARGET *pOutputTarget, ID3D11DeviceContext1 *pDeviceContext, - SVGASignedRect const &updateRect) + SVGASignedRect const &updateRect, + bool *pfChanged) { DXOUTPUTTARGET_B8G8R8X8_I *pThis = (DXOUTPUTTARGET_B8G8R8X8_I *)pOutputTarget->pHwOutputTarget; + *pfChanged = false; /* Copy data from staging resource to the system memory. */ D3D11_MAPPED_SUBRESOURCE map; @@ -175,7 +195,12 @@ static DECLCALLBACK(int) dxOutputTargetReadback_B8G8R8X8_I(VMSVGAOUTPUTTARGET *p && updateRect.bottom == (int32_t)cHeight && cWidth * cbPixel == map.RowPitch) { - memcpy(pu8Dst, pu8Src, map.RowPitch * cHeight); + size_t const cb = (size_t)map.RowPitch * cHeight; + if (memcmp(pu8Dst, pu8Src, cb) != 0) + { + memcpy(pu8Dst, pu8Src, cb); + *pfChanged = true; + } } else { @@ -189,7 +214,11 @@ static DECLCALLBACK(int) dxOutputTargetReadback_B8G8R8X8_I(VMSVGAOUTPUTTARGET *p uint32_t const cbRowWidth = cbPixel * (updateRect.right - updateRect.left); for (int32_t iRow = 0; iRow < updateRect.bottom - updateRect.top; ++iRow) { - memcpy(pu8DstBox, pu8SrcBox, cbRowWidth); + if (memcmp(pu8DstBox, pu8SrcBox, cbRowWidth) != 0) + { + memcpy(pu8DstBox, pu8SrcBox, cbRowWidth); + *pfChanged = true; + } pu8SrcBox += map.RowPitch; pu8DstBox += cbPixel * cWidth; @@ -404,8 +433,11 @@ static DECLCALLBACK(void) dxOutputTargetDestroy_I420(VMSVGAOUTPUTTARGET *pOutput static DECLCALLBACK(int) dxOutputTargetConvert_I420(VMSVGAOUTPUTTARGET *pOutputTarget, ID3D11DeviceContext1 *pDeviceContext, ID3D11ShaderResourceView *pSrcSrv, - UINT srcW, UINT srcH) + UINT srcW, UINT srcH, + SVGASignedRect const &dirtyRect) { + RT_NOREF(dirtyRect); + DXOUTPUTTARGET_I420 *pHwOutputTarget = (DXOUTPUTTARGET_I420 *)pOutputTarget->pHwOutputTarget; /* Save/restore pipeline state. @@ -537,9 +569,11 @@ static int dxCopyPlane(ID3D11DeviceContext1 *pDeviceContext, static DECLCALLBACK(int) dxOutputTargetReadback_I420(VMSVGAOUTPUTTARGET *pOutputTarget, ID3D11DeviceContext1 *pDeviceContext, - SVGASignedRect const &updateRect) + SVGASignedRect const &updateRect, + bool *pfChanged) { RT_NOREF(updateRect); + *pfChanged = true; DXOUTPUTTARGET_I420 *pHwOutputTarget = (DXOUTPUTTARGET_I420 *)pOutputTarget->pHwOutputTarget; @@ -663,19 +697,24 @@ void dxHwOutputTargetDestroy(VMSVGAOUTPUTTARGET *pOutputTarget) int dxHwOutputTargetConvert(VMSVGAOUTPUTTARGET *pOutputTarget, ID3D11DeviceContext1 *pDeviceContext, ID3D11ShaderResourceView *pSrcSrv, - UINT srcW, UINT srcH) + UINT srcW, UINT srcH, + SVGASignedRect const &dirtyRect) { VMSVGAHWOUTPUTTARGET *pHwOutputTarget = pOutputTarget->pHwOutputTarget; if (pHwOutputTarget->methods.pfnDXOutputTargetConvert) - return pHwOutputTarget->methods.pfnDXOutputTargetConvert(pOutputTarget, pDeviceContext, pSrcSrv, srcW, srcH); + return pHwOutputTarget->methods.pfnDXOutputTargetConvert(pOutputTarget, pDeviceContext, pSrcSrv, + srcW, srcH, dirtyRect); return VINF_SUCCESS; } int dxHwOutputTargetReadback(VMSVGAOUTPUTTARGET *pOutputTarget, ID3D11DeviceContext1 *pDeviceContext, - SVGASignedRect const &updateRect) + SVGASignedRect const &updateRect, + bool *pfChanged) { + *pfChanged = false; + SVGASignedRect clipRect = updateRect; SVGASignedRect const boundRect = { 0 , 0, (int32_t)pOutputTarget->desc.cWidth, (int32_t)pOutputTarget->desc.cHeight }; vmsvgaR3ClipRect(&boundRect, &clipRect); @@ -684,6 +723,6 @@ int dxHwOutputTargetReadback(VMSVGAOUTPUTTARGET *pOutputTarget, VMSVGAHWOUTPUTTARGET *pHwOutputTarget = pOutputTarget->pHwOutputTarget; if (pHwOutputTarget->methods.pfnDXOutputTargetReadback) - return pHwOutputTarget->methods.pfnDXOutputTargetReadback(pOutputTarget, pDeviceContext, clipRect); + return pHwOutputTarget->methods.pfnDXOutputTargetReadback(pOutputTarget, pDeviceContext, clipRect, pfChanged); return VINF_SUCCESS; } diff --git a/src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11.cpp b/src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11.cpp index 13880a6b7f7..3a52230f52e 100644 --- a/src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11.cpp +++ b/src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11.cpp @@ -3965,10 +3965,82 @@ DECLINLINE(int) dxFenceCmp64(uint64_t u64FenceA, uint64_t u64FenceB) } +DECLINLINE(SVGASignedRect) dxScreenRectUnion(SVGASignedRect const &a, SVGASignedRect const &b) +{ + SVGASignedRect u; + u.left = RT_MIN(a.left, b.left); + u.top = RT_MIN(a.top, b.top); + u.right = RT_MAX(a.right, b.right); + u.bottom = RT_MAX(a.bottom, b.bottom); + return u; +} + + +DECLINLINE(bool) dxScreenRectsTouchOrOverlap(SVGASignedRect const &a, SVGASignedRect const &b) +{ + return a.left <= b.right + && b.left <= a.right + && a.top <= b.bottom + && b.top <= a.bottom; +} + + +DECLINLINE(uint64_t) dxScreenRectArea(SVGASignedRect const &r) +{ + if ( r.right <= r.left + || r.bottom <= r.top) + return 0; + return (uint64_t)(r.right - r.left) * (uint64_t)(r.bottom - r.top); +} + + +DECLINLINE(bool) dxShouldMergeScreenRects(SVGASignedRect const &a, SVGASignedRect const &b) +{ + if (dxScreenRectsTouchOrOverlap(a, b)) + return true; + + SVGASignedRect const u = dxScreenRectUnion(a, b); + uint64_t const cbUseful = dxScreenRectArea(a) + dxScreenRectArea(b); + uint64_t const cbUnion = dxScreenRectArea(u); + + /* + * A readback/update pair has fixed overhead. Merge nearby small rects when + * the extra copied area is bounded; keep far apart large rects separate. + */ + return cbUnion <= _256K + || cbUnion <= cbUseful * 4; +} + + +static SVGASignedRect dxPendingScreenUpdateUnion(VMSVGAHWSCREEN *p) +{ + SVGASignedRect dirtyRect; + dirtyRect.left = 0; + dirtyRect.top = 0; + dirtyRect.right = (int32_t)p->cHwScreenWidth; + dirtyRect.bottom = (int32_t)p->cHwScreenHeight; + + bool fHaveDirtyRect = false; + for (uint32_t i = 0; i < p->cUpdates; ++i) + { + uint32_t const idx = (p->idxLastUpdate + i) % RT_ELEMENTS(p->aUpdates); + if (p->aUpdates[idx].u64ReadbackFence != p->u64ReadbackFence) + continue; + + dirtyRect = fHaveDirtyRect ? dxScreenRectUnion(dirtyRect, p->aUpdates[idx].rect) + : p->aUpdates[idx].rect; + fHaveDirtyRect = true; + } + + return dirtyRect; +} + + static void dxStartScreenReadback(VMSVGASCREENOBJECT *pScreen, DXDEVICE *pDXDevice) { /* Get the screen data to the system memory. */ VMSVGAHWSCREEN *p = pScreen->pHwScreen; + SVGASignedRect const dirtyRect = dxPendingScreenUpdateUnion(p); /* Check targets. Targets are created and deleted on FIFO thread so it is ok to access them without a lock. */ VMSVGAOUTPUTTARGET *pOutputTarget; @@ -3978,7 +4050,7 @@ static void dxStartScreenReadback(VMSVGASCREENOBJECT *pScreen, DXDEVICE *pDXDevi AssertContinue(pHwOutputTarget); dxHwOutputTargetConvert(pOutputTarget, pDXDevice->pImmediateContext, - p->pScreenTextureSRV, p->cHwScreenWidth, p->cHwScreenHeight); + p->pScreenTextureSRV, p->cHwScreenWidth, p->cHwScreenHeight, dirtyRect); } /* Submit all the work: target conversion and readback to staging resources. */ @@ -4020,13 +4092,20 @@ static void dxProcessPendingUpdates(PVGASTATECC pThisCC, VMSVGASCREENOBJECT *pSc SVGASignedRect const &updateRect = p->aUpdates[p->idxLastUpdate].rect; /* Check targets. Targets are created and deleted on FIFO thread so it is ok to access them. */ + bool fAnyOutputTargetChanged = false; VMSVGAOUTPUTTARGET *pOutputTarget; RTListForEach(&pScreen->listOutputTargets, pOutputTarget, VMSVGAOUTPUTTARGET, nodeOutputTarget) { VMSVGAHWOUTPUTTARGET *pHwOutputTarget = pOutputTarget->pHwOutputTarget; AssertContinue(pHwOutputTarget); - dxHwOutputTargetReadback(pOutputTarget, pDXDevice->pImmediateContext, updateRect); + bool fOutputTargetChanged = false; + int rc = dxHwOutputTargetReadback(pOutputTarget, pDXDevice->pImmediateContext, + updateRect, &fOutputTargetChanged); + AssertRC(rc); + if (!fOutputTargetChanged) + continue; + fAnyOutputTargetChanged = true; /* Increment u64UpdateSequenceNumber, skipping 0 on rollover. */ uint64_t u64 = pOutputTarget->u64UpdateSequenceNumber + 1; @@ -4037,9 +4116,10 @@ static void dxProcessPendingUpdates(PVGASTATECC pThisCC, VMSVGASCREENOBJECT *pSc p->aUpdates[p->idxLastUpdate].u64ReadbackFence = 0; - vmsvgaR3UpdateScreen(pThisCC, pScreen, - updateRect.left, updateRect.top, - updateRect.right - updateRect.left, updateRect.bottom - updateRect.top); + if (fAnyOutputTargetChanged) + vmsvgaR3UpdateScreen(pThisCC, pScreen, + updateRect.left, updateRect.top, + updateRect.right - updateRect.left, updateRect.bottom - updateRect.top); /* Next update. */ p->idxLastUpdate = (p->idxLastUpdate + 1) % RT_ELEMENTS(p->aUpdates); @@ -4047,7 +4127,9 @@ static void dxProcessPendingUpdates(PVGASTATECC pThisCC, VMSVGASCREENOBJECT *pSc } if (p->cUpdates > 0) + { dxStartScreenReadback(pScreen, pDXDevice); /* There were screen updates since the last readback. */ + } else p->fReadingBack = false; } @@ -4055,10 +4137,33 @@ static void dxProcessPendingUpdates(PVGASTATECC pThisCC, VMSVGASCREENOBJECT *pSc } +static bool dxMergePendingScreenUpdate(VMSVGAHWSCREEN *p, SVGASignedRect const &updateRect) +{ + for (uint32_t i = 0; i < p->cUpdates; ++i) + { + uint32_t const idx = (p->idxLastUpdate + i) % RT_ELEMENTS(p->aUpdates); + if (p->aUpdates[idx].u64ReadbackFence != p->u64ReadbackFence) + continue; + + SVGASignedRect const &pendingRect = p->aUpdates[idx].rect; + if (!dxShouldMergeScreenRects(pendingRect, updateRect)) + continue; + + p->aUpdates[idx].rect = dxScreenRectUnion(pendingRect, updateRect); + return true; + } + + return false; +} + + static void dxStoreScreenUpdate(PVGASTATECC pThisCC, VMSVGASCREENOBJECT *pScreen, SVGASignedRect const &updateRect) { VMSVGAHWSCREEN *p = pScreen->pHwScreen; + if (dxMergePendingScreenUpdate(p, updateRect)) + return; + if (p->cUpdates >= RT_ELEMENTS(p->aUpdates)) { /* Overflow, usually should not happen. Wait for previous updates to complete. */ diff --git a/src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11.h b/src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11.h index ee27e2e31ef..5e1f7a51151 100644 --- a/src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11.h +++ b/src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11.h @@ -54,10 +54,12 @@ void dxHwOutputTargetDestroy(VMSVGAOUTPUTTARGET *pOutputTarget); int dxHwOutputTargetConvert(VMSVGAOUTPUTTARGET *pOutputTarget, ID3D11DeviceContext1 *pDeviceContext, ID3D11ShaderResourceView *pSrcSrv, - UINT srcW, UINT srcH); + UINT srcW, UINT srcH, + SVGASignedRect const &dirtyRect); int dxHwOutputTargetReadback(VMSVGAOUTPUTTARGET *pOutputTarget, ID3D11DeviceContext1 *pDeviceContext, - SVGASignedRect const &updateRect); + SVGASignedRect const &updateRect, + bool *pfChanged); /* A buffer which accumulates data. If the buffer is full (offFree == cbBuffer), * then a query is issued. The buffer can be reused when the query finishes.