A life twice-lived ...

The Fatal Flaw In osDraw

Stepping past the capabilitites of its progenitor - OpenSimulator offers script functions to draw directly on the surface of primitives.

Unfortunately, because of design limits, its not practically useful for more than casual doodles.

Customized interfaces for DetectedTouch functions are just not practical due to the following flaw.

First, An Example

In-world, rez a prim and drop in the following example: http://opensimulator.org/wiki/OsMovePen (opensimulator)

You should see the surface of your prim turn white with skewed diagonal blue stripes.

The Flaw Exposed

Reading the script, you will see a number of:

CommandList = osMovePen(CommandList, x,y )

or

CommandList = osDrawLine(CommandList,x,y);

The LSL functions are creating “command strings” in the CommandList variable.

Once you have assembled the list of commands, you trigger an osSetDynamicTextureData() call with the CommandList.

OpenSim assembles the custom texture with your drawing and slaps it on the prim and triggers the view update to your viewer.

Why Is This A Flaw?

Two reasons.

Brick Wall 1

OpenSim does not composite a series of commands onto the same texture until told to explicitly clear the “canvas”.

EACH call to osSetDynamicTextureData() which is needed to see the current draw commands… blasts away the old texture and creates a fresh one.

The world wide web had the same bug - a refresh of a web browser cleared the page.

It took a JavaScript and browser change to enable AJAX to draw on the “web page” without triggering a wipe before refresh to enable “the web 2.0” we all know and loathe today.

And why is this a problem here?

LSL has limits on the sizes of things it can manage at a single time… such as that CommandList string variable.

Once you pass the command list and trigger the draw - anything you do will be a redraw unless you keep an ever-growing CommandList of all operations to apply to the canvas.

So the maximum list of things you can draw in a single request is solely dependent on the max string size, which means every bytes of that string counts!

Brick Wall 2

Unfortunately, the maximum number of useful commands you CAN get into a single limited-string-length command list is rather small.

Why? Because each command is a plain text mess almost as long as the LSL call that “created” it.

Curious about how “compact” the commandlist tokens were compared to the wastefully long OSSL calls that created them - I dumped out the command list and was horrified.

PenSize 3; PenColor Blue; MoveTo 255,0;LineTo 0,20; MoveTo 255,20;
LineTo 0,40; MoveTo 255,40;LineTo 0,60; MoveTo 255,60;LineTo 0,80;
MoveTo 255,80; LineTo 0,100; MoveTo 255,100;LineTo 0,120; MoveTo 255,120;
LineTo 0,140; MoveTo 255,140;LineTo 0,160; MoveTo 255,160;LineTo 0,180;
MoveTo 255,180;LineTo 0,200; MoveTo 255,200;LineTo 0,220; MoveTo 255,220;
LineTo 0,240; MoveTo 255,240;LineTo 0,260; 

So, this mess of a single call:

CommandList = osSetPenSize( CommandList, 3 );

Created this mess of a “command”:

PenSize 3;

Not much savings compared to a single byte tokencode followed by zero or more token-specfic variables that are interpreted into operations silently inside OpenSim.

Does the CommandList content passed to osSetDynamicTextureData need to be human-readable too? Not really, when you've already wasted so much notecard script in the LSL on the descriptive-named OSSL calls.

Possible Improvements?

A core OpenSim dev may be able to make changes like these which might help.

The first change would be to implement a new OSSL call to trigger when you want to clear the dynamic texture and start a new one.

The second change is trickier - maintaining the current command “stack” and dynamic texture resource on the server side to enable “server side texture compositing” over SEVERAL osSetDynamicTextureData().

The third change would be to enable a second dynamic texture to draw on while the first is being displayed, then trigger the swap.

All very tricky to get right in the existing scene order of operations.

Even making these changes should include looking at how to avoid the database hits from rapid or continual texture changes on an object.

And the “command language” should definitely be compressed down to single character command tokens followed by binary encoded integer data rather than a human-readable system.

Why It Won't Be Fixed

Nobody wants it.

Even if the entire mechanism is a cool extension of the possibilities of the Linden and OpenSim protocols, the only way the changes above happen is a skilled C# dev who understands OpenSim internals takes it on and patches things without clobbering ANYTHING going on in several key portions of OpenSim.

That person ain't me, and the people who could do it have far too many other priorities and bug fixes.

And the suggestions above may not really fix things - they feel like a stopgap “extension”.

A true fix means far more engineering.

But limits in the viewers, limits in the protocols, limits in existing LSL types like string lengths, server side loading, security issues, and unavoidable politics utterly prevent this.

The current mechanism “renders” the texture data on the server side, applies it to the scene object (triggering a database update also) and then the server has to ship the texture bytes down to the viewer in the form of an object updated texture message.

Rapid drawing changes to a prim are wasteful in several areas such as the server side “render” step, the database hit to store the new texture asset, the database hit again to update the existing prim asset, and then the network bandwidth hit to ship the texture data.

The nicer the picture you draw, the bigger the hit in the render and network shipping step… but no worries - with a limited length string you can't draw too much before you run out of room.

The “fix” to give an object creator inworld the ability to dynamically scribble on prims efficiently would take a series of large changes to the viewer and the server.

Instead of server side rendering, it would have to be pushed to the client.

Instead of pushing a rendered texture down, the “draw commands” would have to be pushed down.

Then the client side would render it - which causes concerns for shared user experience as well as different viewers update differently.

The pitfalls, perils, and politics of the Linden and OpenSim environments pretty much guarantee the work to salvage the concept won't happen.

And really, by the time all of that is done - its not remotely the same thing that exists now.

We're stuck with what we have, which is useful in a few cases, but not for anything large scale.

Which is a pity, since it's a step in the direction that these platforms need to go to survive - putting development capabilities in the hands of content developers.

So, why did I write this?

To capture it as it occurred to me, and to note it down in case there is some day where someone does decide to dig into this “draw on a prim” subsystem and turn it into a powerfully useful feature it could be.

Until then - its an interesting toy that can be occasionally useful in a very limited sense.

Someone wanted a basic doodle board, and got it - accidentally tripping over a far greater need which remains unfulfilled - the need to be able to control pixel by pixel what appears on a prim to any level of detail desired.

In a way this all reminds me of the promise and disappointment of RipScrip which would actually be worth looking at as the “compact” or “tokenized” form of the command strings sent to the server or render, or sent down to the client to render locally.

Oh well - there's always far more possibilities than skill and possibility allow jumping into…

UPDATE

Maybe I am totally wrong? Wouldn't be the first time.

Vegaslon Plutonian pointed out the osSetDynamicTextureDataBlend() call which looks to stamp more draw operations onto the texture already on the face.

Maybe that sort of helps with one of the brick walls.

But I can still see constant string length limits OR operations so frequent that they hammer to database updating the primitive as well as hog up more and more bandwidth.

I still don't feel confident I could remotely get close to achieving the idea I had this morning with the calls as they are, even with blending.

But maybe it does merit a bit more time-wasting investigation.

Still highly skeptical and not at all sure if I should bother…

FINAL UPDATE

Okay false alarm - I was right - the osSetDynamicTextureDataBlend() functions don't actually work when tested.

It seems that still smash over the previous texture.

But, it looks like at least someone was working on taking a sledge hammer to brick wall 1.

It's a start at least… but still a long way between here and there.