WPF 3D Dynamic Textures - c#

I am writing a program (c# .NET 4.0 WPF 3D) that renders earth slices (like OpendTect's or Petrel's seismics module).
The problem is that I need to be able to zoom in deeply, and still see the details.
I tried putting a huge detailed texture (5000x5000), but it eats too much memory (200-300 MB), and crashes when I try to increase the size.
Now I want to see if there is any way of using something like a dynamic texture - the one that will change depending on distance to the camera.
Or maybe there is some other way of dealing with high-resolution surfaces?
I use this code to load texture:
wbm = new WriteableBitmap(
(int)1306*scale,
(int)ns*scale,
96,
96,
PixelFormats.Indexed8,
new BitmapPalette(getColors()));
...
visual3d.Material = visual3d.BackMaterial
= new DiffuseMaterial(new ImageBrush(wbm));

You have several options here:
Optimize current solution. 5000 * 5000 * 4 is around 100 MB. How do you load and show the texture? I have an app that renders up to 1100 3d objects and uses 300MB of memory and the performance is good enough. I strongly advice you to run a profiler, not only for pure performance reasons, but it helps to catch bugs! I my WPF 3D, thanks to the profiler I found that I was doing useless tessellation.
Create one low resolution image of whole texture and a few small high resolution images which are parts of the texture. If the user clicks/zooms-in you will load small high resolution image that will fit viewport. Similar effect to google maps.
Use XNA. You can host XNA inside WPF and use DXT texture. I'm not sure, but WPF doesn't support that texture formant directly.

Related

Some specific questions to GDI+ in C#

Im trying to create a little game with C# and GDI+. For learning purposes I'm trying to avoid as much frameworks etc. as possible. So I have some specific questions to GDI+
Is it possible to fill a region object in GDI with an image?
-If not, is there a manual way for it?
Can you read and set single pixels in a graphics object (not a bitmap)?
Have you got any tips for me to increase overall performance in GDI?
Thanks for any help
Is it possible to fill a region object in GDI with an image?
A region can't be filled, it doesn't store pixels. What you are almost certainly looking for here is the Graphics.Clip property. Assign the region to it and draw the image, it will be clipped by the region.
Can you read and set single pixels in a graphics object (not a bitmap)?
No, the Graphics object doesn't store any pixels itself, it only keeps track of where you draw to. The "device context" in Windows speak. Which can be a bitmap, the screen, a printer, a metafile. Not all of these device contexts let you read a pixel back after drawing (not a printer and not a metafile for example). But no problem of course when you draw to a bitmap.
Have you got any tips for me to increase overall performance in GDI?
There is one crucial one, .NET makes it very easy to overlook. The pixel format of the bitmap you draw to is super-duper important. The most expensive thing you'll ever do with a bitmap is copying it, from CPU memory to the video-adapter's memory. That copy is only fast if the format of the pixels exactly match the format the video-adapter uses. On all modern machines that's PixelFormat.Format32bppPArgb. The difference is huge, it is ten times faster than all the other ones.
Many answers that will detail these points:
Once you have a Region it will limit where pixels are drawn. Use Graphics.DrawImage then
No way to read and only a perverted way to set a Pixel by Graphics.FillRectangle(br, x,y,1,1); The reason behind this is probably that Graphics can not only operate in a Pixel mode but also with various other Units from points to inches and mm..
Use Lockbits. Just one example using one Bitmap. Other common jobs demand locking two (one input one output) or or even three (two inputs and one calculated output) bitmaps..
Know what you invalidate, often only a small part really needs it..
Learn about ImageList, it can't do much but is useful for what it does, that is cache images of one size and color depth
Learn when to use a Panel and when a Picturebox

Copy OpenGL back buffer directly onto GDI DC pixel data

I'm writting a GUI wich uses OpenGL via the OpenTK and the GLControl on C# and i'm trying to use dirty rectangles for drawing only the controls that need to be drawed. Obviusly it's not wise to redraw an entire maximized form just for refreshing a mouse-hover button.
My first attempt was to use glScissors but this doesn't limit the SwapBuffers, wich in my platform, I suspect (because of the performance almost entirely dependent on the window size) doesn't 'swap' but do a full copy of the back buffer onto the front buffer.
The second attempt was the glAddSwapHintRectWIN wich in theory would limit the swapped (in this case copied) area of the SwapBuffers, but this is only a hint and it doesn't do anything at all.
The third attempt was the glDrawBuffer to copy a part of the back buffer onto the frame buffer, for some unknown reason, even when i copy only a part of the buffer, the performance still decreases the same way before when the window size increase.
It seams that a full-area refresh it's still hapening no matter what i do.
So i'm trying to use the glReadPixels () and somehow get a pointer to draw directly onto a hDC pixel data getted from the CreateGraphics() of the control. Is this possible?
EDIT:
I think something is wrong with the GLControl, why the performance of this code depends on the screen size, i'm not doing any swapbuffers or clearing, just drawing a constant-size triangle on the front buffer:A driver problem, maybe?
GL.DrawBuffer(DrawBufferMode.Front);
Vector4 Color;
Color = new Vector4((float)R.NextDouble(), 0, 0, 0.3F);
GL.Begin(BeginMode.Triangles);
GL.Color4(Color.X, Color.Y, Color.Z, Color.W);
GL.Vertex3(50, 50, 0);
GL.Vertex3(150F, 50F, 0F);
GL.Vertex3(50F, 150F, 0F);
GL.End();
GL.Finish();
EDIT 2
This solutions are not viable:
Drawing onto a texture and using glGetTexImage for drawing onto a GDI bitmap and then drawing that bitmap onto the window hDC
Reading buffer pixels from the buffer using glReadPixels onto a GDI bitmap and then drawing that bitmap onto the window hDC.
Splitting the window onto a grid of viewports and updating only the cells that contains the dirty rectangle
First of all, what platform (GPU and OS) are you using? What kind of performance are we talking about?
Keep in mind that there are several limitations when trying to combine GDI and OpenGL on the same hDC. Indeed, in most cases this will turn off hardware acceleration and give you OpenGL 1.1 through Microsoft's software renderer.
Hardware accelerated OpenGL is optimized for redrawing the entire window every frame. SwapBuffers() invalidates the contents of the backbuffer, which makes dirty rectangles impossible to implement when double buffering on the default framebuffer.
There are two solutions:
do not call SwapBuffers(). Set GL.DrawBuffer(DrawBufferMode.Front) and use single-buffering to update the rectangles that are dirty. This has severe drawbacks, including turning off desktop composition on Windows.
do not render directly to the default framebuffer. Instead, allocate and render into a framebuffer object. This way, you can update only the regions of the FBO that have been modified. (You will still need to copy the FBO to screen every frame, so it may or may not be a performance win depending on your GUI complexity.)
Edit:
40-60ms for a single triangle indicates that you are not getting any hardware acceleration. Check GL.GetString(StringName.Renderer) - does it give the name of your GPU or does it return "Microsoft GDI renderer"?
If it is the latter, then you must install OpenGL drivers from the website of your GPU vendor. Do that and the performance problem will disappear.
After several test with OpenTK, it appears that in single or double buffered mode, the slowdown observed with control size increasing still remains, even with constant size scissor enabled. Even the use or not of GL.Clear() doesn't impact slowdown.
(Note that only height changes has significant impact.)
Testing with ansi c example, I had the same results.
Making the same couple of tests under linux gave the same results too.
Under linux I noticed that frame rate changes when I move from one display to the other. Even with vsync disabled.
Next step would be to check if directX has the same behaviour. If yes, than the limitation is located on the bus between display and graphic card.
EDIT: conclusion:
This behaviour is leading you to false impression. Consider only building your interface on a FBO with dirty rect mechanisms and render it on a quad (made of tri's is better) and swap as usual without thinking that you can improve swapping for a given window size by clipping some operations.

Quickly scale/crop a bitmap image stream for display in WPF

Question:
What is a fast way to scale and/or crop a bitmap provided from a WritableBitmap for display in the UI?
Requirements:
Have Low CPU usage
Handle large images (5 Megapixel, abt 2500x2000 pixels)
Resize and/or crop to the same resolution/area as the UI element the bitmap is displayed in.
Use WPF
Specifically, it must allow a 14FPS 5 Megapixel camera image stream to be displayed in a WPF UI element at full speed.
Update:
I have been able to speed up the drawing quite a bit by painting to a Canvas control using an ImageBrush as follows, where m_bitmap is my WriteableBitmap:
ImageBrush brush = new ImageBrush();
brush.ImageSource = m_bitmap;
brush.Stretch = Stretch.Uniform;
canvas.Background = brush;
I'm now able to get the full 14FPS, though it still using about 20% CPU, so I'm not sure how well it preform if I add another camera or two (the plan is to have 4 or so running).
Another thing I think might be slowing down the drawing is the images are in a mono, Gray8, format, not the standard RGB32 (or is it bgra32 for WPF?) format. If I understand correctly, the image has to be converted to the standard format to be displayed, which would add significant overhead to each frame's drawing time.
Some background:
I'm currently working with a 5 Megapixel, 14 FPS, video camera and am trying to get the frames to render to the screen at full speed. I would like to do this using WPF.
I currently have an example in WinForms that runs full speed for an unscaled image, but (as I would expect) it has major trouble if I set the pictureBox.SizeMode = Zoom;. The example reads raw data directly from the camera stream to a buffer and then copies the data from the buffer into the bitmap set to the PictureBox control. The copy algorithm uses LockBits to speed things up.
I converted that example into WPF, rewriting the parts using Bitmap objects to instead use WritableBitmap objects and an Image control instead of PictureBox. Unforunately this is not able to render the stream to the screen at any decent rate, scaled or unscaled. Both have significant CPU load and very slow updates.
The performance when rendering to the screen is turned off is great. It is able to process the image stream at full speed and resolution while using around 3% CPU and less than 100MB memory.
Note: when I say rendering to the screen is turned off, the WritableBitmap is still being continuously updated, only is not set to the Image control.
I've seen a lot of discussion about getting fast bitmap updating in WPF, but have been unsuccessful in getting it to work at an reasonable speed/cpu load. Also I would like to have the image scaled in such a way that I can see the whole image.
I imagine the key will lie in some sort of scaling/crop combination that needs to be done so that WPF will not try to render(cache?) all 5 million pixels, but only those on the screen, and only at the current screen resolution. I imagine/hope this can be done fairly easily and without too much hit to memory or CPU, but currently have no idea how to do so. I have found the DecodePixelWidth and DecodePixelHeight properties, but those are only applicable when loading an image from a file to a BitmapImage.
Did you have a look at the following post?
Resizing WritableBitmap
If it does not solve your problem, I have more questions for you:
What is the resolution of your image?
Is the size of you UI element constant? What's its size?
Edit:
After your edit, I noticed that you want to display a BitmapImage in Gray8 PixelFormat, why don't you try to set this property when creating your BitmapImage (m_bitmap)?
m_bitmap.Format = PixelFormat.Gray8; // could not test
I am certain that taking your 8 bits/pixel and multiplying the amount of bits needed per pixel by 4 while not gaining any quality is slowing down your application. Especially because you run operations on 32 bits per pixel images when you could be running those operations on 8 bits per pixel images.
While its interface is a bit old-fashioned, I believe that convert (see http://en.wikipedia.org/wiki/ImageMagick) is very often used (and may in fact be the industry standard).
Edit: StackOverflow has about 2,300 question tagged with imagemagick here. See for example What is the difference for sample/resample/scale/resize/adaptive-resize/thumbnail operators in ImageMagick convert?
The OP for https://apple.stackexchange.com/a/41531 decided to go with ImageMagick. And the accepted answer to Efficient JPEG Image Resizing in PHP also suggests ImageMagick, with 19 votes.
However, I don't know whether ImageMagick is capable of meeting your requirements of 14FPS, 5 Megapixels.
The only answer to Recommendation for real time image processing tools on Linux suggests a fork graphicsmagick, which seems to also be available for Windows.

PixelShading in C#

I hope I do not make my first mistake with my first post.
I am writing a library for several graphical effects and filters (for example Sobel or Gauß mask).
Because of the low Speed, doing this on the CPU, I wrote some shaders with the Shazzam tool.
My concret Problem is, that I am not able to use this shader in C#.
In the Internet I found only advice how to apply a pixelshader as a effect in XAML directly to a element, which is not usable for my application, because this makes it impossible to apply several shaders on one Image, which is needed, for example the Canny Edge Detector.
To illustarte this issue a Little pseudo-code, which should Show, what I expect from the method.
PixelShader somePixelShader = new PixelShader(pixelshader.ps);
somePixelShader.Input = Bitmap;
somePixelShader.Height = 200;
somePixelShader.Width = 800;
somePixelShader.Execute();
Bitmap = somePixelShader.Result;
As you see, everything should be done in C#.
Perhaps you can help me with my issue.
You can make a copy of the current effect output as a bitmap with RenderTargetBitmap, then submit this outputted image as the new input for the next effect, rinse, repeat.
Update : after a small (and inconclusive) test, this will not work : Can't render pixel shader to RenderTargetBitmap! Please help!
Check out these white papers for step-by-step instructions + examples on how to compile and use a pixel shader in WPF or SL.
You may also want to check out the WPF Pixel Shader Effects Library here.

How To Handle Image as Background to CAD Application

My application is used to design airports for a flight simulator. Users can add one or more images as background. The image(s) can be sized accurately and then used as a template to lay down features such as runways, aprons and so on.
I use a third party graphics library (Piccolo) which has an image class (as far as I can see it is a simple wrapper for System.Drawing.Image).
So far I have done little except allow the user to add an image, size it and so on. It will be no surprise that users sometimes complain of poor performance. We tell them not to load large images (up to 100k seem OK) but don't stop them and 100Mb bitmaps have been used with horrible results.
I need to fix this in a couple of ways. First by converting any image they use to an efficient format (size vs definition) and second by ensuring that the loaded image is suitably sized for the dimensions - at the moment I don't do anything specific to deal with the resizing of say a 2000 x 2000 image to fit a 500 x 500 area of the display.
The default 1:1 display of the application represents 1m per pixel. Once the user has resized the image to fit accurately would I be right in thinking that the best resoultion for the image would be to resample it to that size? I am aware that if the user zooms in way past 1:1 which they will probably do then the clarity of the image will fall.
My ignorance of handling images is complete. I have looked at some image manipulation libraries (ImageMagick and the free version of dotIamge) first for converting the input image to a standard one and second for resizing -resampling. The truth is that they do far more than I need.
Any pointer much appreciated.
Yes, resampling so that the bitmap doesn't constantly have to be rescaled for every paint should make a big difference. The default Graphics.InterpolationMode makes pretty images but is not cheap.
Another biggie is the pixel format of the bitmap. Format32bppPArgb is 10 times faster than any of the others on most video adapters.
private static Bitmap Resample(Image img, Size size) {
var bmp = new Bitmap(size.Width, size.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using (var gr = Graphics.FromImage(bmp)) {
gr.DrawImage(img, new Rectangle(Point.Empty, size));
}
return bmp;
}

Categories