Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 54 additions & 15 deletions src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11-output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <VBox/log.h>

#include <iprt/mem.h>
#include <iprt/string.h>

#ifdef RT_OS_WINDOWS
# include <iprt/win/windows.h>
Expand All @@ -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. */
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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
{
Expand All @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
115 changes: 110 additions & 5 deletions src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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. */
Expand Down Expand Up @@ -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;
Expand All @@ -4037,28 +4116,54 @@ 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);
--p->cUpdates;
}

if (p->cUpdates > 0)
{
dxStartScreenReadback(pScreen, pDXDevice); /* There were screen updates since the last readback. */
}
else
p->fReadingBack = false;
}
}
}


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. */
Expand Down
6 changes: 4 additions & 2 deletions src/VBox/Devices/Graphics/DevVGA-SVGA3d-dx-dx11.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down