[SlimDX] Texture2D.ToStream() throws an Undetermined Error

Started by
6 comments, last by Nik02 9 years, 2 months ago

Using SlimDX and trying to get a memory stream from a texture. I get the error: E_FAIL: An undetermined error occurred (-2147467259). This should be simple, right?

Here is my device declaration:

d3dDevice11 = new SlimDX.Direct3D11.Device(DriverType.Hardware, DeviceCreationFlags.Debug);

And my Texture declaration:

Texture2DDescription textureDesc = new Texture2DDescription();
textureDesc.BindFlags = BindFlags.ShaderResource | BindFlags.RenderTarget;
textureDesc.Format = SlimDX.DXGI.Format.B8G8R8A8_UNorm;
textureDesc.Width = _inputWidth;
textureDesc.Height = _inputHeight;
textureDesc.MipLevels = 1;
textureDesc.ArraySize = 1;
textureDesc.SampleDescription = new SlimDX.DXGI.SampleDescription(1, 0);
textureDesc.Usage = ResourceUsage.Default;
textureDesc.OptionFlags = ResourceOptionFlags.Shared;
textureDesc.CpuAccessFlags = CpuAccessFlags.None;
textureDesc.ArraySize = 1;
Texture2D _texture = new Texture2D(d3dDevice11, textureDesc);

And the call to get the Stream:

MemoryStream newMemStream = new MemoryStream();
SlimDX.Result sdxRes = SlimDX.Direct3D11.Texture2D.ToFile(
_texture.Device.ImmediateContext,
_texture,
SlimDX.Direct3D11.ImageFileFormat.Bmp,
(Stream)newMemStream);

I have tried various combinations of things. Always the same. I tried ToFile with the same error. I have tried:

new DeviceContext(_texture.Device);

_texture.Device.ImmediateContext
new DeviceContext(_texture.Device)
new SlimDX.Direct3D11.DeviceContext(d3dDevice11)

Always the same result.

Any help would be very much appreciated!

Thanks,

Doug

Advertisement

The texture is not in cpu-accessible memory, so the ToFile method cannot get the data to do its thing.

You need to create a staging texture (which is CPU-accessible), copy your current texture contents to it, and save the said staging texture. Staging resources are special in that both the GPU and CPU can access them, but you can't use them directly for rendering.

Niko Suni

Niko,
Thank you. That was the problem. I created a second texture, marked it for staging and then do the ToStream(0. All appears to be working. However, based on a stopwatch it takes about 700mS to do the CopyResource and then the ToStream which is a memory stream. I realize at this point I am crossing the boundary into CPU-land. But should it really take that long for a 1920 x 1080 frame? Seems really slow. I have a K5000 card from Nvidia which the memory bus speed is supposed to be pretty good.

Do you, or anyone else, have any thoughts on this?

Thanks,
Doug
The ToStream method itself may be the bottleneck. Try to map and copy the texture data manually to your own array; if it is faster, you can verify this.
Note that downloading from the gpu is generally slower than uploading, since a greater amount of bus bandwidth is usually allocated for upload traffic.

Niko Suni

Thank you, Niko. You are right, I put some stopwatches in and the ToStream is very much the problem. Everything else is in the sub 50nS range with the actual acquisition from the camera card about 1.7uS.

I am completely new to all the DirectX/SlimDX stuff. What I know is what I have learned in the last couple of months and that is limited!

The goal here is to get this data into a WPF application. To use a D3DImage then a D3D9 surface works very well and is very fast and renders very quickly. However the code base I am working from was such a mess that I am, essentially, starting over. Right now I just need the raw pixel data in a CPU buffer. I think a Byte[] is probably best, though an int[] would work as well as I am dealing with 32 bit RGBA pixels.

I really have no idea how to map and copy Texture data to my own array. If you can give me a hint I would appreciate it. Otherwise, I will head for Google and see what turns up.

Thanks,

Doug

I mainly work on the native D3D, but I believe it has equivalent functionality to map a subresource. "Mapping" in this context refers to requesting a pointer to resource data, and effectively denying other parties' access to it until you subsequently unmap it. In native d3d, mapping gives you the data pointer directly.

Niko Suni

Niko, Once again, spot on! Exactly what I did and that worked nicely. I am putting the relevant pieces here in cany other noob gets stuck as I was. Texture2DDescription stagingDesc = new Texture2DDescription() { ArraySize = 1, Width = _inputWidth, Height = _inputHeight, BindFlags = BindFlags.None, CpuAccessFlags = CpuAccessFlags.Read, Format = SlimDX.DXGI.Format.R8G8B8A8_UNorm, OptionFlags = ResourceOptionFlags.None, Usage = ResourceUsage.Staging, MipLevels = 1, SampleDescription = new SampleDescription(1, 0) }; _stagingTexture = new Texture2D(d3dDevice11, stagingDesc); After the frame is read ..... DataBox box = d3dDevice11.ImmediateContext.MapSubresource(_stagingTexture, 0, MapMode.Read, SlimDX.Direct3D11.MapFlags.None); pixelData = box.Data.ReadRange((int)box.Data.Length); d3dDevice11.ImmediateContext.UnmapSubresource(_stagingTexture, 0); Way faster than ToStream()!! Thanks again! Doug

No prob, glad I could help smile.png

The ToStream method is likely slow for two reasons:

  • When you write primitive data types to a stream, each write invokes other functionality than a simple memory write. This is as opposed to using your own array, which is essentially just a linear block of memory with fast access. The latter is more unsafe, but very much faster approach. And it is not actually unsafe if you know what you're doing (do sanity checking on memory boundaries at high level etc.)
  • The image codec itself is slow to instantiate/invoke. Apart from switching to a faster image i/o library, there's little you can do about the issue itself. However, you could use a separate thread if you need to save and image while keeping rendering alive - just map/copy the raw pixels in the main drawing thread, and do the actual file i/o in the secondary thread.

Niko Suni

This topic is closed to new replies.

Advertisement