Thursday, February 17, 2005

Change events are special

I just spent a few hours chasing a very mysterious bug, where an event handler was properly called the first time, but never again. This was a rather unspecial on:in: handler:
<on: foo in: bar>
After initialization it was properly registered in the event map, but at some point the event map entry just vanished.

There also was another on: handler which did some stuff whenever bar changed. To "call" that stuff, we just signaled a barChanged event somewhere else:
self signal: #barChanged.
It turned out this innocuously looking line was responsible for the trashed on:in: handler! What happened?

Well, an on:in: handler must always be registered to the object that currently is occupying the bar field. But what if bar changes? Then we must unregister the foo handler in the old bar, and re-register it with the new bar. That's why the system installs a barChanged handler behind the scenes, which normally receives the new and old values of bar as arguments.

Now, can you see what happened? If we just signal #barChanged, then the arguments are nil! The current handler will be unregistered, but no new handler gets registered instead!

Lesson learned:
Do not signal a change event by hand!
Of course, we could have constructed a proper change signal like this:
self signalChanged: #barChanged from: bar to: bar
but why bother? It's much cleaner to not misuse the system-defined change event. I ended up defining another event, #barModified with a proper handler, which is signaled from both the #barChanged handler and other places.

Wednesday, February 09, 2005

About Scripts

We were looking into another Croquet performance problem the other day so we fired up a message tally (world menu - debug - start MessageTally). Curiously enough, 70 percent was taken by ScriptScheduler>>runActiveScripts! Unfortunately, the tally did not further differentiate this item.

But what are scripts, anyway? Everyone knows that Smalltalk is all about objects and messages, so what the heck are scripts?

Well, Croquet and Tweak are not just using Smalltalk as you know it (and the underlying Squeak still is pretty much vanilla Smalltalk-80), but instead improve on it by implementing a new enriched object system. There still are objects (the entities of the system) and messages (their means of communication). But where in Smalltalk methods are invoked synchronously by a message send, we now have asynchronous method invocations as well, which are called "scripts".

Synchronous in this context means that the sender sends a message, which invokes a method in the receiver that is immediately processed. Only after finishing the method the control returns to the sender. In contrast, an asynchronous send only schedules a method invocation for later processing, control is immediately returned to the sender. Here is an example (#perform: is synchronous, #startScript: asynchronous):
Transcript perform: #show: withArguments: #('1').
Transcript startScript: #show: withArguments: #('2').
Transcript perform: #show: withArguments: #('3').
If you execute this snippet from inside Tweak, it prints "132". Outside of it just "13" is printed because the second invocation is only started but never executed. There is no ScriptScheduler running to manage the execution of scripts. Inside Tweak there is one, and Croquet does it in a similar fashion.

Historically the term script in Squeak comes from the Etoys environment, where kids make objects and specify their behavior using scripts. One can have multiple objects and multiple scripts for each. All these scripts are running in parallel, at least from the user's point of view. Surprisingly enough kids don't have any problem with that, whereas concurrency normally is a hard problem even for seasoned programmers.

One reason why the parallel execution of interacting scripts is no problem in practice is the underlying scheduling policy: Scripts are never interrupted by other scripts. Other scripts are only executed when your script finished, or when it gives up control explicitly by waiting for an event. You can write your script almost as if it was the only process on the machine. Here's an example script:
| p |
p := CRectanglePlayer open.
1 to: 500 do: [:i |
    p x: i.
    self waitTick].
Looks just like how we did animations back when, right? Move a bit, wait for vsync, repeat. But the best part is this: While the loop is executing, you can perfectly well use anything else in the world! It's running "in parallel" to everything else on screen.

Another advantage is that you can easily have thread-local storage that way. Say, on mouse click you want to change an object's color to red, and on mouse up set it back to what the color was before. In most GUI frameworks you would have to implement both a mouse down and mouse up handler, and use an instance variable to store the previous color on mouse down. Not so in Tweak. The mouse down handler would look like this:
| oldColor |
oldColor := self color.
self color: Color red.
self waitUntil: #mouseUp.
self color: oldColor.
Much cleaner, in my book. Anyway, to get back to the original problem: So scripts are run as separate processes, but MessageTally's spyOn: method normally only takes samples in the process it was run in, which is the main UI process. Fortunately, there is a new method spyOnScript: which does the Right Thing. When starting the message tally from the Tweak project builder's debug menu, this new method is used, and indeed, we got a much more meaningful tally, and could spot the performance hog immediately.

Tuesday, February 08, 2005

Lend me a Hand

The last Tweak updates broke our Tweak-based Croquet application, objects did not respond to clicks anymore. I first suspected changes in Croquet, but the latest updates there didn't look suspicious. But neither did the last couple of Tweak updates. Investigating further and sprinkling debug output here and there I discovered that pointer events were offset. Moving the Croquet window to the upper left corner confirmed this, all of a sudden everything worked normally. What was happening?

Well, living in a shared world isn't easy. Morphic, Croquet, and Tweak interact very closely in the TeapotMorph, which bridges between those three worlds. Without Tweak (that is, without calling initializeTweakWorld in your initializeDefaultSpace method), the TeapotMorph uses the regular Morphic event dispatching mechanism which leads to calling the event handling methods keyDown:, mouseDown: etc. which in turn dispatch the events to the activeCamera, that is, into the Croquet world.

When using the Tweak overlay, this does not happen, but all events are translated into Tweak events and dispatched into the Tweak world. The initializeTweakWorld method not only sets up the Tweak world, but also registers event handlers (onKeyDown, onMouseDown etc.) that are called when no Tweak object handles the event itself, like when clicking outside a Tweak window. These event handlers translate the Tweak events back into a Morphic event and call the regular non-Tweak TeapotMorph event handling methods, from where processing continues as before.

Now translating the events back and forth also involves offseting them, because Tweak uses relative coordinates (every Tweak object defines its own coordinate system) whereas Morphs use global coordinates. Translating into Tweak was still working fine, the Tweak objects were acting just as expected. However, the back translation was wrong. In fact, the event we got back already was in Morphic coordinates, so when we applied the offset again it was way off. The problem is that events were not explicitely passed around, but rather we used the active hand's last event.

The Hand in Squeak is the object that injects user interface events into the world. Its visual representation is the mouse pointer, but keyboard events come from the hand, too. Because there can be multiple hands in a world, there's a global variable ActiveHand which is set to each hand while processing events initiated by that hand. Multiple hands, you ask? Yes, there was collaboration even before Croquet in Squeak (so each user in a shared Morphic world had a separate hand), and also multiple mice can be used independently, if that is supported by the VM (I once wrote a plugin for that using the XInput extension under Linux).

Now until last week, Tweak also did set ActiveHand to its current hand while processing an event. This had some odd side effects, for example, when some code expected ActiveHand to be a HandMorph (as is the case in Morphic) or a CHandPlayer (as in Tweak). In particular, when running a Tweak overlay in Croquet, which still largely assumes a Morphic environment, this led to confusion. But we pretty much ironed out all these wrinkles.

Except for the back-translation of events coming from the Tweak world, which happens in a Tweak process, so we fully expected the active hand to be a Tweak hand in this handler. But Andreas removed all references to ActiveHand from Tweak in an otherwise innocuously looking update (comment: "Various preps for upgrading to 3.8") so we did, in fact, by referencing ActiveHand get the original Morphic event instead of the expected Tweak event.

Just removing the offset would not work because you might have or might have not loaded that update. So we need to access the active Tweak hand, but without accessing the ActiveHand global. This is easiest if you are programming in Tweak, because every CPlayer has a field aptly named hand that refers to the active hand. Outside of Tweak you must do it the hard way, which is getting the hand from the active process. I'll talk about Tweak processes in another post, this one is already getting too long.

So, to make a long story short, if you fetch Croquet updates now, everything should be working fine again. There are two new methods in TeapotMorph, morphicHand and tweakHand, that return the right sort of hand (of course, the latter returns nil if Tweak is not running). They are used in the two methods I found where it actually matters, namely bringing up the halo which needs a morphic-hand, and back-translating events, which uses the tweak-hand.

That's all for today. Happy Tweakin'!

Friday, February 04, 2005

Wednesday, February 02, 2005

Dynamic Textures

We've been wondering for a while, why screen updates are more expensive than expected in the Tweak overlay. Now I debugged into this and it turns out we're uploading the whole texture even if only a small part was changed. The relevant code is in OGLTextureManager>>uploadTexture:dirtyRect:. A partial upload using glTexSubImage2D() is only performed if the texture in use is not static.

Having found out what's going on it's easy to fix, a one-line change in TeapotMorph>>glRenderTweakCostume:on: does the trick:
texCache isStatic: false.
As simple as that - I just posted the update.