Мультисэмплированная текстура пуста при сохранении в изображение (DirectX)

ОБНОВЛЕНИЕ: эта проблема оказалась глупой опечаткой (см. ответ), которая была четко определена уровнем отладки DirectX после того, как я смог его включить. Тем не менее, я не поднимаю этот вопрос, так как думаю, что код SaveContentsToImage может быть полезным справочником для тех, кто хочет экспортировать мультисэмплированную текстуру.


Я пытаюсь сохранить содержимое мультисэмплированной текстуры (из цепочки обмена) в изображение. Я использую Direct3D с уровнем функций 11.0 через SharpDX. У меня есть код (на основе этого ответа и этот ответ), который отлично работает, когда цепочка обмена инициализируется с числом SampleDescription, установленным на 1 (без мультисэмплинга), но установка счетчика на 2, 4 и т. д., чтобы включить MSAA, приводит к пустому изображению.

Моя функция сохранения в образ использует ResolveSubresource для разрешения мультисэмплированной текстуры из цепочки обмена в промежуточную текстуру без мультисэмплинга, а затем использует CopyResource, чтобы скопировать промежуточную текстуру в промежуточную текстуру с включенным доступом для чтения ЦП. Вот код:

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() генерирует изображение, соответствующее тому, что отображается на экране; если это более высокое значение, результирующее изображение будет пустым.


person Reign of Error    schedule 10.01.2018    source источник
comment
Вы включили отладочное устройство и искали выходные сообщения?   -  person Chuck Walbourn    schedule 11.01.2018
comment
Предполагая, что вы имеете в виду уровень отладки, для этого требуется установка дополнительной функции графических инструментов Windows (см. здесь). Из-за политики безопасности моей компании это требует вмешательства ИТ. Я отправил заявку два дня назад и намерен включить уровень отладки, как только смогу это сделать.   -  person Reign of Error    schedule 11.01.2018
comment
Это действительно было бы первым шагом к выяснению причины здесь.   -  person Chuck Walbourn    schedule 11.01.2018
comment
Справедливо. Я обновлю вопрос, как только смогу включить уровень отладки и проверить его вывод.   -  person Reign of Error    schedule 11.01.2018


Ответы (1)


В конце концов я смог получить функции, необходимые для использования установленного уровня отладки 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);

Произошла ошибка при вызове CopyResource в SaveContentsToImage:

ОШИБКА D3D11: ID3D11DeviceContext::CopyResource: невозможно вызвать CopyResource с несоответствием между исходной множественной выборкой ресурсов (выборки: 4, качество: 0) и целевой множественной выборкой ресурсов (выборки: 1, качество: 0). [ОШИБКА RESOURCE_MANIPULATION № 286: COPYRESOURCE_INVALIDSOURCESTATE]

В этот момент я почувствовал себя довольно глупо. Звонок CopyResource был _d3DDeviceContext.CopyResource(backBufferTexture, copyTexture), хотя должен был быть _d3DDeviceContext.CopyResource(intermediateTexture, copyTexture). Очевидно, я забыл обновить эту строку после добавления промежуточной текстуры для поддержки мультисэмплинга.

person Reign of Error    schedule 18.01.2018