diff --git a/src/VBox/Devices/Graphics/DevVGA-SVGA-internal.h b/src/VBox/Devices/Graphics/DevVGA-SVGA-internal.h index d70b7d28ae2..2e17ec97794 100644 --- a/src/VBox/Devices/Graphics/DevVGA-SVGA-internal.h +++ b/src/VBox/Devices/Graphics/DevVGA-SVGA-internal.h @@ -213,6 +213,10 @@ typedef struct VMSVGAR3STATE STAMPROFILE StatR3Cmd3dDrawPrimitivesProf; STAMPROFILE StatR3Cmd3dSurfaceDmaProf; STAMPROFILE StatR3Cmd3dBlitSurfaceToScreenProf; + STAMPROFILE StatR3DxStartScreenReadbackProf; + STAMPROFILE StatR3DxProcessPendingUpdatesProf; + STAMPROFILE StatR3DxOutputTargetConvertProf; + STAMPROFILE StatR3DxOutputTargetReadbackProf; STAMCOUNTER StatR3CmdDefineGmr2; STAMCOUNTER StatR3CmdDefineGmr2Free; STAMCOUNTER StatR3CmdDefineGmr2Modify; @@ -271,6 +275,16 @@ typedef struct VMSVGAR3STATE STAMCOUNTER StatR3Cmd3dGenerateMipmaps; STAMCOUNTER StatR3Cmd3dActivateSurface; STAMCOUNTER StatR3Cmd3dDeactivateSurface; + STAMCOUNTER StatR3DxStartScreenReadback; + STAMCOUNTER StatR3DxReadbackPoll; + STAMCOUNTER StatR3DxReadbackReady; + STAMCOUNTER StatR3DxReadbackUpdateRects; + STAMCOUNTER StatR3DxReadbackUpdateMerge; + STAMCOUNTER StatR3DxReadbackRestart; + STAMCOUNTER StatR3DxReadbackChanged; + STAMCOUNTER StatR3DxReadbackUnchanged; + STAMCOUNTER StatR3DxOutputTargetConvert; + STAMCOUNTER StatR3DxOutputTargetReadback; STAMCOUNTER StatR3RegConfigDoneWr; STAMCOUNTER StatR3RegGmrDescriptorWr; diff --git a/src/VBox/Devices/Graphics/DevVGA-SVGA.cpp b/src/VBox/Devices/Graphics/DevVGA-SVGA.cpp index 28f4b800f42..8cccdd53656 100644 --- a/src/VBox/Devices/Graphics/DevVGA-SVGA.cpp +++ b/src/VBox/Devices/Graphics/DevVGA-SVGA.cpp @@ -8684,6 +8684,10 @@ int vmsvgaR3Init(PPDMDEVINS pDevIns) REG_PRF(&pSVGAState->StatR3Cmd3dSurfaceDmaProf, "VMSVGA/Cmd/3dSurfaceDmaProf", "Profiling of SVGA_3D_CMD_SURFACE_DMA."); # endif REG_PRF(&pSVGAState->StatR3Cmd3dBlitSurfaceToScreenProf, "VMSVGA/Cmd/3dBlitSurfaceToScreenProf", "Profiling of SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN."); + REG_PRF(&pSVGAState->StatR3DxStartScreenReadbackProf, "VMSVGA/DX/StartScreenReadbackProf", "Profiling of DX screen readback submission."); + REG_PRF(&pSVGAState->StatR3DxProcessPendingUpdatesProf, "VMSVGA/DX/ProcessPendingUpdatesProf", "Profiling of DX pending screen update processing."); + REG_PRF(&pSVGAState->StatR3DxOutputTargetConvertProf, "VMSVGA/DX/OutputTargetConvertProf", "Profiling of DX output target conversion."); + REG_PRF(&pSVGAState->StatR3DxOutputTargetReadbackProf, "VMSVGA/DX/OutputTargetReadbackProf", "Profiling of DX output target readback."); REG_CNT(&pSVGAState->StatR3Cmd3dActivateSurface, "VMSVGA/Cmd/3dActivateSurface", "SVGA_3D_CMD_ACTIVATE_SURFACE"); REG_CNT(&pSVGAState->StatR3Cmd3dBeginQuery, "VMSVGA/Cmd/3dBeginQuery", "SVGA_3D_CMD_BEGIN_QUERY"); REG_CNT(&pSVGAState->StatR3Cmd3dClear, "VMSVGA/Cmd/3dClear", "SVGA_3D_CMD_CLEAR"); @@ -8718,6 +8722,16 @@ int vmsvgaR3Init(PPDMDEVINS pDevIns) REG_CNT(&pSVGAState->StatR3Cmd3dSurfaceScreen, "VMSVGA/Cmd/3dSurfaceScreen", "SVGA_3D_CMD_SURFACE_SCREEN"); REG_CNT(&pSVGAState->StatR3Cmd3dSurfaceStretchBlt, "VMSVGA/Cmd/3dSurfaceStretchBlt", "SVGA_3D_CMD_SURFACE_STRETCHBLT"); REG_CNT(&pSVGAState->StatR3Cmd3dWaitForQuery, "VMSVGA/Cmd/3dWaitForQuery", "SVGA_3D_CMD_WAIT_FOR_QUERY"); + REG_CNT(&pSVGAState->StatR3DxStartScreenReadback, "VMSVGA/DX/StartScreenReadback", "DX screen readback submissions."); + REG_CNT(&pSVGAState->StatR3DxReadbackPoll, "VMSVGA/DX/ReadbackPoll", "DX screen readback query polls."); + REG_CNT(&pSVGAState->StatR3DxReadbackReady, "VMSVGA/DX/ReadbackReady", "DX screen readback query completions."); + REG_CNT(&pSVGAState->StatR3DxReadbackUpdateRects, "VMSVGA/DX/ReadbackUpdateRects", "DX screen update rectangles processed after readback."); + REG_CNT(&pSVGAState->StatR3DxReadbackUpdateMerge, "VMSVGA/DX/ReadbackUpdateMerge", "DX pending screen update rectangles merged before readback."); + REG_CNT(&pSVGAState->StatR3DxReadbackRestart, "VMSVGA/DX/ReadbackRestart", "DX readbacks restarted due to queued updates."); + REG_CNT(&pSVGAState->StatR3DxReadbackChanged, "VMSVGA/DX/ReadbackChanged", "DX output target readbacks that changed host-visible pixels."); + REG_CNT(&pSVGAState->StatR3DxReadbackUnchanged, "VMSVGA/DX/ReadbackUnchanged", "DX output target readbacks that did not change host-visible pixels."); + REG_CNT(&pSVGAState->StatR3DxOutputTargetConvert, "VMSVGA/DX/OutputTargetConvert", "DX output target conversions."); + REG_CNT(&pSVGAState->StatR3DxOutputTargetReadback, "VMSVGA/DX/OutputTargetReadback", "DX output target readbacks."); REG_CNT(&pSVGAState->StatR3CmdAnnotationCopy, "VMSVGA/Cmd/AnnotationCopy", "SVGA_CMD_ANNOTATION_COPY"); REG_CNT(&pSVGAState->StatR3CmdAnnotationFill, "VMSVGA/Cmd/AnnotationFill", "SVGA_CMD_ANNOTATION_FILL"); REG_CNT(&pSVGAState->StatR3CmdBlitGmrFbToScreen, "VMSVGA/Cmd/BlitGmrFbToScreen", "SVGA_CMD_BLIT_GMRFB_TO_SCREEN"); 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..524ef5b712c 100644 --- a/src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11.cpp +++ b/src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11.cpp @@ -49,6 +49,7 @@ #include "DevVGA-SVGA.h" #include "DevVGA-SVGA3d.h" #include "DevVGA-SVGA3d-internal.h" +#include "DevVGA-SVGA-internal.h" #include "DevVGA-SVGA3d-dx-shader.h" #include "DevVGA-SVGA3d-dx-dx11.h" @@ -3965,10 +3966,86 @@ DECLINLINE(int) dxFenceCmp64(uint64_t u64FenceA, uint64_t u64FenceB) } -static void dxStartScreenReadback(VMSVGASCREENOBJECT *pScreen, DXDEVICE *pDXDevice) +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(PVGASTATECC pThisCC, VMSVGASCREENOBJECT *pScreen, DXDEVICE *pDXDevice) { /* Get the screen data to the system memory. */ VMSVGAHWSCREEN *p = pScreen->pHwScreen; + PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State; + SVGASignedRect const dirtyRect = dxPendingScreenUpdateUnion(p); + + STAM_REL_COUNTER_INC(&pSvgaR3State->StatR3DxStartScreenReadback); + STAM_REL_PROFILE_START(&pSvgaR3State->StatR3DxStartScreenReadbackProf, a); /* Check targets. Targets are created and deleted on FIFO thread so it is ok to access them without a lock. */ VMSVGAOUTPUTTARGET *pOutputTarget; @@ -3977,8 +4054,11 @@ static void dxStartScreenReadback(VMSVGASCREENOBJECT *pScreen, DXDEVICE *pDXDevi VMSVGAHWOUTPUTTARGET *pHwOutputTarget = pOutputTarget->pHwOutputTarget; AssertContinue(pHwOutputTarget); + STAM_REL_COUNTER_INC(&pSvgaR3State->StatR3DxOutputTargetConvert); + STAM_REL_PROFILE_START(&pSvgaR3State->StatR3DxOutputTargetConvertProf, b); dxHwOutputTargetConvert(pOutputTarget, pDXDevice->pImmediateContext, - p->pScreenTextureSRV, p->cHwScreenWidth, p->cHwScreenHeight); + p->pScreenTextureSRV, p->cHwScreenWidth, p->cHwScreenHeight, dirtyRect); + STAM_REL_PROFILE_STOP(&pSvgaR3State->StatR3DxOutputTargetConvertProf, b); } /* Submit all the work: target conversion and readback to staging resources. */ @@ -3990,6 +4070,8 @@ static void dxStartScreenReadback(VMSVGASCREENOBJECT *pScreen, DXDEVICE *pDXDevi ++p->u64ReadbackFence; /* vmsvga3dBackProcessPendingTasks will update rectangles with lower u64ReadbackFences. */ if (p->u64ReadbackFence == 0) /* 0 is a special "no fence" value. */ ++p->u64ReadbackFence; + + STAM_REL_PROFILE_STOP(&pSvgaR3State->StatR3DxStartScreenReadbackProf, a); } @@ -3997,19 +4079,27 @@ static void dxProcessPendingUpdates(PVGASTATECC pThisCC, VMSVGASCREENOBJECT *pSc { PVMSVGA3DSTATE pState = pThisCC->svga.p3dState; AssertReturnVoid(pState); + PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State; DXDEVICE *pDXDevice = dxDeviceGet(pState); AssertReturnVoid(pDXDevice->pDevice); VMSVGAHWSCREEN *p = pScreen->pHwScreen; + STAM_REL_PROFILE_START(&pSvgaR3State->StatR3DxProcessPendingUpdatesProf, a); if (p->fReadingBack) { + STAM_REL_COUNTER_INC(&pSvgaR3State->StatR3DxReadbackPoll); BOOL queryData; HRESULT hr = pDXDevice->pImmediateContext->GetData(p->pScreenReadbackQuery, &queryData, sizeof(queryData), 0); - AssertReturnVoid(SUCCEEDED(hr)); + if (FAILED(hr)) + { + STAM_REL_PROFILE_STOP(&pSvgaR3State->StatR3DxProcessPendingUpdatesProf, a); + AssertFailedReturnVoid(); + } if (hr == S_OK) { + STAM_REL_COUNTER_INC(&pSvgaR3State->StatR3DxReadbackReady); /* Query has completed. Copy already updated rectangles. */ Assert(p->cUpdates); while (p->cUpdates) @@ -4020,13 +4110,27 @@ 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); + STAM_REL_COUNTER_INC(&pSvgaR3State->StatR3DxOutputTargetReadback); + STAM_REL_PROFILE_START(&pSvgaR3State->StatR3DxOutputTargetReadbackProf, b); + bool fOutputTargetChanged = false; + int rc = dxHwOutputTargetReadback(pOutputTarget, pDXDevice->pImmediateContext, + updateRect, &fOutputTargetChanged); + STAM_REL_PROFILE_STOP(&pSvgaR3State->StatR3DxOutputTargetReadbackProf, b); + AssertRC(rc); + if (!fOutputTargetChanged) + { + STAM_REL_COUNTER_INC(&pSvgaR3State->StatR3DxReadbackUnchanged); + continue; + } + STAM_REL_COUNTER_INC(&pSvgaR3State->StatR3DxReadbackChanged); + fAnyOutputTargetChanged = true; /* Increment u64UpdateSequenceNumber, skipping 0 on rollover. */ uint64_t u64 = pOutputTarget->u64UpdateSequenceNumber + 1; @@ -4037,9 +4141,11 @@ 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); + STAM_REL_COUNTER_INC(&pSvgaR3State->StatR3DxReadbackUpdateRects); + 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,11 +4153,38 @@ static void dxProcessPendingUpdates(PVGASTATECC pThisCC, VMSVGASCREENOBJECT *pSc } if (p->cUpdates > 0) - dxStartScreenReadback(pScreen, pDXDevice); /* There were screen updates since the last readback. */ + { + STAM_REL_COUNTER_INC(&pSvgaR3State->StatR3DxReadbackRestart); + dxStartScreenReadback(pThisCC, pScreen, pDXDevice); /* There were screen updates since the last readback. */ + } else p->fReadingBack = false; } } + STAM_REL_PROFILE_STOP(&pSvgaR3State->StatR3DxProcessPendingUpdatesProf, a); +} + + +static bool dxMergePendingScreenUpdate(PVGASTATECC pThisCC, VMSVGAHWSCREEN *p, SVGASignedRect const &updateRect) +{ + PVMSVGAR3STATE const pSvgaR3State = pThisCC->svga.pSvgaR3State; + + 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); + STAM_REL_COUNTER_INC(&pSvgaR3State->StatR3DxReadbackUpdateMerge); + return true; + } + + return false; } @@ -4059,6 +4192,9 @@ static void dxStoreScreenUpdate(PVGASTATECC pThisCC, VMSVGASCREENOBJECT *pScreen { VMSVGAHWSCREEN *p = pScreen->pHwScreen; + if (dxMergePendingScreenUpdate(pThisCC, p, updateRect)) + return; + if (p->cUpdates >= RT_ELEMENTS(p->aUpdates)) { /* Overflow, usually should not happen. Wait for previous updates to complete. */ @@ -4067,7 +4203,7 @@ static void dxStoreScreenUpdate(PVGASTATECC pThisCC, VMSVGASCREENOBJECT *pScreen DXDEVICE *pDXDevice = dxDeviceGet(pThisCC->svga.p3dState); AssertReturnVoid(pDXDevice->pDevice); - dxStartScreenReadback(pScreen, pDXDevice); + dxStartScreenReadback(pThisCC, pScreen, pDXDevice); p->fReadingBack = true; } @@ -4233,7 +4369,7 @@ static DECLCALLBACK(int) vmsvga3dBackSurfaceBlitToScreen(PVGASTATECC pThisCC, VM if (!p->fReadingBack) { /* Get the screen data to the system memory. */ - dxStartScreenReadback(pScreen, pDXDevice); + dxStartScreenReadback(pThisCC, pScreen, pDXDevice); p->fReadingBack = true; } @@ -4806,7 +4942,7 @@ static DECLCALLBACK(int) vmsvga3dScreenTargetUpdate(PVGASTATECC pThisCC, VMSVGAS if (!pHwScreen->fReadingBack) { /* Get the screen data to the system memory. */ - dxStartScreenReadback(pScreen, pDXDevice); + dxStartScreenReadback(pThisCC, pScreen, pDXDevice); pHwScreen->fReadingBack = true; } 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.