在 RenderLoop.Run 之前设置 SharpDX.Windows.RenderForm.IsFullscreen 会损坏 window

Setting SharpDX.Windows.RenderForm.IsFullscreen before RenderLoop.Run corrupts window

在实现全屏和分辨率切换代码时,我在 SharpDX 中观察到一个奇怪的错误行为。每当我在调用 RenderLoop.Run() 之前将 RenderForm.IsFullscreen 设置为 true 并且稍后将 return 设置为 windowed 模式时,window 已损坏:

请注意它是如何缺少一个图标的,而我的桌面背景和控制台却闪闪发光。另外,我不能点击它,任何点击都会被重定向到底层 windows。即使 RenderLoop 仍在 运行 将背景颜色清除为深蓝色,也只会填充灰色且太小的区域。 (作为用户,我可以通过任务栏按钮最大化损坏的 window 来解决这个问题。简单地调整大小没有帮助。)

这是我的完整测试代码:

using System;
using System.Drawing;
using System.Windows.Forms;
using SharpDX;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using SharpDX.Windows;

internal class Program
{
    private static RenderForm                _window;
    private static SharpDX.Direct3D11.Device _device;
    private static DeviceContext             _deviceContext;
    private static SwapChain                 _swapChain;
    private static RenderTargetView          _renderTargetView;

    private static bool Fullscreen
    {
        get
        {
            return _window.IsFullscreen;
        }
        set
        {
            // Do not resize multiple times by preventing the resized event.
            _window.UserResized -= _window_UserResized;

            _window.IsFullscreen = value;

            _swapChain.SetFullscreenState(_window.IsFullscreen, null);
            // Resize the window when returning to windowed mode to fit the most recent resolution.
            if (!_window.IsFullscreen)
            {
                _window.ClientSize = _resolution;
            }

            // Allow new resize events.
            _window.UserResized += _window_UserResized;
        }
    }

    private static Size _resolution;
    private static Size Resolution
    {
        get
        {
            return _resolution;
        }
        set
        {
            _resolution = value;
            Debug.WriteLine("Setting resolution: " + _resolution.ToString());

            // Do not resize multiple times by preventing the resized event.
            _window.UserResized -= _window_UserResized;

            // Resize the window in windowed mode.
            if (!_window.IsFullscreen)
            {
                _window.ClientSize = _resolution;
            }

            // Dispose existing objects.
            Utilities.Dispose(ref _renderTargetView);

            // Resize the back buffer.
            _swapChain.ResizeBuffers(0, _resolution.Width, _resolution.Height, Format.R8G8B8A8_UNorm, SwapChainFlags.AllowModeSwitch);

            // Create the new and resized render target and set it.
            using (Texture2D backBuffer = _swapChain.GetBackBuffer<Texture2D>(0))
            {
                _renderTargetView = new RenderTargetView(_device, backBuffer);
            }
            _deviceContext.OutputMerger.SetRenderTargets(_renderTargetView);

            // Resize the swap chain buffers to set the fullscreen resolution.
            ModeDescription bufferDescription = new ModeDescription()
            {
                Width = _resolution.Width,
                Height = _resolution.Height,
                RefreshRate = new Rational(60, 1),
                Format = Format.R8G8B8A8_UNorm
            };
            _swapChain.ResizeTarget(ref bufferDescription);

            // Allow new resize events.
            _window.UserResized += _window_UserResized;
        }
    }

    private static void Main(string[] args)
    {
        _window = new RenderForm();
        _window.KeyDown += _window_KeyDown;
        _window.IsFullscreen = true;

        // Set default resolution.
        _resolution = new Size(800, 600);

        // Describe the swap chain buffer mode.
        ModeDescription bufferDescription = new ModeDescription()
        {
            Width = _resolution.Width,
            Height = _resolution.Height,
            RefreshRate = new Rational(60, 1),
            Format = Format.R8G8B8A8_UNorm
        };
        // Describe the swap chain.
        SwapChainDescription swapChainDescription = new SwapChainDescription()
        {
            ModeDescription = bufferDescription,
            SampleDescription = new SampleDescription(1, 0),
            Usage = Usage.RenderTargetOutput,
            BufferCount = 1,
            OutputHandle = _window.Handle,
            IsWindowed = !_window.IsFullscreen,
            Flags = SwapChainFlags.AllowModeSwitch // Allows other fullscreen resolutions than native one.
        };
        // Create the device with the swap chain.
        SharpDX.Direct3D11.Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.None, swapChainDescription, out _device, out _swapChain);
        _deviceContext = _device.ImmediateContext;

        // Set the resolution to run the code which resizes the internal buffers.
        Resolution = _resolution;

        _window.UserResized += _window_UserResized;
        RenderLoop.Run(_window, Loop);
    }

    private static void Loop()
    {
        _deviceContext.ClearRenderTargetView(_renderTargetView, new Color4(0.4f, 0.5f, 0.6f, 1f));
        _swapChain.Present(1, 0);
    }

    private static void _window_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.F)
        {
            Fullscreen = !Fullscreen;
        }
    }

    private static void _window_UserResized(object sender, EventArgs e)
    {
        Resolution = _window.ClientSize;
    }
}

我可以重写代码以记住全屏设置为布尔值而不是直接使用 _window.IsFullscreen,然后检查我是否在渲染循环的第一次迭代中并在那里设置全屏状态检查布尔值,但这很尴尬。

当我这样做时,window 会在桌面上进行动画处理,就好像它是新创建的一样。看起来 SharpDX 在这里弄乱了 window 样式。

这是 SharpDX 的错误还是我做错了什么?我没有找到在调用 RenderLoop.Run().

之前不应将 IsFullscreen 设置为 true 的信息

正在分析 window 样式集,损坏的 window 丢失了 WS_VISIBLE。

所以,我只是忘记了 Show() 渲染表单。事实证明我什至在分析任何 D3D 东西之前都必须这样做,否则 window 会再次损坏。

这是固定的 Main() 方法:

private static void Main(string[] args)
{
    _window = new RenderForm();
    _window.KeyDown += _window_KeyDown;
    _window.IsFullscreen = true;
    _window.Show(); // Do not forget this or window styles may break

    // Set default resolution.
    _resolution = new Size(800, 600);

    // Describe the swap chain buffer mode.
    ModeDescription bufferDescription = new ModeDescription()
    {
        Width = _resolution.Width,
        Height = _resolution.Height,
        RefreshRate = new Rational(60, 1),
        Format = Format.R8G8B8A8_UNorm
    };
    // Describe the swap chain.
    SwapChainDescription swapChainDescription = new SwapChainDescription()
    {
        ModeDescription = bufferDescription,
        SampleDescription = new SampleDescription(1, 0),
        Usage = Usage.RenderTargetOutput,
        BufferCount = 1,
        OutputHandle = _window.Handle,
        IsWindowed = !_fullscreen,
        Flags = SwapChainFlags.AllowModeSwitch // Allows other fullscreen resolutions than native one.
    };
    // Create the device with the swap chain.
    SharpDX.Direct3D11.Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.None,
        swapChainDescription, out _device, out _swapChain);
    _deviceContext = _device.ImmediateContext;

    // Set the resolution to run the code which resizes the internal buffers.
    Resolution = _resolution;

    RenderLoop.Run(_window, Loop);
}