c# - The reason behind slow performance in WPF -


i'm creating large number of texts in wpf using drawtext , adding them single canvas.

i need redraw screen in each mousewheel event , realized performance bit slow, measured time objects created , less 1 milliseconds!

so problem? long time ago guess read somewhere rendering takes time, not creating , adding visuals.

here code i'm using create text objects, i've included essential parts:

public class columnidsinplan : uielement     {     private readonly visualcollection _visuals;     public columnidsinplan(baseworkspace space)     {         _visuals = new visualcollection(this);          foreach (var column in building.modelcolumnsintheelevation)         {             var drawingvisual = new drawingvisual();             using (var dc = drawingvisual.renderopen())             {                 var text = "c" + convert.tostring(column.groupid);                 var ft = new formattedtext(text, cultureinfo, flowdirection,                                            typeface, columntextsize, columntextcolor,                                            null, textformattingmode.display)                 {                     textalignment = textalignment.left                 };                  // apply transforms                 var st = new scaletransform(1 / scale, 1 / scale, x, space.flipyaxis(y));                 dc.pushtransform(st);                  // draw text                 dc.drawtext(ft, space.flipyaxis(x, y));             }             _visuals.add(drawingvisual);         }     }      protected override visual getvisualchild(int index)     {         return _visuals[index];     }      protected override int visualchildrencount     {                 {             return _visuals.count;         }     } } 

and code run each time mousewheel event fired:

var columnsgroupids = new columnidsinplan(this); mycanvas.children.clear(); fixedlayer.children.add(columnsgroupids); 

what culprit?

i'm having trouble while panning:

    private void workspace_mousemove(object sender, mouseeventargs e)     {         mousepos.current = e.getposition(window);         if (!window.ismousecaptured) return;         var tt = gettranslatetransform(window);         var v = start - e.getposition(this);         tt.x = origin.x - v.x;         tt.y = origin.y - v.y;     } 

i'm dealing same issue , i've discovered quite unexpected. i'm rendering writeablebitmap , allowing user scroll (zoom) , pan change rendered. movement seemed choppy both zooming , panning, naturally figured rendering taking long. after instrumentation, verified i'm rendering @ 30-60 fps. there no increase in render time regardless of how user zooming or panning, choppiness must coming somewhere else.

i looked instead @ onmousemove event handler. while writeablebitmap updates 30-60 times per second, mousemove event fired 1-2 times per second. if decrease size of writeablebitmap, mousemove event fires more , pan operation appears smoother. choppiness result of mousemove event being choppy, not rendering (e.g. writeablebitmap rendering 7-10 frames same, mousemove event fires, writeablebitmap renders 7-10 frames of newly panned image, etc).

i tried keeping track of pan operation polling mouse position every time writeablebitmap updates using mouse.getposition(this). had same result, however, because returned mouse position same 7-10 frames before changing new value.

i tried polling mouse position using pinvoke service getcursorpos like in answer eg:

[dllimport("user32.dll")] [return: marshalas(unmanagedtype.bool)] static extern bool getcursorpos(out point lppoint);  [structlayout(layoutkind.sequential)] public struct point {     public int x;     public int y;      public point(int x, int y)     {         this.x = x;         this.y = y;     } } 

and did trick. getcursorpos returns new position each time called (when mouse moving), each frame rendered @ different position while user panning. same sort of choppiness seems affecting mousewheel event, , have no idea how work around one.

so, while of above advice efficiently maintaining visual tree practice, suspect performance issues may result of interfering mouse event frequency. in case, appears reason rendering causing mouse events update , fire slower usual. i'll update if find true solution rather partial work-around.


edit: ok, dug little more , think understand going on. i'll explain more detailed code samples:

i rendering bitmap on per-frame basis registering handle compositiontarget.rendering event described in this msdn article. basically, means every time ui rendered code called can update bitmap. equivalent rendering doing, it's rendering code gets called behind scenes depending on how you've set visual elements , rendering code can see it. override onmousemove event update variable depending on position of mouse.

public class mainwindow : window {   private system.windows.point _mousepos;   public window()   {     initializecomponent();     compositiontarget.rendering += compositiontarget_rendering;   }    private void compositiontarget_rendering(object sender, eventargs e)   {     // update writeablebitmap here using _mousepos variable   }    protected override void onmousemove(mouseeventargs e)   {     _mousepos = e.getposition(this);     base.onmousemove(e);   } } 

the problem that, rendering takes more time, mousemove event (and mouse events, really) gets called less frequently. when rendering code takes 15ms, mousemove event gets called every few ms. when rendering code takes 30ms, mousemove event gets called every few hundred milliseconds. theory on why happens rendering happening on same thread wpf mouse system updates values , fires mouse events. wpf loop on thread must have conditional logic if rendering takes long during 1 frame skips doing mouse updates. problem arises when rendering code takes "too long" on every single frame. then, instead of interface appearing slow down little bit because rendering taking 15 ms per frame, interface stutters because 15ms of render time introduces hundreds of milliseconds of lag between mouse updates.

the pinvoke workaround mentioned before bypasses wpf mouse input system. every time rendering happens goes straight source, starving wpf mouse input system no longer prevents bitmap updating correctly.

public class mainwindow : window {   private system.windows.point _mousepos;   public window()   {     initializecomponent();     compositiontarget.rendering += compositiontarget_rendering;   }    private void compositiontarget_rendering(object sender, eventargs e)   {     point screenspacepoint;     getcursorpos(out screenspacepoint);      // note screenspacepoint in screen-space pixel coordinates,      // not same wpf units mousemove event.      // may want convert wpf units when using getcursorpos.     _mousepos = new system.windows.point(screenspacepoint.x,                                           screenspacepoint.y);     // update writeablebitmap here using _mousepos variable   }    [dllimport("user32.dll")]   [return: marshalas(unmanagedtype.bool)]   static extern bool getcursorpos(out point lppoint);    [structlayout(layoutkind.sequential)]   public struct point   {     public int x;     public int y;      public point(int x, int y)     {       this.x = x;       this.y = y;     }   } } 

this approach didn't fix rest of mouse events (mousedown, mousewheel, etc), however, , wasn't keen on taking pinvoke approach of mouse input, decided better stop starving wpf mouse input system. ended doing updating writeablebitmap when needed updated. needs updated when mouse input has affected it. result receive mouse input 1 frame, update bitmap on next frame not receive more mouse input on same frame because update takes few milliseconds long, , next frame i'll receive more mouse input because bitmap didn't need updated again. produces more linear (and reasonable) performance degradation rendering time increases because variable length frame times sort of average out.

public class mainwindow : window {   private system.windows.point _mousepos;   private bool _bitmapneedsupdate;   public window()   {     initializecomponent();     compositiontarget.rendering += compositiontarget_rendering;   }    private void compositiontarget_rendering(object sender, eventargs e)   {     if (!_bitmapneedsupdate) return;     _bitmapneedsupdate = false;     // update writeablebitmap here using _mousepos variable   }    protected override void onmousemove(mouseeventargs e)   {     _mousepos = e.getposition(this);     _bitmapneedsupdate = true;     base.onmousemove(e);   } } 

translating same knowledge own particular situation: complex geometries lead performance issues try type of caching. example, if geometries never change or if don't change often, try rendering them rendertargetbitmap , add rendertargetbitmap visual tree instead of adding geometries themselves. way, when wpf performing it's rendering path, needs blit bitmaps rather reconstruct pixel data raw geometric data.


Comments

Popular posts from this blog

database - VFP Grid + SQL server 2008 - grid not showing correctly -

jquery - Set jPicker field to empty value -

.htaccess - htaccess convert request to clean url and add slash at the end of the url -