保存到图像时,多重采样纹理为空白 (DirectX)

Multisampled texture is blank when saved to image (DirectX)

更新: 这个问题原来是一个愚蠢的拼写错误(见答案),在我能够启用它之后,它被 DirectX 调试层清楚地识别出来。但是,我把这个问题留了下来,因为我认为 SaveContentsToImage 代码对于其他希望导出多重采样纹理的人来说可能是一个有用的参考。


我正在尝试将多重采样纹理(来自交换链)的内容保存到图像中。我通过 SharpDX. I have code (based on this answer and ) 使用功能级别为 11.0 的 Direct3D,在将 SampleDescription 计数设置为 1(无多重采样)初始化交换链时工作正常,但将计数设置为 2、4 等以启用 MSAA生成空白图像。

我的保存到图像功能使用 ResolveSubresource to resolve the multisampled texture from the swapchain into a non-multisampled intermediate texture, then uses CopyResource 将中间纹理复制到启用了 CPU 读取访问权限的暂存纹理中。这是代码:

private void SaveContentsToImage()
{
    // TODO breaks when using MSAA.

    D3D11.Texture2D backBufferTexture = _swapChain.GetBackBuffer<D3D11.Texture2D>(0);

    // Intermediate texture used to resolve source using MSAA (unnecessary if source sample count is 1).
    D3D11.Texture2DDescription intermediateTextureDesc = backBufferTexture.Description;
    intermediateTextureDesc.SampleDescription = new SampleDescription(1, 0);
    intermediateTextureDesc.Usage = D3D11.ResourceUsage.Default;

    D3D11.Texture2D intermediateTexture = new D3D11.Texture2D(_d3dDevice, intermediateTextureDesc);

    _d3DDeviceContext.ResolveSubresource(backBufferTexture, 0, intermediateTexture, 0, backBufferTexture.Description.Format);
    //_d3DDeviceContext.ResolveSubresource(_renderTargetView.Resource, 0, intermediateTexture, 0, backBufferTexture.Description.Format); // Works identically to above.

    D3D11.Texture2DDescription copyDesc = backBufferTexture.Description;
    copyDesc.SampleDescription = new SampleDescription(1, 0);
    copyDesc.Usage = D3D11.ResourceUsage.Staging;
    copyDesc.BindFlags = D3D11.BindFlags.None;
    copyDesc.CpuAccessFlags = D3D11.CpuAccessFlags.Read;

    D3D11.Texture2D copyTexture = new D3D11.Texture2D(_d3dDevice, copyDesc);

    _d3DDeviceContext.CopyResource(backBufferTexture, copyTexture);

    DataStream dataStream;
    var dataBox = _d3DDeviceContext.MapSubresource(copyTexture, 0, 0, D3D11.MapMode.Read, D3D11.MapFlags.None, out dataStream);

    DataRectangle dataRectangle = new DataRectangle
    {
        DataPointer = dataStream.DataPointer,
        Pitch = dataBox.RowPitch
    };

    Bitmap wicBitmap = new Bitmap(_wicFactory, copyTexture.Description.Width, copyTexture.Description.Height, PixelFormat.Format32bppBGRA, dataRectangle);

    byte[] pixelData = new byte[copyTexture.Description.Width * copyTexture.Description.Height * 4];
    wicBitmap.CopyPixels(pixelData, copyTexture.Description.Width * 4);
    System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(copyTexture.Description.Width, copyTexture.Description.Height);
    BitmapData bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, copyTexture.Description.Width, copyTexture.Description.Height), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
    Marshal.Copy(pixelData, 0, bitmapData.Scan0, pixelData.Length);
    bitmap.UnlockBits(bitmapData);

    bitmap.Save("test.png");

    Console.WriteLine("Saved image.");
}

交换链的​​配置方式如下:

ModeDescription backBufferDescription = new ModeDescription(RenderAreaWidth, RenderAreaHeight, new Rational(60, 1), Format.B8G8R8A8_UNorm);

SwapChainDescription swapChainDescription = new SwapChainDescription()
{
    ModeDescription = backBufferDescription,
    SampleDescription = new SampleDescription(SampleCount, 0),
    Usage = Usage.RenderTargetOutput,
    BufferCount = 1,
    OutputHandle = _renderForm.Handle,
    IsWindowed = true,
};

SampleCount 是一个整数常量,设置为 1、2、4 等。如果设置为 1,则 SaveContentsToImage() 生成与呈现到屏幕的图像相匹配的图像;如果该值较高,则生成的图像为空白。

我最终能够安装使用 DirectX 调试层所需的功能(除了图形工具可选功能外,还必须安装并启用开发人员模式)。

一旦我能够使用调试层,我就将其设置为在除信息之外的所有消息严重性级别上中断:

D3D11.InfoQueue infoQueue = _d3dDevice.QueryInterface<D3D11.InfoQueue>();

infoQueue.SetBreakOnSeverity(D3D11.MessageSeverity.Corruption, true);
infoQueue.SetBreakOnSeverity(D3D11.MessageSeverity.Error, true);
infoQueue.SetBreakOnSeverity(D3D11.MessageSeverity.Message, true);
infoQueue.SetBreakOnSeverity(D3D11.MessageSeverity.Warning, true);

SaveContentsToImage 中调用 CopyResource 时出错:

D3D11 ERROR: ID3D11DeviceContext::CopyResource: Cannot invoke CopyResource with a mismatch between the source Resource Multisampling (Samples:4, Quality:0) and the destination Resource Multisampling (Samples:1, Quality:0). [ RESOURCE_MANIPULATION ERROR #286: COPYRESOURCE_INVALIDSOURCESTATE]

此时我觉得自己很傻。 CopyResource 调用是 _d3DDeviceContext.CopyResource(backBufferTexture, copyTexture),而它应该是 _d3DDeviceContext.CopyResource(intermediateTexture, copyTexture)。显然我在添加中间纹理以支持多重采样后忽略了更新这一行。