I've been learning SketchUp. It's... pretty good. I gave it a try a few years ago with version 6 and I found it intolerable, but version 8 seems to be much improved.

I'm in the process of making a ridiculously detailed 3D model of DNA Lounge, based on our blueprints. The end goal is to end up with some imagery that looks like this amazing poster of The Haçienda, a defunct nightclub that I have fetishized since I was a kid.

I'm finding that this project is tweaking the same kind of zoned-out obsessive parts of my brain that I get building "real" models, like that orithopter and zeppelin, even though it's not physical. It's fiddly and trance-like and hours go by.

Anyway, about SketchUp:

  • Watching the first four tutorial videos was a big help, because there's a lot about the UI that is non-intuitive and non-discoverable. CAD software UIs are notoriously awful, and SketchUp seems better than ones I've tried before, but it's still pretty weird.

    In many ways it's a lot like Illustrator. So much so, in fact, that almost every way in which it is unlike Illustrator annoys me. Now, don't get me wrong, Illustrator's UI is also non-intuitive and non-discoverable. Illustrator has a learning curve like a plumb line. It's just that I've already put in the hours and hours and hours to learn it, and now I'm learning something new that's almost-but-not-quite. To put it another way: Illustrator is the Qwerty of drawing UIs. Why are you coming at me with this Dvorak nonsense?

    The most frustrating thing about it is that it wants to "merge geometry" all the time, which means, you draw a line or a face and it snaps the endpoints to existing endpoints. Illustrator does this too. It's familiar. Here's where SketchUp fucks that up:

    1. When you move an object and SketchUp snaps two points together, they become the same point. You can no longer ever separate those two objects.
    2. There's no way to turn off "Snap to Point".
    3. There's no way to turn on "Snap to Grid".
    4. If an object is invisible, the mouse doesn't snap to it, but it will merge with it!

    That last part is so absurd I couldn't actually believe it at first, but really. Look:

    That is truly an error message for the record books. Has anyone ever said, gosh, I sure would like this random click I just made to be affected by something that I can't possibly see?

  • So it turns out that there are two things you have to do to avoid this: First, group everything. Things inside groups will snap to, but not magically-merge-with, things in other groups. So basically after you've laid down your second endpoint, make it a group if you want anything to behave sensibly.

    The second is that, as far as I can tell, Layers are completely useless. In every other program that I've ever used that had a concept of "layers", those are grouping objects that sit one level above other kinds of groups, from which you can control visibility, editability, etc. Not so in SketchUp. "What layer am I on" is a property of a vertex. They don't group things at all. I think a single rectangle can end up with it's corner points on four different layers. Also, layers control only visibility. They're a bulk way to hide vertexes, that's it.

  • The other thing I find really frustrating about the UI is how often you have to switch tools, and how much that slows things down. In Illustrator, if you're in the "Selection" or "Direct Selection" tool you can not only select things but move them. In SketchUp, you constantly have to switch back and forth between the Select and Move tools, or Select and anything else.

    Illustrator has the thing where you hold down the space bar and are temporarily in the "Hand" tool for scrolling. SketchUp uses middle-button and Option-middle-button for that, but it's kind of awful because -- something that I have recently learned -- dragging my mouse while holding down the mouse wheel hurts after a while. I only just learned this because I've never before used an app that made me do that. Now I have a bruised little divot in my index finger.

  • I find that the rectangle tool just about never does what I want, unless I'm aiming at two existing points on an already-coplanar polygon. And even then, not always. The line tool plus inferencing seems to work better, mostly? Inferencing (the snap-to-horizontally-aligned points lines it draws) gets confused a lot, too, especially by background clutter. I keep rotating things to get a view of the "sky" behind me when I'm drawing.

  • The Orbit tool is confusing, because it is not the kind of "virtual trackball" type of behavior I've come to expect from other programs. It does not orbit around the center of the screen, or even around the X/Y position of the mouse: it seems to orbit around the object under the mouse (or possibly some point on that object's interior?) This means that if you slip, it's easy to end up orbiting around some object that is miles away, and moving just a few pixels whips you out to the Wild Blue Yonder and you have a very, very long walk back.

    Likewise, the speed of zooming in and out varies depending on what's under the mouse. This means that if you back up and slide inside an object that was behind you, you have to roll that mouse wheel for a mile to get back. Seriously, 3 clicks backward to fall into the "wall whiteout", then 50 rolls forward to return. So frustrating.

  • Given how picky everything about SketchUp is about things being co-planar or otherwise aligned, I'm amazed that there are no "Horizontal Align" and "Vertical Align" tools, e.g., multi-select some vertexes and say "I expect these to all be lined up, make it so." (The "Move" and "Scale" tools plus inferencing are almost this, but they only work on objects, not individual vertexes, and you have to do it one line at a time.)

  • It is incredibly easy to end up with two points that look like they're the same point, but that are not. I guess it happens when you have a lock on a point, but twitch just slightly when you click? You end up with a second point next to the first point, but so close to it that they are visually identical on the screen. You only notice this later when you can't close your object and fill the surface, and then you're on a mad hunt for the broken point. You can only see them if you zoom in absolutely as far as you can, and even then, you can't really get close to them or you run into the camera clipping plane! Maybe these points are so close together that floating-point rounding errors are actually coming into play? Look at this shit:

    Is something wrong here? I can't tell.

    "Enhance! Enhance!"

    Oh, too bad, you're out of zoom. Hope you have good eyes and a steady hand.

  • But even worse, once you have identified that you have two points that didn't snap together but should have, you have little recourse. Basically you have to delete one or both of them and re-draw everything that was connected to them. There's no way to just lasso them or something and say "merge these". At least, I've had no luck picking one up and dropping it on to another. Even if you do that, you end up with flat surfaces that are subtly but perceptibly buckled diagonally, because inevitably you picked the wrong point and now your walls aren't coplanar, or aren't at right angles. (See also "no 'Snap to Grid', no 'Align Vertically'.)

  • It's so frustrating that I can't multi-select faces to use the push-pull tool on them all at once. Yes, you can double-click each face in turn to repeat the last push-pull distance, but I find that half the time when I do that it pushes instead of pulls, or vice versa.

  • Sometimes the Arc tool will lay down an arc that is exactly tangential to both edges of the right angle I'm trying to turn into a curve. I've done it by accident several times. Never on purpose.

    Related: once you've used Follow Me to turn a line into a tube, you can't ever reshape it in any useful way. Hope you saved a copy of the original path!

  • Importing DXF files is a pain in the ass. The only way I could make it work was to use a trial version of SketchUp 6 Pro. The importer no longer works in SketchUp 7 or 8.

    It is shameful to ship software using a new file format that does not have import and export of the existing decades-old formats as a basic feature.

Tags: , , , , ,

back-buffering performance on iOS with Quartz

Dear Lazyweb, how do you efficiently buffer your drawing to the screen on iOS?

You've got an app that accumulates 2D graphics onto a canvas, without recomputing the scene every time. You need to blit that canvas onto the screen every time drawRect is called. How?

I've done some searching and it seems that lots of people have asked this question, but I haven't found any answers that work. Several vague theories, no real explanations.


It turns out that I can't get better than like 4 frames per second when running on an iPad 3 (with the 2048×1536 screen, even if I only draw at 1024×768). Oddly, on an iPad 1, with a native 1024×768 screen I'm getting 15 FPS, which is still super slow, but I don't know why it's faster.

This is for non-OpenGL stuff. The GL stuff is fine.

On a really simple saver like Julia I'm spending 70% of my time actually rendering into the backbuffer, just drawing dots, and 25% of my time inside CGContextDrawImage. This is crazy, because on MacOS and in the simulator this takes basically zero time. CPU utilization is 0.1% instead of 60+%.

The X11 savers typically draw a few lines each frame and expect them to accumulate. This is no problem on MacOS because the CGContext of a given NSView never changes, and you can keep adding to it. But on iOS, you can't depend on UIGraphicsGetCurrentContext() having the same value across calls to drawRect. What's worse is that in practice, you can't depend on it retaining its bits. Among other things, it gets cleared by orientation changes, and there's double-buffering going on so you get different buffers on alternate frames. Even when it doesn't clear on you, you get constant flicker.

So, that means that if your app does not fully re-compute its scene every frame, you need to draw to an off-screen buffer and then splat that on the screen at the end. Annoying, but not too unusual a situation.

So I was doing this:

  CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
  CGContextRef backbuffer = CGBitmapContextCreate (NULL, w, h, 8, w*4, cs, kCGImageAlphaPremultipliedLast);
  CGColorSpaceRelease (cs);

- (void)drawRect:(NSRect)rect
  UIGraphicsPushContext (backbuffer);
  // ...draw more stuff into backbuffer...

  CGContextRef cgc = UIGraphicsGetCurrentContext();
  CGContextConcatCTM (cgc, t);   // rotate for orientation

  CGImageRef img = CGBitmapContextCreateImage (backbuffer);
  CGContextDrawImage (cgc, target, img);
  CGImageRelease (img);

When the orientation changes, the underlying code sees e.g. a 640x480 screen "resize" into 480x640 and I re-create the back buffer at the new size and copy what I can of the old one's bits to the new one:

  CGImageRef img = CGBitmapContextCreateImage (old);
  CGContextDrawImage (backbuffer, rect, img);
  CGImageRelease (img);
  CGContextRelease (old);

So, as I said, that is inexplicably slow on Retina iPhones and non-retina iPads, and intolerably slow on Retina iPads even when the size of the backbuffer is half the real size of the screen (that is, the size of the screen on an iPad 1).

So then I tried this:

Lots of people seemed so say that if you wave the chicken of NSLayer at your CGBitmapContext code, things magically get better:

  // Init this from under the first call to drawRect, else UIGraphicsGetCurrentContext() is null
  CGLayerRef backbuffer = CGLayerCreateWithContext (cgc, backbuffer_size, NULL);

To copy it:

  CGContextDrawLayerInRect (CGLayerGetContext (backbuffer), rect, old);
  CGLayerRelease (old);

And drawing:

  CGContextDrawLayerInRect (cgc, target, backbuffer);

That improved things by like, 10%, but not enough to matter.

Bizarrely, performance is worse by about 30% in landscape than portrait, and as far as I can tell, the only difference there is whether I appended a non-zero rotation to the CTM when copying.

Any ideas?

If you have a functional iOS development environment and want to play around with it, the code is in xscreensaver/OSX/XScreenSaverView.m. Email me and I'll send you my latest version of that file with my recent attempts. In the app, click on the options arrow on the "Julia" line and turn on "Show frame rate" to see the numbers.

Tags: , , , , ,