The Miasma applet is a modification of the Plasma applet. All the modifications are released into the public domain.
The original Plasma applet can be found here:
http://rsb.info.nih.gov/plasma/
The original Plasma applet provides only one measurement, frames per second (fps), and one parametric variable, scale. This is not really surprising, since Plasma was written simply to illustrate a bug in an early JDK 1.2 release from Sun. Since that time, it has gradually assumed quite a different role, regardless of its original design, intent, or suitability for such a role.If an image's scale-factor were the only variable affecting animation speed, then a single scale parameter would be fine. But there are many other factors that affect animation speed, and Plasma provides no way for a Java developer to evaluate these other factors, or determine what each one's contribution is to overall speed. Knowing how each part contributes to overall speed is the only way to understand what will work best for doing animation, or even what the maximum speed is.
The basic design of Plasma may cause it to over-produce animation frames. That is, more pixels may be produced than can be shown. I say "may be" instead of "will be" because it depends on the platform, the configuration, and other variables. Over-production is especially likely if the actual image-drawing side of things is slower than the pixel-production side, or if there is more than one CPU at work. Over-production affects the measured speed because over-produced pixels are computed but never drawn. That is, more work is being done than is being measured. All this affects not only the speed of animation, but what it looks like (skipped frames result in jerkiness).
I made these changes so you can use the Miasma applet to explore how each aspect of this particular animation technique (a MemoryImageSource) affects performance. Miasma is not a single experiment designed to definitively measure animation speed. It's an apparatus for conducting various experiments that measure the effects of different contributions to overall animation speed. If you don't explore the different contributions, then you're missing the point.
The main changes I made are:
- vertical scaling is now correct, according to scale factor (fixed Plasma bug)
- controllable: priority, pixel-type, drawing, delivery, filtering
- producer-thread does not outrun consumer (drawing) thread
- an accurate average frame-rate is calculated and shown
- frame-rates are shown using Applet.showStatus()
At high frame-rates, the cost of drawing the frame-rate display in the original Plasma becomes significant. That is, Plasma is visibly faster if it doesn't show frame-rate. In a benchmark, this is poor. By changing to Applet.showStatus(), the cost of updating the frame-rate is amortized over more frames as the frame-rate increases, so it consumes less, percentage-wise.
The average frame-rate can be calculated over a configurable period of time. As with most averaging, smaller periods are more volatile.
Some of the code I changed from Plasma was just to help me understand what it was doing. For example, it was not clear to me what the palette-filling code was doing, so I dissected it and recoded it.
To better see whether RGB or palette pixels were being drawn, I made the RGB display a red/green image instead of a red/blue one. Red/green is not as aesthetically pleasing as red/blue, but so what. Benchmarks don't have to be aesthetically pleasing.
I changed some code to more efficiently iterate during the pixel calculation. Mostly I was curious what kind of speed gains could be had there. This has the visual effect of "reversing" the animation, but since I'm not trying to exactly emulate Plasma, so what. If exact Plasma emulation was needed, the sine-wave lookup tables or stepping variables could be calculated differently, and the animation would be visibly identical to Plasma.
Finally, I did not fix all of Plasma's shortcomings. Miasma itself can be improved.
I did nothing with Plasma2, which is a completely different beast. In particular, it uses a different pixel production algorithm, so comparing Plasma to Plasma2 lies somewhere between unwise and misleading. There may be some relevance, but it's hard to tease out exactly what it is. Furthermore, Plasma2 shows nothing that Plasma can't show, so why does Plasma2 exist at all?If you want, feel free to modify Miasma to do what Plasma2 does. It should be easy.
I'M NOT INTERESTED IN WHAT RESULTS YOU GET ON YOUR MACHINE. Don't send me Miasma results, because I'm not interested in how fast or slow your machine/JVM/video-card is.This may be hard to understand, considering how many changes I made to Plasma. But I didn't do Miasma as an inter-platform comparison benchmark. I did it to help developers identify and understand the individual costs of doing animation with a MemoryImageSource and a produced Image. That is, I did Miasma mainly as an aid to understanding exactly what contributes to the speed of a particular animation algorithm.
The other reason I did Miasma, and published it, is to show that writing even a simple benchmark is not as easy as measuring the frame-rate of a simplistic animation program. In short, the benchmark itself must not have weaknesses that undermine or invalidate its own measurements.
Given the original purpose of Plasma (demonstrating a bug in JDK 1.2's scaled image-drawing), it's hard to argue with what it does and how it does it. But it's another thing entirely to draw wide-ranging performance conclusions or rankings from Plasma. For example, Plasma's animation algorithm is not the only way to do animation, especially since JDK 1.2 introduced the BufferedImage class that may be many times faster. If one is limited to JDK 1.1, and one must animate by generating or manipulating pixels, then Plasma or Miasma represent one way to do that: a MemoryImageSource. But if animation can be done by flipping a sequence of predetermined frames, or you're not limited to JDK 1.1, then a MemoryImageSource may not be the only way to animate. And it may not be the fastest, either.
If you disagree with my conclusions about Plasma, feel free to ignore them. Also feel free to improve on Plasma yourself, or improve on Miasma.
If you run Miasma with the same parameters you give to Plasma, it will probably measure a different speed (fps) than Plasma does. There are two main reasons for this:
1) a different pixel-production loop;
2) a different way of displaying the measured fps.
So even if everything is identical (same machine, JVM, video, etc.) Plasma and Miasma may return different results.The various parameters to the Miasma applet control the following things:
- name=showfps
A boolean controlling whether frame-rates are shown or not. As a benchmark, it's useless without frame-rates, but maybe you like it for its aesthetic appeal.- name=scale
An integer scale-factor. The underlying image is produced at 1/scale, and is drawn on the screen scaled up by the scale factor. In general, the scale-factor should evenly divide into both the height and width, so the platform can scale the image best.- name=avg
A number of seconds over which to calculate the average frame-rate. This is calculated to the nearest resolvable millisecond, and shown to the nearest 1/10 second.- name=draw
A boolean controlling whether the delivered image is drawn or not. Setting this to false will tell you the maximum frame-rate possible, as if drawing the image took no time at all. Doing this can help you gauge exactly how much the overall frame-rate depends on calculation-speed and how much on drawing-speed. Very revealing.- name=deliver
A count of how many images will be delivered. Set to a positive number, say 500 or so, then 500 images will be calculated and delivered (and drawn if 'draw' is true). The 501st image will be calculated, but not delivered. That is, after 500 images the 500th image will be redrawn ever after. The purpose of this is to compare the difference between drawing alone vs. delivering and drawing new images. That is, between doing animation by flipping through existing images vs. calculating a new image for each frame. There may be little or no difference on some platforms, while others show a significant difference.
Set to a negative number, billions of images will be delivered, effectively delivering all images all the time.- name=filter
A boolean controlling whether scaling is done by drawImage() itself, or by a ReplicateScaleFilter. This ultimately determines how many pixels are in the image actually drawn. The effects are often revealing.
Exercises:
1) With scale=1, measure unfiltered and filtered frame-rates. Explain any difference.
2) With scale=10, measure unfiltered and filtered frame-rates. Explain any difference.- name=rgb
A boolean controlling whether RGB (int) or palette (byte) pixels are delivered and drawn. Both sizes of pixels are always calculated, though only one or the other is delivered. By always calculating both sizes of pixels, the pixel-calculation's contribution is equalized, at the cost of "over-producing" pixel forms.
Although it's possible to produce only one form of pixels or the other, doing so has little effect on speed (try it). Also, one must consider the speed cost of adding a conditional branch in the heart of a tight loop vs. the cost of producing both forms of pixels. Not every case of over-production costs enough to eliminate.- name=sync
A boolean controlling whether the pixel-producing thread is coordinated (synchronized) with the image-drawing thread. This may have a noticeable effect on animation jerkiness. It can have a significant effect on speed, too. When false, the pixel-producing thread may over-produce images which are never drawn. This is usually considered a bad idea.- name=pri
A number 1-10 for the pixel-producing thread's priority. Had little effect in my testing, but YMMV.