Graphics Engine
What is the story on anti-aliasing?
No heap memory allocation during repaint cycle!
Floating point representations
Browser Enhancement Technologies
Floats vs. Doubles
The Jmol graphics engine is a completely written in Java. There
is no Java3D, OpenGL or any type of hardware acceleration.
The graphics engine is a special purpose graphics engine,
not a general purpose 3D graphics library.
The rendering engine was built especially for displaying molecules,
so it does a very good job of drawing spheres and cylinders.
It is a completely software implementation of a z-buffer.
There is a int[] pixelBuffer which holds ARGB values and a
short[] zBuffer which holds the pixel depth at that point.
During the repaint cycle, the entire scene is rendered into this
pixelBuffer. Then the entire scene is draw as one image with a
single Graphics.drawImage(...) operation.
It does not use a display list of triangular meshes. That would be
too slow. Instead, there are optimized routines for drawing spheres
and cylinders.
If you see opportunities to improve the rendering
speed please let us know!
Anti-aliasing is not currently enabled in Jmol. Most of the
mechanism is in place, but is currently turned off.
It is currently disabled because of performance problems on older hardware.
We plan to complete the implementation and re-enable it in a
future release ... when we have a little more time to figure out how
it should be handled on older machines.
The Jmol Graphics3D engine is implemented completely in software. It
cannot use the underlying Sun 2D graphics calls because they do not offer
the zBuffer functionality of deciding on a pixel-by-pixel basis whether or
not a pixel should be drawn.
The anti-aliasing technique used in the Jmol Graphics3D engine is called
full-scene-anti-aliasing. The entire scene is rendered into a buffer that
is twice as wide and twice as high. Then, 4 pixels are converted into 1
pixel, with appropriate mixing of RGB values.
The end result is that it takes more memory to hold the zBuffer and
pixelBuffer. And it takea lot more CPU cycles to render a scene. The
drawing part takes 4 times as long. Overall, it probably takes 3 times as
long to render a scene.
On newer machines with lots of CPU power this is probably not a problem.
On older machines it would be unacceptable.
Therefore, we need to put in some options.
Jmol does not perform any heap memory allocation during the repaint
cycle. For performance reasons, it is important that we continue to
follow this guideline.
[Dave read a draft of this memo and gave permission
to use his code as an example]
In doing a little 'code review' of Dave's recently contributed code for
Ribbons/Mesh, I saw something that is worth discussing.
To be clear, Dave has written some excellent code. But with a small
modification we can ensure that we maintain optimal performance. And I are
going to use this code as an example to raise awareness of memory
allocation issues in Jmol.
To render the Ribbons, Dave needed two arrays of Point3i objects to hold
the screen coordinates of the edges of his ribbons. So, he allocates the
arrays and fills them up with new Point3i objects. Things get calculated,
things get drawn, and nice-looking ribbons appear on the screen.
And then the temporary objects get thrown away. And therein lies a small
problem: temporary objects which we are allocating and discarding during
the repaint cycle.
Why is that a problem?
Well, repaint cycles can occur rather quickly ... hopefully 30 times per
second during rotations. So, if we are allocating objects out of the heap
[by using new SomeObject(...)], then we can end up allocating a lot of
them rather quickly.
Actually, the problem is not with the allocation. The problem is with the
*deallocation*; we are immediate discarding the objects. These discarded
objects put unnecessary strain on the Java garbage collector, which must
run through memory looking for discarded data structures.
When the garbage collector runs it can sometimes generate pauses or
'hiccups' in the system response ... a bad thing.
Therefore, we should address this by doing our best to 'recycle' memory
objects during the repaint cycle.
We can accomplish this by associating temporary objects with instances of
the Renderer classes. We can ask the Renderer instances to allocate/store
the temporary objects for us, and we can reuse the same temps every time
through the repaint cycle.
Interestingly, there are other parts of the system where this guideline
does not really apply. For example, during IO operations we are free to
allocate/discard almost as much junk as we want. During IO we need to do a
lot of text parsing. This parsing generates many temporary strings that
have very short lifetimes. But that is OK because we don't read files that
frequently and because users expect it to be a relatively slow operation.
Someone raised the following issue which I thought would be of general
interest.
Regarding the display of crystal cell parameters:
> In a few cases the a, b, or c etc values display with 'extra' decimal
> places the 10pre8 applet displays a=10.436999 wheas the
> CRYST1 record of the pdb file has the value 10.437. Presumably
> a 'rounding' anomaly with the cell coordinate calculations.
Actually, this particular case is a more subtle issue. It wasn't really a
'rounding' issue because no calculations were performed on the numbers.
But it is still a problem. And the source of this problem is of
general interest to those who work with floating point numbers.
We are used to working in base 10 ... 10 fingers. From time-to-time we
come across rational numbers which cannot be represented in base 10.
Perhapst the most common examples are 1/3 and 2/3.
We write them as 0.33 or 0.67, or
extend them out a few more digits (0.33333 0.66667) if we need more
accuracy in our calculations.
Well, we run across the same issue in the computer world. And people have
been struggling with ways to address this issue since the beginning of
digital computers.
Most computers today generally use a floating point representation defined
in 1985 by the IEEE standards organization:
IEEE Std 754-1985
IEEE Standard for Binary Floating-Point Arithmetic
This is a 'base 2' (binary) representation of floating point numbers, not
a 'base 10' representation. Fundamentally, this means that there is a
different set of rational numbers which cannot be accurately represented.
In this case, no calculations were performed on the number 10.437 ... it
was read straight out of the file. But the number 10.437 cannot be
accurately represented in binary ... so we end up with 10.436999 (I
haven't actually verified this, but I assume it is the case).
Other random facts:
-
COBOL supported decimal and fixed-point numbers to address
this issue for dealing with dollars and cents in a business
environment
-
one of the representations on IBM mainframes was base 16 (instead
of base 2) While this did not address this particular issue,
it did provide faster performance (during the 'normalizing' process)
-
People who want high performance generally do not follow the IEEE
standard.
-
The initial Java Virtual Machine specification *required* IEEE
representation (and operational behavior) for floating point numbers.
This (should) ensure portability across platforms ... the same
calculation should give you the same answer on different systems. This
requirement has since been relaxed somewhat ... to allow
higher-performance computing ... and because of the recognition that it
is an unsolvable problem.
-
I think that most people agree that the IEEE standard has errors, but
people must follow it
-
Systems for doing symbolic/pure math (Mathematica, MATLAB, Macsyma)
support rational numbers (fractional representation of integers
... 1/3, 2/3) to try to avoid this type of problem
for rational numbers.
I will try to give a brief summary of the 4 different techniques that
can be used to add functionality to the browser. It is important that we
use the correct terminology to keep from getting confused.
In order from simplest to most complex:
Helper Application
A helper application is a regular application that gets automatically
launched when the browser receives file that it doesn't know what to do
with. The type of the file is identified by something called the
'MIME-type'.
Lets look at an example using a fresh installation of Netscape/Mozilla.
(We are going to say Netscape/Mozilla because Internet Explorer has some
'special' support for other MSFT products).
The first time Mozilla receives a .doc file with mime-type
'application/msword' it doesn't know what to do with the file. It knows
that the file is not of type 'text/html' or 'image/jpeg', so it doesn't
know how to handle that file type natively.
Therefore, Mozilla prompts the user to ask whether he/she wants to save
the file or launch an application. If the user selects an application then
he/she can choose to always launch the application when files of that
mime-type are encountered.
Every browser has this table of 'associations' between mime-types and
applications ... although Safari's table seems to be well-hidden.
A helper application can be any application ... including the Jmol app.
Note that for this scheme to work properly the web server must be
configured to send the correct mime-type. This is usually not a problem
for major applications. However, it is a problem for less well known
file types on web servers with novice system administrators ... as
is often the case with people try to build and deploy Chime web apps.
Untrusted (or Unsigned) Applet
This is a Java Applet that is embedded in a web page. Most untrusted
applets are quite small and download quickly. The user never really
notices them. They are little menuing system, games, etc.
This type of applet is 'untrusted' in the sense that it came from some
arbitrary web server some random place on the web. Therefore, we don't
really trust it. Therefore, untrusted applets are placed in a tight
security box. They cannot read or write to the local hard disk and they
can only talk to the web server where they came from. If they try to do
anything else then they are promptly decapitated.
The JmolApplet is an untrusted applet ... fatter and more complex than
most untrusted applets.
Trusted (or Signed) Applet
The security restrictions placed on an untrusted applet are rather
severe. And people want to develop web-based applications that can do
fancier things ... like save files to the local hard disk.
Before this type of applet is executed, the browser explicitly asks the
user for permission to run this applet. By saying yes the user is
essentially giving permission for the software to be installed on the
local hard drive.
To protect all parties a Trusted Applet is *signed* with an encrypted
digital signature. The encryption also ensures that the applet code has
not been modified or 'played with' by a 3rd party.
A 'certifying authority' is some organization that you trust to do the
verification ... kind of like the Better Business Bureau.
Starting with preliminary release 10.00.12 (this includes 10.2 and 11), Jmol includes a
'signed' JmolApplet (JmolAppletSigned.jar), although no official
'certifying authority' is backing it up.
When a user goes to a web page that has the signed JmolApplet, they will
be prompted and asked if they trust this applet. They will say 'yes' and
the session will continue.
The signed JmolApplet that is then downloaded can read/write files
on their hard drive. It can also read/write data
from/to arbitrary places on the web.
That will give the signed JmolApplet a lot more leeway to do interesting
things ... like read local molecular model files ... or pull files
directly from databases on the web.
Note that this signed JmolApplet is still an applet that only
exists in the context of a specific web page.
It is not an arbitrary application
that can be launched at any time.
A user cannot execute it unless it is part of a web page that they have
access to. However, this should include a web page that is coming from a
CD-ROM or is stored in a local file system.
The signed JmolApplet is offered for convenience of some users. However,
we want to keep the unsigned JmolApplet for people who want to build simpler web
sites that do not request/require explicit permission to write to the
local hard drive.
I would like to see the unsigned JmolApplet be 'lean and mean' ... with
limited functionality ... but good support for just showing molecules to
beginning students and casual visitors.
The signed JmolApplet is for serious users who want more serious
functionality.
Plug-in
To understand a plug-in it is helpful to think back to the helper
application. A helper application is not associated with a specific web
page. Rather, it is associated with a specific file type, a specific
mime-type. The web browser will launch MS Word every time it sees a
document with the mime-type 'application/msword'.
A plug-in functions in a similar way. But the display of the data is
embedded within the browser window. At some level, it is software that
extends the capabilities of the browser itself. It doesn't matter where
the files come from. When the browser sees a non-native mime-type it can
check its list of installed plug-ins and see if one of them can handle it.
Two popular examples are Flash and Acrobat Reader. People usually install
these things once and never worry about them again.
Once a plug-in is installed it can do whatever it wants ... like any piece
of installed software.
Developing and maintaining plug-ins is very costly and expensive. Although
Netscape defined a plug-in API, the fact is that the dynamic behavior of
each browser on each platform is different. And many browsers
'implement/support' the Netscape Plug-in API, but they are different
implementations and they have different behavior. The fundamental problem
is that you are trying to take one piece of complicated software (your
fancy plug-in) and embed it within another piece of complicated software
(the web browser), alongside other plug-ins. And, in general, you are
sharing the same memory and some of the same data structures. Therefore,
when someone makes a mistake things tend to break.
As I write this it becomes more clear than ever that a Jmol Plug-in will
never exist.
That's enough for now.
Several people have asked why the Jmol code uses floats instead of
doubles. Indeed, many younger Java programmers have probably never
seen a program with so many floats.
The answer is that they are smaller ... floats take up 4 bytes
and doubles take up 8 bytes. And yet, they have plenty of range
and precision for our purposes.
So, why is smaller important? You probably say that memory is cheap,
and, in general, you are right.
However, Jmol needs to run as an applet on 1.1 JVMs, where the
amount of virtual memory is somewhat smaller.
More importantly, memory bandwidth is never cheap. CPU
manufacturers do everything they can to try to design larger
caches to try to increase the effective memory bandwidth.
So, by working with 4-byte floats instead of 8-byte doubles,
we make more effective use of memory system ... the various
layers of caching + overall memory bandwidth. We get to store
more data in a fixed-size cache and we get to retrieve more
data from memory each time we access a cache line.
Frankly, it is just as easy to use floats ... and there is no
reason to be wasteful.
|