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