[.net] DirectX Textures from Bitmaps.

Started by
5 comments, last by Thevenin 17 years, 5 months ago
I'm really confused, and rather frustrated atm. I'm trying to get minimaps rendered dynamically for my MMORPG (to ditch dependence on prerendered minimaps, etc). Since C# doesn't let you do anything raw, I figured I would just create a directX texture every frame (the texture is like 30x30, so it should be small enough to not matter). Since I cannot plot to the DirectX texture, I plotted to a Bitmap object, and than converted it into a DirectX texture on every frame. My problem is, I don't really know how to do this. I could convert the Bitmap to a Stream and use the DirectX TextureLoader.FromStream() function, or the Texture = new Texture(Device,Bitmap,Usage,Pool). But the later of the two seems to be INCREDIBLY slow!
Texture TheTexture = new Texture(MyDirectX.MyDevice, MyMinimap,
           Usage.None, Pool.Managed);
I found it was 14x faster to load a 512x512 bitmap (from my Assembly().Reflection) as a stream from the TextureLoader class in DirectX. What should I do? Does anyone know how to convert a Bitmap to a stream, or how to get the new Texture() command to work properly? Edit: Jeeze, even this doesn't work; it throws an illegal operation error when the TextureLoader reads it.
Stream TheStream = new MemoryStream();
MyMinimap.Save(TheStream, ImageFormat.Png);
Texture TheTexture = TextureLoader.FromStream(MyDirectX.MyDevice, TheStream);
[Edited by - Thevenin on November 7, 2006 4:49:23 PM]
Advertisement
Found the problem, and a subsequent problem.

        Stream TheStream = new MemoryStream();        MyMinimap.Save(TheStream, ImageFormat.Png);        TheStream.Position = 0;        MyMinimapTex = TextureLoader.FromStream(MyDirectX.MyDevice, TheStream);


I had to set the stream.position to 0. Inaddition, it was cumulating alot of memory, so I had to dispose the texture after the EndDraw() command.
You could create a texture and lock it/unlock it when you need to update it. I use the following code to update a texture. It has to be created in the Pool.Managed pool, I think (as opposed to the Pool.Default pool). It might be less memory intensive that creating and disposing a texture each frame. Whether that makes a difference, I don't know.

  756         public override void WritePixels(PixelBuffer buffer)  757         {  758             Direct3D.Surface surf = mTexture.GetSurfaceLevel(0);  759   760             int pitch;  761             int pixelPitch = mDisplay.GetPixelPitch(surf.Description.Format);  762             PixelFormat pixelFormat = mDisplay.GetPixelFormat(surf.Description.Format);  763   764             surf.Dispose();  765   766             GraphicsStream stm = mTexture.LockRectangle(0, 0, out pitch);  767   768             if (buffer.PixelFormat != pixelFormat)  769                 buffer = buffer.ConvertTo(pixelFormat);  770   771   772             for (int i = 0; i < SurfaceHeight; i++)  773             {  774                 int startIndex = buffer.GetPixelIndex(0, i);  775                 int rowStride = buffer.RowStride;  776                 IntPtr dest = (IntPtr)((int)stm.InternalData + i * pitch);  777   778                 Marshal.Copy(buffer.Data, startIndex, dest, rowStride);  779             }  780   781             mTexture.UnlockRectangle(0);  782   783         }
I would suggest you follow kanato's advice and lock/unlock the texture, creating/destroying a texture every frame would be a pretty slow operation, even for a small image. However, I would suggest you use the C# unsafe keyword and use raw pointers to write to the texture, I've found this to be faster than writing to the stream provided by the GraphicsStream. It's pretty easy to get the pointer to the locked data:

GraphicsStream stream = null;IntPtr bufferPointer = IntPtr.Zero; stream = yourTexture.LockRectangle(...);bufferPointer = stream.InternalData;  // This is our pointer. unsafe{   byte *buffer = (byte *)bufferPointer.ToPointer();    // Write to the texture using the buffer pointer....} yourTexture.UnlockRectangle(...);


Disclaimer:
I'm recalling this stuff from memory, so it may not be 100% accurate.
I tried both methods posted, but they throw null reference exceptions when I try to LockRectangle. I have a feeling its due to the way I create the Texture (I don't use new Texture(), I use the TextureLoader, because new Texture() always throws an error).

How do I can I create a texture?

Edit: Oops, I'm an idiot. I had left the MyMinimapTex.Dispose() in my code from my previous code. I still don't know how to create textures without loading Textureloader though. =/
I updated my DirectX references, and suddenly using new Texture(...) is no longer throwing errors at me. Yay!
Quote:Original post by Tape_Worm
I would suggest you follow kanato's advice and lock/unlock the texture, creating/destroying a texture every frame would be a pretty slow operation, even for a small image. However, I would suggest you use the C# unsafe keyword and use raw pointers to write to the texture, I've found this to be faster than writing to the stream provided by the GraphicsStream. It's pretty easy to get the pointer to the locked data:

*** Source Snippet Removed ***

Disclaimer:
I'm recalling this stuff from memory, so it may not be 100% accurate.


I used a slightly modified version of your code (shown below for people who later google this topic).

    public unsafe void sDrawMinimap()    {        int TheMapSize = MyMinimap.Width;        GraphicsStream TheStream = null;        IntPtr TheBufferPointer = IntPtr.Zero;        TheStream = MyMinimapTex.LockRectangle(0, LockFlags.None);        TheBufferPointer = TheStream.InternalData;                 unsafe        {            uint* TheBuffer = (uint*)TheBufferPointer.ToPointer();                    /* Write to the texture using the buffer pointer.... */            for (int TheY = 0; TheY < TheMapSize; TheY++)                for (int TheX = 0; TheX < TheMapSize; TheX++)               {                   *TheBuffer = (uint)Color.Purple.ToArgb();                   TheBuffer++;               }        }        MyMinimapTex.UnlockRectangle(0);    }


It works quite nicely. [smile]

Thanks kanato, for the managed solution aswell. I'll likey use your managed solution for the client app, and the unsafe solution for the map editor (because the minimap on the client app is signficantly smaller).

Edit: Switched 'TheX' and 'TheY'. Forgot that its stored row by row in memory.

This topic is closed to new replies.

Advertisement