This might be a very simple question, but I searched and found no other way to do it. It doesn't make sense to redraw the background on every Draw. Is there a way to draw some things and leave them on the screen?
I've tried to comment-out the
GraphicsDevice.Clear(Color.CornflowerBlue);
But that doesn't help. (What is its purpose?)
The dark purple colour you are seeing is used by XNA and DirectX to indicate an uninitialised buffer. XNA will also clear buffers to this colour to emulate the behaviour of the Xbox 360 or Windows Phone, so that if you build a game on Windows, it "just works" on those other platforms (or, rather, so it fails in the same way, so you can debug it).
XNA is double-buffered. You don't draw directly to the screen, but to a "backbuffer". The screen only displays the "front buffer". Every time GraphicsDevice.Present gets called (Game calls it for you in EndDraw), those two buffers get swapped, and what you were drawing gets displayed (and you get a fresh buffer to draw on).
I'm not sure why XNA marks the buffer as uninitialised when it gets swapped. I haven't come across this behaviour before - mostly because it's very unusual to want to swap buffers and preserve their contents.
Usually what you want to do is call Game.SupressDraw, when you know you're not going to modify the contents of the screen (saving both a call to Draw and a swap). See also answers here and here.
Keep in mind that clearing the screen with GraphicsDevice.Clear is extremely fast. And that XNA has no concept of "background" or "foreground" (you're always drawing on top of whatever is already in the buffer).
If you do have some expensive-to-render content that you want to re-use between frames, generally you would draw it into to a render target once, and then draw that to the screen each frame. But, as always, avoid premature optimisation! Graphics cards are designed specifically to redraw scenes every frame - they're pretty damn fast!
See this, if you want to just prevent it clearing the image you can do:
GraphicsDevice.GetType().GetField("lazyClearFlags", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(GraphicsDevice, ClearOptions.DepthBuffer);
Related
I'm using a SwapChainPanel to render a control. The render method attaches to the CompositionTarget.Rendering event.
Also, RenderTarget.CreateCompatibleTarget is called to create an offscreen target. The compatibleTarget.Bitmap property is called to create a cached bitmap that can be blitted onscreen.
During each frame:
BeginDrawing() is called on the onscreen target.
If the scene has been invalidated by program logic, it is redrawn to the offscreen target.
The onscreen target is cleared using the background color. Without this, successive frames are somehow blended into each other.
The offscreen bitmap (cached above) is drawn onto the onscreen target using onscreenTarget.DrawBitmap(cachedBitmap), with opacity set to 1.
onScreenTarget.Flush() is called to flush the contents.
EndDrawing() is called on the onscreen target.
I find that this gives a very low frame rate.
Comparison with WindowRenderTarget
For comparison, I tested the exact same scene code in a WinForms app using a WindowRenderTarget. (SharpDX makes this possible since it works on UWP as well as desktop.) This gives a much higher frame rate, and zero steady-state CPU consumption.
Questions:
Why does SwapChainPanel produce such a low frame rate compared to WindowRenderTarget?
Why is it necessary to clear the onscreen target each frame before drawing the bitmap in step 4 even when the opacity is 1?
Can I avoid steps 1-6 if nothing has changed? This consumes around 7% CPU.
I don't think there should be too much performance difference between SwapChainPanel & WindowRenderTarget, because both of them are almost directly based on DirectX components. You could compare their settings to investigate the performance differences: Is the SwapChainPanel related with D2D device? Any differences among the configuration of devicecontext, swapchain description, and WindowRenderTarget's renderTargetProperty; Are there any difference between the bitmap processing methods?
THe opacity is set for the image you are drawing. However, the clear() if for the overall rendering view.
When you mean "if nothing changed", I supposed you mean you have nothing to draw. Then of course you can do nothing. Otherwise, the step from 1 to 6 are necessary.
Besides, you can find a good documented SwapChainPanel example here. Some settings in this example are for performance improving.
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.
Simply put, I'm trying to draw an image (2560x2048) that is supposed to be zoomed / draged and such but the performance is very bad, because it flickers everytime I move it. I use a custom control to be able to drag the image to a new position and zoom in and out, which means it have to be flexible and fast.
So, what is the easiest and best way to just draw a single image with the graphics card? Without having to initialize a thousand directX objects just for one simple purpose.
Overall, the application is a tool - so not a game. But this particular large image is supposed to be drawn effectively.
Double buffering is your friend http://msdn.microsoft.com/en-us/library/3t7htc9c.aspx
Well I am Borland C++ friendly so i would use simple Canvas (simple Windows GDI interface).
No need for GL,GLSL or DX
you need 2 bitmaps.
one as source image (that is your 2560x2048) next is back-buffer of the screen (client size of your view area). Both has to be DIB. I prefer pf32bit pixel format (32 bit int as a color so I can use int *p ...)
need to write render function (or use GDIs strech draw or CopyRect)
do not use Set or Put or Pixels they are slow (always checking size and color format and many other stuff for each pixel). Instead use bitmaps scan lines (in VCL bmp->ScanLine[y]).
This usually speeds up things about ~1000 times if done properly
put it all together
clear back-buffer with background color. Use your render function to copy viewed area to back-buffer (no need to render whole image). When all rendering is done copy back-buffer to the screen.
when all works you can further speed up things
use array of scan lines pointers instead of calling them in rendering. Addresses of scan lines are changed only after resizing of bitmap so on resize delete all array of pointers and create and fill new one (int *pyx[ys];)
This kind of bitmap rendering is fast enough for simple software 3D rendering so for your purpose must be sufficient with high enough framerate (estimate well over 70fps on average desktop machine)
some code for Borland C++ VCL (just so you know what to look for in your programing languge)
// init
int xs=0,ys=0,*pyx=NULL;
Graphics::TBitmap *bmp=new Graphics::TBitmap;
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf32bit;
// exit
if (bmp) delete bmp; bmp=NULL;
if (pyx) delete pyx; pyx=NULL;
// resize(_xs,_ys)
if (pyx) delete pyx; pyx=NULL;
bmp->Width=_xs;
bmp->Height=_xs;
xs=bmp->Width;
ys=bmp->Height;
pyx=new int*[ys];
if (pyx==NULL) return; // not enough memory
for (int y=0;y<ys;y++) pyx[y]=(int*)bmp->ScanLine[y];
// now pyx[y][x]=0; means setpixel(x,y)=color(0) without any slowing down checks
// now c=pyx[y][x]; means colro(c)=getpixel(x,y) without any slowing down checks
// but beware of accessing x,y, outside <0;xs),<0;ys) !!!
I have a C# .NET application with which I've created a custom image display control. Each image display represents its own display context and draws the image using glDrawPixels (Yes I know it would be better to use textures, I plan to in the futures but this app is already too far along and my time is limited).
I am now trying to have both images pan simultaneously. That is, when one image is moved down ten pixels, the second image moves down ten pixels. Like so:
imageOne.YPan -= 10;
imageTwo.YPan -= 10;
imageOne.Invalidate(); //This forces a redraw.
imageTwo.Invalidate(); //This forces a redraw.
Alright so here is the problem I am having. Only one of the images displays is redrawing. If I place a pause in between the two Invalidate calls and make the pause duration at least 110 milliseconds both will redraw, but not simultaneously. So it looks as if the second image is always trying to catch up to the first. Plus, a 110 millisecond pause slows down the motion too much.
I have tried placing the updating and invalidating of each image in its own thread but this did not help.
At the beginning of drawing I make the appropriate context is current, and at the end I am calling swapbuffers(). I tried adding a glFinish to the end of the draw function, but there was no change.
Could it be that its the graphics card that is the problem? I am stuck using an integrated gpu that only has openGL 1.4.
Hopefully, I have provided enough detail that the answer to my problem can be found.
Its difficult telling what's wrong with what you do since you give so little detail. Here are some pointers which may help.
- before doing something in a context, make sure you make it the current one. If you want to pan two contexts, make the first one current, pan it and then make the second one current and pan it. These is no real reason why this should not work.
- If it looks like there is a timing problem, adding glFinish() at strategic places may help weed the problem out
- As should always be done, on occasions call glError() and see that everything went well.
- I'm not sure how this is done in the framework you're talking about but you should make sure that both contexts get a swapBuffers() call for every frame.
Invalidate doesn't force an immediate redraw. It marks the window invalid, and when the message queue runs out of other messages, a paint message will be created and processed. But that won't happen until you finish processing the current message and return to the main message loop, and it may be delayed even more than that.
Generally OpenGL animation is an exception to the rule of doing all drawing inside Control.OnPaint (or in a handler for the Control.Paint event).
I am creating a custom control using C# GDI+.
Quick explanation...the control will be say 500 pixels on screen but will contain perhaps 500000 pixels of information. So although i'm only showing 500px at a time i need to obviously scroll in the horizontal plane (left & right). The tricky part is that each 500px chunk of bitmap takes a while (between 100ms - 1000ms) to render.
So my plan is to maintain a 1500px bitmap in memory. i.e. the 500px visible part and 500px either side of the visible area and draw the off-screen parts asynchronously as the user scrolls.
I would like some feedback, advice, criticism or examples of code to help me achieve this. It seems fairly straight forward but after a few initial test attempts its proving more difficult than one would imagine.
Thanks.
The effectiveness of this approach depends on, amongst other things, the amount of moving around the user will do. If the user is making small movement, then stopping to consider the new information, this could work. However, if the user is whizzing back and forth, you'll still have an issue.
Does your application lend itself to gradually improving the quality of the image - i.e. providing a quick usable image and then improving it as the user stops to consider it?
I had a similar problem a few years back. As dommer mentions, if you're processing chunks of the image before displaying them you're best off showing something and improving it later. If you're having problems blitting the original image, you've got something wrong with your method. GDI+ is very particular about pixel depth (you want 32bpp with alpha).
In our case, we processed in 500px tiles and padded out a tile around the visible view Ff the user scrolled outside the area we'd processed we blitted bits of the original image with a dark semi-opaque rectangle super-imposed on them. These chunks were queued for processing. As we processed chunks of the image (centre-out) they semi-opaque rectangles would disappear.
It worked reasonably well, was very responsive and very fast. It's very fast to blit the original bitmap onto the screen, and in our case the processing was usually very close behind. The effect of the tiles getting lighter was actually quite pretty.
Draw only the visible area.
Build a method
Bitmap DrawGraph(leftMargin, rightMargin) {...}
and then in OnPaint() do
Bitmap bmp = new Bitmap(e.ClipRectangle.Width, e.ClipRectangle.Height);
bmp = DrawGraph(e.ClipRectangle.Left, e.ClipRectangle.Right);
e.Graphics.DrawImageUnscaled(bmp, e.ClipRectangle.Location);