Display Images fast with SlimDX .

Started by
6 comments, last by ZeroWalker 10 years, 8 months ago

Okay i am trying to replace the use of Panels and Pictureboxes in my application, as they aren´t suitable for drawing many pictures fast.

Now i am looking at SlimDX, and have gotten it to work sligthly, but, it´s much slower than drawing in a Panel, and i am a bit confused to what i can change.

I also have a problem where the texture size isn´t correct, it appears much bigger and distorted than it´s supposed to, and this kills performance.

Here is the code:

First to initialize everything:


var form = new SlimDX.Windows.RenderForm("Test");
form.Width = 1280;
form.Height = 1024;
SlimDX.Direct3D9.PresentParameters presentParams = new SlimDX.Direct3D9.PresentParameters
{
BackBufferWidth = form.Width,
BackBufferHeight = form.Height,
DeviceWindowHandle = form.Handle,
PresentFlags = SlimDX.Direct3D9.PresentFlags.None,
Multisample = SlimDX.Direct3D9.MultisampleType.None,
BackBufferCount = 0,
PresentationInterval = SlimDX.Direct3D9.PresentInterval.Immediate,
SwapEffect = SlimDX.Direct3D9.SwapEffect.Flip,
BackBufferFormat = SlimDX.Direct3D9.Format.X8R8G8B8,
Windowed = true,
};

var device = new SlimDX.Direct3D9.Device(new SlimDX.Direct3D9.Direct3D(), 0, SlimDX.Direct3D9.DeviceType.Hardware, form.Handle, SlimDX.Direct3D9.CreateFlags.HardwareVertexProcessing, presentParams);
device.Viewport = new SlimDX.Direct3D9.Viewport(0, 0, form.Width, form.Height);
SlimDX.Direct3D9.Sprite sprite;
sprite = new SlimDX.Direct3D9.Sprite(device);


Any thoughts on that, please tell.

Then the actual drawing.


SlimDX.Windows.MessagePump.Run(form, () =>
{

sprite.Begin(SlimDX.Direct3D9.SpriteFlags.None);
device.BeginScene();

var tx = SlimDX.Direct3D9.Texture.FromStream(device, ms, 0, 0, 0, SlimDX.Direct3D9.Usage.None, SlimDX.Direct3D9.Format.X8R8G8B8, SlimDX.Direct3D9.Pool.Managed, SlimDX.Direct3D9.Filter.None, SlimDX.Direct3D9.Filter.None, 0);

sprite.Draw(tx, Color.Transparent);
sprite.End();
tx.Dispose();
device.EndScene();
device.Present();

});

Now there is a bit more going on, but that´s just getting the network stream etc and hasn´t anything to do with the actual drawing performance.


Now, is there anything wrong with this?
Can it be improve (hope it can)?


The only thing i am trying to do, is to draw a new picture every loop (i send a new image, and i want to display it).

So the thing that must be improved is how fast it can draw it. Perhaps the conversions is slow, i don´t know, but form my tests it´s the Texture.FromStream that is slow, the actual displaying is fast.

Thanks

Advertisement

No answer;S?

Did i explain bad, or miss something?

If so please tell, as i am at a loss currently at how SlimDX works and is in need of assistance.

Thanks


var tx = SlimDX.Direct3D9.Texture.FromStream(device, ms, 0, 0, 0, SlimDX.Direct3D9.Usage.None, SlimDX.Direct3D9.Format.X8R8G8B8, SlimDX.Direct3D9.Pool.Managed, SlimDX.Direct3D9.Filter.None, SlimDX.Direct3D9.Filter.None, 0);

That line is the problem. You're calling that -every- frame and it's not a fast process to decode an image from a stream. Aside from the decoding process, you're also creating and destroying a GPU resource every frame and that's just a big no-no for performance. It's better to create a single resource for the lifetime of the application and update its contents.

If you need to stream in textures there may be better ways to accomplish your goals. For example, you could use a dynamic texture and updating the contents by locking, decoding/writing the texture data and unlocking. It might net you some performance improvement. You could also detect when a new image appears in the stream and only update when you get that new image. You could also read the image stream into a memory buffer on another thread and upload that buffer to the texture after it's finished filling. There are many ways to achieve this, but creating a new texture, decoding an image into it, and then destroying it after display is not a good way to do it.

Okay great, well hmm.

As you say, i myself see that it is by no means optimal.

Though the procedure is like this.


I take a screen shot and send it.

I receive it and put it in a MemoryStream.

I decode the memorystream (the texture).


So, when the texture reads the memorystream, it´s already a new image.
(there is a new image every loop).

So there is no need for making something like filling it and telling it when it´s done and there is a new image, as there is Always a need image as it reads from a TCP Stream, and it reads before making the texture. So if there is no new Image yet, it will just wait on the Stream (and the last picture will be showed until it´s refreshed).


But then, how can i tweak the texture?
Cause i use it just as an image to display it like a movie of some sort.
So basically, i don´t care if it´s a image, texture or whatever, i just need to display it.


But the Dynamic Texture and updating the contents sounds interesting if it can be done with how i handle stuff.

Here is the code with the NetworkStream and all that:


SlimDX.Windows.MessagePump.Run(form, () =>
{
if (checkBox1.Checked && tt1.Connected)
{

tcpstream.Read(lenArray, 0, 4);
Int32 length = BitConverter.ToInt32(lenArray, 0);
var tempBytes = new byte[length];
using (ms = new MemoryStream(tempBytes))
{
int currentPosition = 0;
while (currentPosition < length)
{
currentPosition += tcpstream.Read(tempBytes, currentPosition, length - currentPosition);
}

sprite.Begin(SlimDX.Direct3D9.SpriteFlags.None);
device.BeginScene();
using (var tx = SlimDX.Direct3D9.Texture.FromStream(device, ms, NonPower, NonPower, 1, SlimDX.Direct3D9.Usage.None, SlimDX.Direct3D9.Format.Unknown, SlimDX.Direct3D9.Pool.Managed, SlimDX.Direct3D9.Filter.None, SlimDX.Direct3D9.Filter.None, 0))
sprite.Draw(tx, Color.Transparent);
sprite.End();
device.EndScene();
device.Present();
}
}
});

Looking at your code, I'd move the TCP read and filling of the memory stream to another thread. That way it can handle that part in the background and free up some CPU time.

Again, as for the decoding from the memory stream using FromStream, I'd just create a dynamic texture (outside of the loop) and just continuously update it. This is where putting your stuff into another thread could help. You could decode the image data in the other thread and copy the data into a format that's friendly to the texture, and then when it's done filling the memory stream (and you're back on your main thread) you lock the dynamic texture, and just dump the contents directly into the texture and unlock.

You can still preserve the behaviour you have now. That is, no image, then you show nothing (empty texture), first image arrives, it gets displayed, subsequent image arrives, and it gets displayed, nothing else arrives so just use the last image. You need only update the texture after the memory stream has been filled with data.

Again, that's how I'd consider handling it, and it may not be the best way, but I do know that creating the resource and destroying it every frame is just bad for performance and I don't think there's anything you can do to improve the performance with the code as it is.

Ah okay, not sure exactly how to do that, but will try to move the TCP read and filling to another thread.

But, i don´t know how to make a dynamic texture, can you show me some example of it?

EDIT:

Okay have move them to 2 separate threads.
Though, i don´t really know how to make them cooperate.

I rad from the TCP stream and write to a MemoryStream in one thread.

Then the other is supposed to read from that MemoryStream, and well.
It "works", but it´s very random, as they try to read/write at the same time.
I don´t know how to make it read when the other thread is writing if you get what i mean.

Okay have made some changes, and well, the problem is still, how to read the MemoryStream from another thread.
Though from my understanding, you say i should fill the texture, then just update it.
Sadly, i don´t understand how to make a texture that can be update while in loop.

But here is my changes, they are pretty much like before, except it reads the memorystream from another thread (a hacky way though).

Here is the Receiving part:


if (checkBox1.Checked && tt1.Connected)
{
try
{
while (checkBox1.Checked && tt1.Connected)
{
Stoptimer = Stopwatch.StartNew();
Stoptimer.Start();


tcpstream.Read(lenArray, 0, 4);
Int32 length = BitConverter.ToInt32(lenArray, 0);
var tempBytes = new byte[length];
using (texturestream = new MemoryStream(tempBytes))
{
int currentPosition = 0;
texturestream.Position = 0;
while (currentPosition < length && checkBox1.Checked && texturestream.CanWrite)
{
currentPosition += tcpstream.Read(tempBytes, currentPosition, length - currentPosition);
}
Stoptimer.Stop();
System.Console.WriteLine(Stoptimer.ElapsedMilliseconds.ToString());
read = true;
Thread.Sleep(4);
read = false;
}
}
}

And here is the Render part:


SlimDX.Windows.MessagePump.Run(form, () =>
{
//Stoptimer = Stopwatch.StartNew();
//Stoptimer.Start();
sprite.Begin(SlimDX.Direct3D9.SpriteFlags.None);
device.BeginScene();
try
{
if (texturestream.CanRead && read == true)
{
texturestream.Position = 0;
using (var tx = SlimDX.Direct3D9.Texture.FromStream(device, texturestream, NonPower, NonPower, 1, SlimDX.Direct3D9.Usage.None, SlimDX.Direct3D9.Format.Unknown, SlimDX.Direct3D9.Pool.Managed, SlimDX.Direct3D9.Filter.None, SlimDX.Direct3D9.Filter.None, 0))
sprite.Draw(tx, Color.Transparent);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message + " Render");
}
sprite.End();
device.EndScene();
device.Present();

Stoptimer.Stop();
//System.Console.WriteLine(Stoptimer.ElapsedMilliseconds.ToString());
});
}
SlimDX.Windows.MessagePump.Run(form, () =>
{
//Stoptimer = Stopwatch.StartNew();
//Stoptimer.Start();
sprite.Begin(SlimDX.Direct3D9.SpriteFlags.None);
device.BeginScene();
try
{
if (texturestream.CanRead && read == true)
{
texturestream.Position = 0;
using (var tx = SlimDX.Direct3D9.Texture.FromStream(device, texturestream, NonPower, NonPower, 1, SlimDX.Direct3D9.Usage.None, SlimDX.Direct3D9.Format.Unknown, SlimDX.Direct3D9.Pool.Managed, SlimDX.Direct3D9.Filter.None, SlimDX.Direct3D9.Filter.None, 0))
sprite.Draw(tx, Color.Transparent);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message + " Render");
}
sprite.End();
device.EndScene();
device.Present();

Stoptimer.Stop();
//System.Console.WriteLine(Stoptimer.ElapsedMilliseconds.ToString());
});
}SlimDX.Windows.MessagePump.Run(form, () =>
{
//Stoptimer = Stopwatch.StartNew();
//Stoptimer.Start();
sprite.Begin(SlimDX.Direct3D9.SpriteFlags.None);
device.BeginScene();
try
{
if (texturestream.CanRead && read == true)
{
texturestream.Position = 0;
using (var tx = SlimDX.Direct3D9.Texture.FromStream(device, texturestream, NonPower, NonPower, 1, SlimDX.Direct3D9.Usage.None, SlimDX.Direct3D9.Format.Unknown, SlimDX.Direct3D9.Pool.Managed, SlimDX.Direct3D9.Filter.None, SlimDX.Direct3D9.Filter.None, 0))
sprite.Draw(tx, Color.Transparent);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message + " Render");
}
sprite.End();
device.EndScene();
device.Present();

Stoptimer.Stop();
//System.Console.WriteLine(Stoptimer.ElapsedMilliseconds.ToString());
});
}

So as you can see, i just have a bool named read, tell me if i can read it or not ( as i dispose the memorystream all the time and remake it), and i also use Thread.Sleep to let it have time to read it before it´s disposed (a really bad way to do it though).


So, well, any suggestion is appreciated, as you can surely see my lack of skills in this code.

Can´t someone at least give me a hint?
I am stuck with the Dynamic Texture.
I can make one, but have no idea how to write to it without remaking it, i don´t find any Write commands;S

This topic is closed to new replies.

Advertisement