Reputation: 625
UPDATE: this issue turned out to be a silly typo (see answer), which was clearly identified by the DirectX debug layer after I was able to enable it. However, I'm leaving the question up as I think the SaveContentsToImage
code might be a useful reference for others wishing to export a multisampled texture.
I'm trying to save the contents of a multisampled texture (from the swapchain) to an image. I'm using Direct3D with feature level 11.0 via SharpDX. I have code (based on this answer and this answer) that works fine when the swapchain is initialized with the SampleDescription count set to 1 (no multisampling), but setting the count to 2, 4, etc. to enable MSAA results in a blank image.
My save-to-image function uses ResolveSubresource to resolve the multisampled texture from the swapchain into a non-multisampled intermediate texture, then uses CopyResource to copy the intermediate texture into a staging texture with CPU read access enabled. Here's the code:
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.");
}
Here's how the swapchain is configured:
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
is an integer constant set to 1, 2, 4, etc. If it's set to 1, SaveContentsToImage()
generates an image that matches what gets rendered to the screen; if it's a higher value, the resulting image is blank.
Upvotes: 0
Views: 794
Reputation: 625
I was eventually able to get the features required for use of the DirectX debug layer installed (in addition to the Graphics Tools optional feature, developer mode must also be installed and enabled).
Once I was able to use the debug layer, I set it to break on all message severity levels except information:
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);
There was an error in the call to CopyResource
in SaveContentsToImage
:
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]
At this point I felt rather silly. The CopyResource
call was _d3DDeviceContext.CopyResource(backBufferTexture, copyTexture)
when it should have been _d3DDeviceContext.CopyResource(intermediateTexture, copyTexture)
. Evidently I neglected to update this line after adding the intermediate texture to support multisampling.
Upvotes: 2