Paint.NET’s performance is an interesting topic. There are some areas, such as startup, where Paint.NET is very fast. There are other areas, such as selection manipulation, where it is quite slow, even laughably so in non-pathological situations. If you’ve ever worked with the Magic Wand tool on an image of more than about 4 megapixels, then you probably understand what I’m talking about.

Just about the slowest thing you can do in Paint.NET v3.5.x is to use the Magic Wand on a large image and then try to move, rotate, or combine that selection. The geometry processing is slow, and the rendering takes just as long. I even ran into a test case where clicking the Magic Wand on a very high resolution image of the Milky Way took hours of processing time and a few gigabytes of memory. Ouch. (I actually killed the process after about 20 minutes, so “hours” is just an estimate.)

At this point I’m going to jump ahead to the results of what I’ve done. I took* a 6 MP image (3871 x 1595 pixels) of a 2011 BMW 5-series and loaded it up in v3.36, v3.5.10 and in 4.0. (For those who aren’t aware, 3.36 and 3.5.x are basically two separate generations of the Paint.NET codebase.) I then resized this image to be 5x larger, giving me a 136 MP image (19,355 x 7,975). I clicked with the Magic Wand tool in the same spot in each, waited for the selection to be created, and then did some simple things like scrolling, zooming, and drawing a gradient (which is clipped to the selection).

In 3.5.10, scrolling and zooming were very slow, limited by the rate at which the geometry of the selection could be continually rasterized. Drawing something like a gradient was able to run at about 1 frame every 2 seconds. The app just couldn’t deal with it. CPU usage was extremely high the whole time.

In 4.0, scrolling and zooming were perfectly fluid. Oftentimes the selection itself would lag behind what I was doing but it always caught up a moment later. It didn’t get in the way of me trying to find the area of the image that I wanted to do stuff with next. I could even draw a gradient, clipped to the selection, and it was responsive the whole time (the status bar and whatnot kept updating). Now, to be honest, the gradient actually takes longer to render than in 3.5.10, but it fills in gradually as each tile completes its work, and allows the user to keep doing other stuff while it’s in-progress. This is a much better situation to be in: I can and will optimize the gradient renderer to be faster at some point, but in the meantime I still have something I can ship and that people can get utility from.

If we go back to Paint.NET v3.36, the situation becomes almost comical. You see, 3.36 had an animated “dancing ants” selection outline, and it uses GDI+ to render this on the UI thread. As such, in this test case, it is constantly chewing up CPU while also providing an almost entirely non-responsive UI. You can’t even hover the mouse over the menus to get them to light up and indicate they can be clicked without waiting a second or two first. The 3.5 release improved this by removing the animation. 4.0 will give us the best of both worlds: we get the animation, and we also get trivial CPU usage. We also get all sorts of other improvements.

The way I do this in 4.0 is to render the selection on another thread. Actually, I render masks, which are 1-byte per pixel bitmaps that only store the alpha channel. Brushes are then used to fill in the color. I create 5 of these masks: 1 for the interior, and then 4 for the outline to take care of the animation. When I go to draw the selection back on the UI thread, I just use ID2D1RenderTarget::FillOpacityMask() three times. Once for the interior, once for the Nth mask with a black brush, and once more for the ((N+4)%4)th mask with a white brush. This increases memory usage and GPU fill rate requirements, but with dramatic improvements to responsiveness, CPU usage, and battery life.

Because I’m using Direct2D and do this mask filling with hardware acceleration, CPU usage for an animated selection almost non-existent. Click on the screenshot below for evidence.


paint.net 4.0 sipping on CPU usage while still animating a very complicated selection outline

In summary, I’ve traded 1.25 frame buffers of memory for a huge performance win. Manipulating selections is still just as slow as it ever was, and over time I plan to move that work off the UI thread or whatever it takes. Right now if you use the Move Selection tool on a complex selection, it does a bunch of matrix math to transform all the points before committing the changes to the Selection object. Well, clearly we can render that in a much easier way: take the cached bitmap and just apply the same transformation. etc. etc. One step at a time.

* By “take” I don’t mean that I was the photographer.

One nice thing about the Move tools in Paint.NET is that they implement something I call fine-grained history. When you go to edit a selection or move its pixels around, you get 1 history entry for each adjustment you make. This allows you to move it around, rotate it, etc. and then when you press “Undo” it only backs out of the most recent adjustment you made. Contrast this to the Gradient, Line/Curve, and Text tools which don’t keep track of each edit you make and can only undo things wholesale.

Recently I’ve been working on adding this to the Gradient tool for 4.0. This support will then make its way into the shape tools, and probably the Text tool as well (also, shapes will be undergoing an overhaul soon). The reason I’ve been working on the Gradient tool is that it is a fairly simple tool and this allows me to get the code for fine-grained history working without having to worry about as many “moving parts.”

Here’s a screenshot showing the Gradient tool having gone through it’s initial “draw” operation, followed by two “edit” operations:


(click for full resolution version)

Once this makes its way to the new Shapes tool (not yet written unfortunately), you’ll be able to: 1) draw a shape, 2) move, rotate, resize the shape, 3) change the colors, 4) change the stroke, 5) change any other properties, and then 6) finish/commit it to the layer. Currently the shape tools in v3.5 are fairly barbaric and don’t even let you change their color or other properties once you’ve pressed down the mouse button. It can be quite painful if you are consistently off by a few pixels and have to constantly redo your Rectangle until it’s just right.

There are some other important benefits to doing this. I’ll be able to significantly reduce memory usage, for starters. In Paint.NET v3.5, the tools are implemented in a rather cumbersome way. While you draw, it’s modifying the layer data so it contains what you’re drawing … then it draws that to the screen. Then, when you move the mouse it has to copy back the original contents of the layer before drawing the new stuff. When you’re done, it has to copy back the original pixels, save it to disk, then redraw everything one last time. This is implemented with a very carefully debugged protocol that requires all sorts of data to be copied around various buffers before, during, and after any edit operation. Any bug in the protocol for drawing, copying, and emitting history entries can lead to data loss.

With the new system, the gradient (et. al.) draws itself using a layer overlay, which only affects the rendering pipeline and not the image itself. Undoing the edit before it’s committed is a simple matter of throwing away the overlay. When it’s time to commit the drawing, the code just says “hey I changed this rectangular area” and then a simple diffing algorithm copies away only the parts of the layer that changed. There’s no need for me to manage complicated geometry masks and dirty regions any more (other than what’s needed for an active selection, of course). All the large and temporary (and permanent, ack!) buffers are simply no longer needed.

This is all very important! Reducing memory usage is very important for an application like Paint.NET, especially on 32-bit systems. This also removes another barrier toward migrating to a fully tile-based memory allocator, which then opens up doors for many other very, very cool features.

Another benefit to doing this is that it moves the tool rendering code off of the UI thread and into the rendering pipeline (which was already moved off the UI thread for 4.0). The UI will always be responsive while you’re drawing, even if the canvas is still “catching up” to what you’ve done. It’s pretty cool to be able to throw around the Gradient tool’s “handles” (aka “nubs”) and see them responding at a much higher framerate than the canvas itself. If you’ve ever had to work with an image that is very large, or has a lot of layers, or both, then you probably already know that this will be a great thing.

Also, right now the Move Selected Pixels tool is a monster (for me that is; not for you). While it does work perfectly well, the code is wildly complicated and fragile. Adding or changing any features would be a recipe for disaster. By migrating it to this much simpler system, I’ll be able to reduce memory usage further, increase performance and quality, and remove any “hitch” you may notice at the start of a move operation. The reason the “hitch” happens is that the history data (the layer’s pixels) must be saved off into a temporary buffer before any rendering can be done. Afterward, there’s a frightening dance of copying pixel data from the layer to a scratch area, and then transforming pixels from a 2nd copy back to the layer, and then undoing it all before redoing it, etc. The nightmare gets worse when history entries come into the mix. The code is basically the worst time travel paradox you could possibly imagine – I can’t honestly say I fully understand how it works, even though I wrote it. What I can say is that it works, but only because it’s been thoroughly and exhaustively tested, debugged, and bugfixed.

Oh, and you’ll notice in this screenshot that I’ve finished up the last bits of UI reorganizing. I’ve done away with the Window and Utilities menus. Instead, each of the floating windows has its own toggle button at the top-right. The gear icon opens up the Settings dialog, and the question mark icon gives you the traditional help menu. Anything that was in the Utilities menu has either been moved into the Settings dialog, or removed. I‘ve also increased the size of these icons to 24×24 because I found they were just too darn small at 16×16.

Within the Settings dialog I’ve added a Diagnostics page which gives you a peek at your system information as Paint.NET sees it (basically the information that’s printed at the top of a crash log, and more). It will also give you access to the crash logs folder and some other simple things that should make troubleshooting a bit simpler. Oh, and for those who don’t like the “dancing ants” (animated selection outline), I’ve included a checkbox to control whether this is on or off (not on the Diagnostics tab though).

In time, I hope to elaborate further on what I mean by “this stuff opening doors for other very cool features.” I’ve been throwing around ideas for the last few days and it’s exciting how much things are radically simplified by all this. I’m hoping that 4.0 will serve as a solid basis for many, many features over its lifetime. 4.0 won’t have all the features everyone wants, but it will enable me to finally add so many more of them!

I last reported on usage stats back in February, when Windows 7 had finally tipped the scales to become the OS used by the majority of Paint.NET users. I’m going to use a briefer format this time and just report on the OS and CPU stats so that I can spend more time on another blog entry (in other words, no table and no language stats this time).

Windows 7 has hit 55.75% (previously 44.74%), XP has fallen to 32.99% (previously 39.57%), and Vista has tumbled to only 10.98% (previously 15.55). And in other good news, 64-bit has jumped all the way up to 34.26% (from 26.08%), while only 65.74% of Paint.NET users are trucking around with a 32-bit version of Windows (down from 73.92%). If you haven’t migrated to 64-bit yet, I highly recommend doing so! And also be sure to install the latest service pack for the version of Windows that you’re using (SP3 for XP, SP2 for Vista, and SP1 for Win7).

Some other stats that I haven’t included before:

  • Of the Windows 7 users, 37.70% are using the “RTM” version and 62.23% have upgraded to SP1.
  • Of the Windows Vista users, 19.14% are using SP1 while 79.40% are using SP2. Only 1.40% are using RTM (possibly due in part to having dropped RTM support awhile ago).
  • Of the Windows XP users, 2.63% are using SP2 while 97.34% are using SP3 (possibly due in part to having dropped SP2 support awhile ago).
  • About 98% of the user base is using Paint.NET v3.5.3 or newer.

As promised, here’s the update that fixes the shortcut keys for View –> Actual Size.

And as usual, you can either use the built-in updater from the Utilities menu, or you can download and install directly from the website: http://www.getpaint.net/. There’s no need to worry about removing the old version; that is all handled automatically.

The official changelist:

  • Fixed: Ctrl+Shift+A now works again as a shortcut for View -> Actual Size (broken in 3.5.9)
  • Fixed: Ctrl+0 still works for View -> Actual Size even if 10 or more images are open (broken in 3.5.9, it would switch to the 10th image).

Enjoy!

Update: (October 4th, 4:30pm PST) Download.com is no longer doing this (at least, not with Paint.NET).

I just noticed that download.com updated their listing of Paint.NET to version 3.5.9. Neat. Except that now they’re wrapping it in their download manager which tries to get you to install the “StartNow” toolbar. What a bunch of **** (insert your favorite 4-letter word).

I’ve already sent them an e-mail telling them that this is a violation of the Paint.NET license and to either cease this, or to remove Paint.NET from their site.

In the meantime, I obviously recommend not downloading Paint.NET from download.com.

More info on this despicable practice: Download.com wraps downloads in bloatware, lies about motivations (extremetech.com)

Update: (October 4th, 9:30am PST) I received a reply from them at 2am stating they had removed the download manager from their listing of Paint.NET v3.5.9. However, as of 9am, they have not actually done this. If you go to the Paint.NET v3.5.9 listing on download.com, it still has their spammy download manager attached to it!

Yesterday, I released Paint.NET v3.5.9, which has some good bug fixes, as well as adding back the Korean translation and mapping Ctrl+0 to the View –> Actual Size command. Ctrl+0 is now the “primary” shortcut key listed in the View menu. As it turns out, I forgot to do one small thing. You see, Ctrl+1 through Ctrl+9 are shortcut keys for “switch to image 1” through “switch to image 9.”

As it turns out, I forgot to deactivate Ctrl+0 as a shortcut key for “switch to image 10.” So, if you have 10 or more images open, Ctrl+0 will not work for View –> Actual Size. Thankfully, it does not perform both actions in this case, and you can still use Ctrl+Shift+A or Ctrl+Alt+0. I will be rolling out an update called, you guessed it, Paint.NET v3.5.10, to fix this.

In the meantime, my recommendation is to install v3.5.9. However, if you’re planning on a deployment and would like to save some effort, I advise you to hold off until v3.5.10 is available. It should be next weekend (October 8th/9th) or the following (October 15th/16th). I may take the opportunity to fix another bug if any are found, or maybe to bundle in a performance improvement or two.

For some reason, I didn’t even think of this until precisely 5 minutes after I had finished releasing v3.5.9. Why is it that things always seem to work out like that? Smile

As usual, you can either use the built-in updater from the Utilities menu, or you can download and install directly from the website: http://www.getpaint.net/. There’s no need to worry about removing the old version; that is all handled automatically.

Here’s the changes from v3.5.8:

  • Improved: The "Auto-detect" bit-depth setting for PNG, BMP, and TGA now also determines which bit-depth to use based on which one produces the smallest file size, as well as which ones can save the image without losing fidelity.
  • Improved: You can now use Ctrl+0 as a shortcut key for View -> Actual Size, in addition to Ctrl+Shift+A and Ctrl+Alt+0.
  • Fixed: Some text in the DirectDraw Surface (DDS) Save Configuration UI was not being loaded.
  • Fixed: Some DirectDraw Surface (DDS) files authored with other software (e.g. Unreal 2004) could not be loaded.
  • Fixed: In some rare circumstances, clicking on the Save button in the toolbar would crash.
  • Fixed: The Korean translation has been added back in, with the help of Bing machine translation to cover the few remaining strings that were untranslated.

Also, fixes since the 3.5.9 Beta include a few compatibility issues with plugins.

Enjoy!

This is a minor update to Paint.NET v3.5. It has some bug fixes, and also adds back in the Korean translation (thanks to Bing translator).

As usual, you can download it directly from the website or you can use the built-in updater. Make sure that you enable “Also check for pre-release (beta) versions”, which you can do by going to Utility –> Check for Updates, and then clicking on the Options button.

Here’s the changes from v3.5.8:

  • Improved: The "Auto-detect" bit-depth setting for PNG, BMP, and TGA now also determines which bit-depth to use based on which one produces the smallest file size, as well as which ones can save the image without losing fidelity.
  • Improved: You can now use Ctrl+0 as a shortcut key for View -> Actual Size, in addition to Ctrl+Shift+A and Ctrl+Alt+0.
  • Fixed: Some text in the DirectDraw Surface (DDS) Save Configuration UI was not being loaded.
  • Fixed: Some DirectDraw Surface (DDS) files authored with other software (e.g. Unreal 2004) could not be loaded.
  • Fixed: In some rare circumstances, clicking on the Save button in the toolbar would crash.
  • Fixed: The Korean translation has been added back in, with the help of Bing machine translation to cover the few remaining strings that were untranslated.

As for paint.net 4.0, progress has been slow the last few months because I’ve been rather busy at work. I work at Microsoft in Windows on a little project called Windows Performance Analyzer (formerly known as “xperf” in some circles). The new version has a completely new UI written using WPF. Our PM, Michael Milirud, presented this at BUILD last week: http://channel9.msdn.com/Events/Speakers/michael+milirud . I highly recommend checking it out, although I’m biased of course Smile

One of the features I’m planning for 4.0 is tentatively titled “Shapes.” Right now you can only draw rectangles, ellipses, lines, and (admittedly lame) freeform polygons. Even MSPaint in Win7 has a larger shapes library. The idea I’ve got is to ship with a much larger list of built-in shapes, but also to allow users to install new shapes (from the forum, or their own imagination, or from wherever). The idea is to use XAML as the serialization format, and to make it compatible with WPF’s Drawings and Geometry objects. This way you can use any existing XAML editor to create the shape. (And this also means I don’t have to write an authoring tool!)

I already have the data format working – I have my own Geometry classes that wrap Direct2D, and was able to get them to roundtrip using XamlServices from System.Xaml.dll (I was quite amazed really – it worked the first time!). Since WPF and Direct2D use the same object model for their Geometry classes, it should be no trouble to auto-convert from WPF XAML files, thus eliminating the need for little annoying conversion utilities. So a XAML shape could use either the Paint.NET Geometry classes, or WPF’s. Probably sitting in a ResourceDictionary. Now, Geometry objects only describe shape outlines, with no fill or stroke parameters. The next thing is to add “Drawing” support. Each element of a Drawing contains a Geometry*, but also includes details about stroke color, stroke thickness, and a fill brush. Imaging you have a vector version of a company logo, with specific brush stroke and fill colors. You could plug that into Paint.NET and use it to draw. And yes, you’d be able to use solid colors as well as gradient brushes.

* In WPF, a Drawing can also contain other elements such as bitmaps or text or whatever.

I just recently finished a whole lot of code whereby the canvas in paint.net 4.0 is now rendering via Direct2D. This has numerous advantages: layer composition is finally off the UI thread, it’s faster, CPU usage is lower, and I even added back those darned “dancing ants” that everyone seems to be so fond of from ye ol’ version 3.36, etc. etc. Not to mention the next set of changes … which I won’t mention yet. Ok I will: I’m planning to move all rendering off the UI thread. I’m maybe 1/4th of the way there since layer composition is off of it, and now I have to get all of the tools to do the same (much of the rendering code in paint.net is multithreaded, but it still forks/joins/blocks on the UI thread, so the logic is still inherently single threaded).

Anyway.

It’s neat when ATI and NVIDIA keep one-upping each other by adding fancy antialiasing and other gizmo settings into their control panels. It’s fun to download a new game and then shoehorn whatever antialiasing and anisotropic filtering settings you want to use with it. Booting up the original Deus Ex and forcing it to use 131,072x antialiasing is a treat (for about 5 minutes anyway). However, it would be smart if they would not apply those settings to regular desktop applications.

Case in point. If you try to turn on “morphological filtering” on an ATI Radeon 5850 with the absolutely latest drivers downloaded just yesterday from their website: (click for full size)

I wish I had written a filter that made the text look that way. It’s supposed to be normal Calibri, but this “morphological filtering” makes the canvas look like an oil painting that’s being rained on. It’s fully animated and very trippy (hmm, I have some friends who may be interested in this). This also happens with other Direct2D-based applications, such as Windows Live Messenger 2011, so I’m quite sure it’s not some bit of wonky code I checked into paint.net 4.0.

So, here’s my plea to ATI: please fix your drivers so that this doesn’t happen with Direct2D-based desktop applications. I don’t want to add code that detects an ATI video card, and then snoops the registry or whatever you store configuration into, detects this morpho-whatever thing, and then forces software rendering. From what I can tell, all this setting really does is make games blurry and slow, and it’s worse than that “Quincunx” stuff that NVIDIA had on the GeForce 3/4.

And NVIDIA, I’m keeping my eye on you too …

Anyway this Radeon has got to go. My workstation had a GeForce GTX 260 Core 216 in it, and I’ll be reverting to it. I upgraded my gaming PC from the Radeon 5850 to a GeForce GTX 570, and figured why not put the Radeon in the workstation? (which has a Core i7-980x at 4.0GHz btw, mwahaha) The benchmarks show it’s quite a bit faster. Unfortunately this card is just glitchy, for additional reasons other than the LSD setting shown above. I don’t know if it’s because I’m running dual monitors in a weird configuration (one landscape, one portrait), or if I’m just unlucky. It never had any problems playing games. Oh well.

If you’re using the latest version of .NET Reflector, 7.1, then you are probably experiencing much slower startup times compared to 6.x and earlier. On my system, it takes about 15 seconds for it to start! RedGate is aware of the problem and are burning the midnight oil in order to fix it.

I did some quick profiling using xperf, aka Windows Performance Analyzer, and found an issue that accounted for 7 seconds of CPU usage on a Xeon W3520 (basically a Core i7-920 2.66GHz). Thankfully, no update is needed to work around the issue while RedGate comes up with an official solution. I’ve posted my findings and the quick fix to the RedGate forum. Try it out, and let the RedGate guys know if it helps! There is still much work to be done here, but the quick fix I’ve posted will hopefully provide relief in the meantime.

tl;dr … If you install the Reflector assemblies to the GAC using gacutil.exe, and run NGEN on them, then the startup time is significantly improved.

Follow

Get every new post delivered to your Inbox.

Join 231 other followers