Loading ...
Sorry, an error occurred while loading the content.

This was't a SMOP

Expand Messages
  • Rob Nagler
    This wasn t a SMOP. Not even close. If you ve read my book, you know that I use SMOP to mean Subject Matter Oriented Programming. The Hacker s Dictionary
    Message 1 of 2 , Apr 30 9:32 AM
    View Source
    • 0 Attachment
      This wasn't a SMOP. Not even close. If you've read my book, you know
      that I use SMOP to mean Subject Matter Oriented Programming. The
      Hacker's Dictionary defines SMOP as a Small Matter of Programming.
      To me, they are one in the same. If you focus on the subject matter,
      it truly is a small matter of programming. Way too much software is
      written with the solution in mind.

      I'm beat. I lost four days trying to figure out how to write 150
      lines of Objective-C code. Objective-C is easy even though I hadn't
      written an Objective-C app before this week. What killed me was the
      fact that all the examples on developer.apple.com are wrong, and
      worse, they're badly written. I'm happy I finally figured out my
      little 150 line program. Apologies to the other bivions who had to
      listen to me rant all week. Now it's your turn. :-)

      Here's the correct code that had me stumped:

      GLint h = height;
      while (--h > 0) {
      bcopy(&data[h * bytesPerRow], &flipped[(height - h) * bytesPerRow], bytesPerRow);
      }
      glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
      glPixelStorei(GL_UNPACK_ROW_LENGTH, bytesPerRow / (bitsPerPixel / 8));
      glDrawPixels(width, height, format, GL_UNSIGNED_BYTE, flipped);

      There's other code that was easy. For example, I was easily able to
      go full screen and hide the mouse with this:

      if (CGCaptureAllDisplays() != CGDisplayNoErr)
      return nil;
      [ctx setFullScreen];
      [ctx makeCurrentContext];
      CGDisplayHideCursor(kCGDirectMainDisplay);
      CGAssociateMouseAndMouseCursorPosition(FALSE);

      Yes, this list is supposed to be about Perl, but it's mostly about XP,
      and XP is about code. There are lessons to be learned in reading
      other people's code, and that's what this message is about.

      Fail Fast. Never assume. That's true in *any* application. Code
      that fails slowly is bad. That's the first problem with the Apple
      examples. Here's the bit that bit me:

      // Upload the texture bitmap. (We assume it has power-of-2 dimensions.)

      When we assume at bivio, we write it big, e.g.

      // ASSUME: Image has power-of-2 dimensions

      This tells the reader that the coder has been lazy, and all bets are
      off. All Apple demo apps assume that the images they load are
      power-of-2 dimensions. If you load something else in them, they look
      like a 1950s TV set gone haywire, or in my case, they simply don't
      display anything. Here's the full context:

      // Upload the texture bitmap. (We assume it has power-of-2 dimensions.)
      width = [self pixelsWide];
      height = [self pixelsHigh];
      hasAlpha = [self hasAlpha];
      bitmapData = [self bitmapData];
      format = hasAlpha ? GL_RGBA : GL_RGB;
      glTexImage2D( GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, bitmapData );

      Apart from being unnecessarily imperative, and therefore unnecessarily
      verbose, it is very bad code. If the image does not a power-of-2
      dimensions, the call to glTexImage2D fails, and nothing is displayed.

      My mistake was to trust the world. This code is buried four levels
      down in a simple application that's called NSOpenGLFullScreen. The
      demo app coder thought this was too boring of a problem so instead of
      writing a correct application that displays a single image full
      screen, it shows a rotating earth that you can title, pan, and adjust
      the lighting on. The original subject matter is lost in a mess of 500
      lines of code packaged in four classes. Too bad for me.

      My optimism about the simplicity of the problem cost me four days.
      I got caught up in believing that "texturing" was the right solution
      to drawing an image in OpenGL. I got lost understanding how to
      texture a rectangle. The example uses a sphere, which wouldn't work
      for my problem, or for most problems. Indeed, there was another
      example that shows a movie on a teapot. That's very nice, but it's
      not something most people really want to do. Indeed, all I wanted to
      do was put a simple image on the screen, right side up.

      The nice thing about choosing the Earth is that you have to rotate it
      every which way:

      glRotatef( rollAngle, 1.0, 0.0, 0.0 );
      glRotatef( -23.45, 0.0, 0.0, 1.0 ); // Earth's axial tilt is 23.45 degrees from the plane of the ecliptic.
      glRotatef( animationPhase * 360.0, 0.0, 1.0, 0.0 );
      glRotatef( 90.0, 1.0, 0.0, 0.0 );

      Cohesion is the name of the game in programming. The more cohesive
      your code, the better it is. 23.45 is not cohesive. Indeed, it's
      stupid. On a 1280x1024 screen, it's .45 is invisible to the naked
      eye. Moreover, this mess of code confuses the reader, because it
      turns out that with OpenGL images are rendered upside-down if you
      don't flip them. No amount of rotation will get it right. However,
      I'm not a graphics guru, and I thought somehow this magical rotation
      also corrected the flipping problem. The demo coder failed to mention
      this minor fact.

      Coupling should be loose. You should be able to call a routine
      setFlipped, and it should flip something. The NSImage class has such
      a method, and even an isFlipped method. How orthogonal! How wrong of
      me to assume it actually does anything.

      If you set flag to YES and then lock focus and draw into the
      image, the content you draw is cached in the inverted (flipped)
      orientation. Changing the value for flag does not affect the
      orientation of the cached image.

      Even after calling "recache". The image isn't flipped. Andrew
      Platzer writes:

      [NSImage setFlipped:] has inconsistent behaviour depending on
      whether it's cached or not. Unfortunately, it can't be changed
      since the existing apps depend on it's behaviour. If you need to
      draw an image in a flipped coordinate system, instead, use
      NSAffineTransform to undo the flipping and avoid -[NSImage
      setFlipped:].

      Here's the code Andrew recommends:

      NSGraphicsContext saveGraphicsState]
      NSAffineTransform *transform = [NSAffineTransform transform]
      [transform translateXBy:point.x yBy:point.y + [image size].height];
      [transform scaleXBy:1.0 yBy:-1.0];
      [transform set];
      ... draw image @ (0,0)...
      [NSGraphicsContext restoreGraphicsState]

      Unfortunately, there are some floating point numbers in the above, and
      OpenGL does not guarantee that your image won't be mucked with other
      than flipping. I'll repeat my solution:

      while (--h > 0) {
      bcopy(&data[h * bytesPerRow], &flipped[(height - h) * bytesPerRow], bytesPerRow);
      }

      It's likely that this solution will work on any image, and it can be
      written to the full screen. Remember that's my problem, and
      NSGraphicsContext can't be full screen. In order to use Andrew's code
      on the full screen you have to use a magical class CGGLContext, which
      gives you a CGContext, which is not an NSGraphicsContext, and
      moreover, the CGGLContext is not documented at all and there are no
      examples.

      Tests. Probably the hardest thing to do when writing code is getting
      it right. That's why XPers document their assumptions in tests. The
      NSOpenGLFullScreen code does not come with tests. Nor do any of the
      Apple demo apps. That's why they are all wrong. Here's one of the
      biggest errors:

      // Set memory alignment parameters for unpacking the bitmap.
      glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );

      Well, this is wrong. The magic number is 4, not 1. If you have an
      image that has a power-of-two dimensions, you could put any unpack
      alignment in there, and it will be fine. Alignmments are powers-of-2,
      because they are about low-level computer bits that most programmers
      shouldn't have to deal with. If the image is a power-of-two, it has
      no alignment problems. It's already correctly aligned, always, no
      matter what any of the other parameters are. (Really, the image must
      be at least 2^2, but that's a pretty small image to be rendering.)
      All Apple examples I could find, have GL_UNPACK_ALIGNMENT as 1, and
      they are wrong. The demo images they use are all power-of-2
      dimensions, and the worst part is they don't mention this assumption.

      Most old-time programmers such as myself joke about SMOPs. I was
      caught by this one. I thought it was just a SMOP. It wasn't. I went
      down a zillion wrong paths. I didn't focus on my first problem: the
      images weren't showing up. Yes, I'm pissed at the Apple examples, but
      that's just bad code, and most code out there is awful. It's great to
      be optimistic, but I forgot that optimism is the biggest assumption of
      all.

      Moreover, I started with the assumption that it was good to start with
      demo programs, and pretty much stuck with that assumption until I
      learned that glDrawPixels is what I needed. There are no glDrawPixels
      examples, which means that I had to throw out the examples, and start
      a freshly written app (which led me down a serious diversion with the
      Xcode IDE, but that's another subject entirely). Once I did this,
      pixesl started showing up on the screen, because glDrawPixels always
      draws pixels even if the image doesn't have a power-of-2 dimensions.
      Once I saw the 1950s TV screen, I knew I had a synchronization
      problem, and I was able to do a binary search on the parameter space
      to get the image aligned right.

      My program is done, and I'm still beat so I'm taking the rest of the
      day off. That was another mistake I made. I was staying up to all
      hours of the night and morning trying to solve this problem. If I had
      walked away, like I'm doing now, I probably would have solved it
      faster.

      What I learned from all this is that XP has some pretty good
      practices. I wish I had followed them from the beginning...

      Rob
    • Matisse Enzer
      XP-related-but-not-Perl: I ve learned that there is a study underway at Stanford University on pair programming vs. solo programming. They are looking for test
      Message 2 of 2 , May 12, 2006
      View Source
      • 0 Attachment
        XP-related-but-not-Perl:

        I've learned that there is a study underway at Stanford University on
        pair
        programming vs. solo programming.

        They are looking for test subjects:

        They are looking for experienced programmers who can come to their
        lab on
        Stanford campus for a 3 - 5 hour session to do a task in Java. Some
        people
        will work in pairs and others on their own. The sessions can be
        scheduled
        on weekdays or weekends, whichever is most convenient.

        They are using Eclipse on Windows XP.

        Contact: Robert Plummer <experiment@...>
        Subject: Stanford pair programming research project
      Your message has been successfully submitted and would be delivered to recipients shortly.