hecomi / uDesktopDuplication

Desktop Duplication API implementation for Unity (only for Windows 8/10)
http://tips.hecomi.com/entry/2016/12/04/125641
MIT License
563 stars 97 forks source link

Use dirty rectangles for improving GPU performance. #13

Closed hecomi closed 5 years ago

hecomi commented 7 years ago

Now uDD uses CopyResource() to copy the DDA texture to Unity one. This may be improved by updating only dirty rects area using CopySubresourceRegion(). I've attempted to implement this with the below code:

void Monitor::Render()
{
    const auto& frame = duplicator_->GetLastFrame();
    const auto& texture = frame.texture;
    const auto metaData = frame.metaData; // copy

    if (!texture) return;

    // Get texture
    if (unityTexture_)
    {
        D3D11_TEXTURE2D_DESC srcDesc, dstDesc;
        texture->GetDesc(&srcDesc);
        unityTexture_->GetDesc(&dstDesc);
        if (srcDesc.Width  != dstDesc.Width ||
            srcDesc.Height != dstDesc.Height)
        {
            Debug::Error("Monitor::Render() => Texture sizes are defferent.");
            Debug::Error("    Source : (", srcDesc.Width, ", ", srcDesc.Height, ")");
            Debug::Error("    Dest   : (", dstDesc.Width, ", ", dstDesc.Height, ")");
            return;
        }
        else
        {
            ComPtr<ID3D11DeviceContext> context;
            GetDevice()->GetImmediateContext(&context);

            if (!hasBeenUpdated_)
            {
                context->CopyResource(unityTexture_, texture.Get());
            }
            else
            {
                for (UINT i = 0; i < metaData.dirtyRectSize; ++i)
                {
                    const UINT offset = metaData.moveRectSize + sizeof(RECT) * i;
                    const RECT& dirtyRect = *metaData.buffer.As<RECT>(offset);
                    D3D11_BOX box = 
                    {
                        static_cast<UINT>(dirtyRect.left),
                        static_cast<UINT>(dirtyRect.top),
                        0,
                        static_cast<UINT>(dirtyRect.right),
                        static_cast<UINT>(dirtyRect.bottom),
                        1,
                    };
                    context->CopySubresourceRegion(
                        unityTexture_,
                        0,
                        static_cast<UINT>(dirtyRect.left),
                        static_cast<UINT>(dirtyRect.top),
                        0,
                        texture.Get(),
                        0,
                        &box);
                }
            }
        }
    }

    if (UseGetPixels())
    {
        CopyTextureFromGpuToCpu(unityTexture_);
    }

    hasBeenUpdated_ = true;
}

But this output flickering image and didn't update cursor area because the cursor was drawn without updating dirty rects. I understand the way to fix the cursor area (to keep the previous cursor area and include it and the current area into dirty rect) but I don't know the reason why I got a flickering image. More investigation will be needed.

hecomi commented 5 years ago

This has lots of problems, so close it for now.