ValveSoftware / openvr

OpenVR SDK
http://steamvr.com
BSD 3-Clause "New" or "Revised" License
6.07k stars 1.28k forks source link

Overlay texture flickering (C#, SharpDX, DX11) #1353

Open BeamRider opened 4 years ago

BeamRider commented 4 years ago

I'm experiencing flickering/disappearing overlay texture when using SetOverlayTexture with a Texture2D prepared using SharpDx.

I have two threads, one is generating a System.Drawing.Bitmap (a GDI+ bitmap) asynchronously to the the thread handling the overlay. Obviously access to this bitmap is synchronized between the two with a simple locking mechanism.

The overlay thread is doing the following (simplfied code):

while (!leave)
{
    OpenVR.Compositor.WaitGetPoses(renderPoses, gamePoses);
    /* evaluate HMD pose here */
    Present();
    Thread.Sleep(20); // let the cpu to rest a little 
}

The present method is:

protected void Present()
{
    lock (_bmpLockObj)
    {
        if (_textureUpdated)
        {
            _textureUpdated = false;
            try
            {
                PrepareTexture();
                _ovrTexture = new Texture_t { eColorSpace = EColorSpace.Auto, eType = ETextureType.DirectX, handle = (IntPtr)_texture };
                EVROverlayError ovlError = OpenVR.Overlay.SetOverlayTexture(_overlayHandle, ref _ovrTexture);
                if (ovlError != EVROverlayError.None)
                {
                    Logger.Error($"Unable to Set Overlay Texture: {ovlError.ToString()}");
                 }
            } catch (Exception ex) {
                Logger.Error(ex);
            }
        }
    }
}

And the PrepareTexture:

protected void PrepareTexture()
{
    lock (_bmpLockObj)
    {
        Rectangle bounds = new Rectangle(0, 0, _bitmap.Width, _bitmap.Height);
         BitmapData textData = _bitmap.LockBits(bounds, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

        if (_texture != null)
            _texture.Dispose();

        _texture = new D3D11.Texture2D(D3DDevice, new D3D11.Texture2DDescription()
        {
            Width = _bitmap.Width,
            Height = _bitmap.Height,
            ArraySize = 1,
            BindFlags = D3D11.BindFlags.ShaderResource,
            Usage = D3D11.ResourceUsage.Immutable,
            CpuAccessFlags = D3D11.CpuAccessFlags.None,
            Format = DXGI.Format.B8G8R8A8_UNorm,
            MipLevels = 1,
            OptionFlags = D3D11.ResourceOptionFlags.None,
            SampleDescription = new DXGI.SampleDescription(1, 0),
        }, new SharpDX.DataRectangle(textData.Scan0, textData.Stride));

        _bitmap.UnlockBits(textData);
        _bitmap.Dispose();
        _bitmap = null;
    }
}

I'm not sure if the Texture is properly configured for the purpose. Doing a very similar thing with OpenGL worked without any problem.

The overlay will flicker when the update rate of the bitmap is high (a new bitmap is generated every time), but when the update is low, sometimes, the overlay just disappears.

This issue appear to have a similar behavior of #741, but there is no commonality of the listed causes.

Zaetc commented 4 years ago

I think your texture is properly configured. I do everything almost the same way, and I have no such problem, differences:

BeamRider commented 4 years ago

The deviced3d.ImmediateContext.Flush() solved the problem to a good extent. I tried it but always before the SetTexture, putting it below did the magic.

Some users are reporting single-shot flashes here and there, but seems not to be a serious problem. This is making me think that synchronization is missing somewhere. A suggestion for the documentation: a proper howto or an example of an high refresh rate DX overlay may be very useful.

@Zaetc many thanks for your help