haiku-website/static/legacy-docs/benewsletter/Issue3-34.html

720 lines
50 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Be Newsletters - Volume 3: 1998</title><link rel="stylesheet" href="be_newsletter.css" type="text/css" media="all" /><link rel="shortcut icon" type="image/vnd.microsoft.icon" href="./images/favicon.ico" /><!--[if IE]>
<link rel="stylesheet" type="text/css" href="be_newsletter_ie.css" />
<![endif]--><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="Be Newsletters" /><link rel="up" href="volume3.html" title="Volume 3: 1998" /><link rel="prev" href="Issue3-33.html" title="Issue 3-33, August 19, 1998" /><link rel="next" href="Issue3-35.html" title="Issue 3-35, September 2, 1998" /></head><body><div id="header"><div id="headerT"><div id="headerTL"><a accesskey="p" href="Issue3-33.html" title="Issue 3-33, August 19, 1998"><img src="./images/navigation/prev.png" alt="Prev" /></a> <a accesskey="u" href="volume3.html" title="Volume 3: 1998"><img src="./images/navigation/up.png" alt="Up" /></a> <a accesskey="n" href="Issue3-35.html" title="Issue 3-35, September 2, 1998"><img src="./images/navigation/next.png" alt="Next" /></a></div><div id="headerTR"><div id="navigpeople"><a href="http://www.haiku-os.org"><img src="./images/People_24.png" alt="haiku-os.org" title="Visit The Haiku Website" /></a></div><div class="navighome" title="Home"><a accesskey="h" href="index.html"><img src="./images/navigation/home.png" alt="Home" /></a></div><div class="navigboxed" id="naviglang" title="English">en</div></div><div id="headerTC">Be Newsletters - Volume 3: 1998</div></div><div id="headerB">Prev: <a href="Issue3-33.html">Issue 3-33, August 19, 1998</a>  Up: <a href="volume3.html">Volume 3: 1998</a>  Next: <a href="Issue3-35.html">Issue 3-35, September 2, 1998</a></div><hr /></div><div class="article"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="Issue3-34"></a>Issue 3-34, August 26, 1998</h2></div></div></div><div class="sect1"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="Engineering3-34"></a>Be Engineering Insights: Class Struggle</h2></div><div xmlns:d="http://docbook.org/ns/docbook"><span xmlns="http://www.w3.org/1999/xhtml" class="author">By <span class="firstname">Mikol</span> <span class="surname">Ryon</span></span></div></div></div><p>
I work in QA. What I do around here at Be is pretty much just break
things. How do you break a computer? Besides throwing it off a roof, that
is.
</p><p>
First, you make sure the software running on the computer does what it's
supposed to. A good way to do that is by "class struggle." Just walking
through the Be Book and making sure that each method listed exists as
printed and acts as described can help you make sure your code does what
you think it should.
</p><p>
One of these class struggle missions grew into a rudimentary graphics
benchmark program. Here's the code in its very raw state.
</p><p>
ftp://ftp.be.com/pub/samples/graphics/obsolete/Grafiek_Proef.zip
</p><p>
As it's my first attempt at Be programming, it's rough, a bit dirty, like
a giant snowball rolling down hill picking up cruft as it goes. In spite
of that, it is occasionally useful.
</p><p>
The zip file contains source code, PPC and x86 project files, and a
makefile. If you run Grafiek_Proef from the command line it attempts to
measure the number of operations per second of each operation. It also
takes arguments of a random number seed to use, [-seed integer] and
[-noclip], which change the drawing region from 640x480 to 1024x768. Your
comments, corrections, and suggested changes are welcome here.
</p><p>
The second and more entertaining method I use while trying to break the
BeOS has given me a reputation as a bit of a necro. I ask a computer to
do things it should not be able to do. Very often, however, the BeOS does
it anyway.
</p><p>
You may recall that some time back, there was a stir on BeDope and
slashdot.org:
</p><p>
<a class="ulink" href="http://www.bedope.com/contests/contest1.html">http://www.bedope.com/contests/contest1.html</a><br />
http://slashdot.org/articles/9804141121220.shtml
</p><p>
Here I thought I was telling the BeOS to do something it couldn't do, so
I could watch where it broke. But it worked. Some people claimed that the
screenshot on the "Russian Doll" contest page of BeOS running
SheepShaver, Mac OS, SoftWindows, AppleWin (Apple ][ emulator), and Dig
Dug was a fake. Nope. I confess. That was me. In fact I later topped it:
</p><p>
http://www.bedope.com/051898.html
</p><p>
What does all this have to do with the ultra sleek OS of the future?
Sometimes it's nice to take a look back and see where you've been. It's
much more fun running emulators of antique computers than dealing with
legacy code and bloatware.
</p><p>
And while you're at it, download Steve Sakoman's bt848 TV card drivers
(http://www.sakoman.com/) and plug your Atari 2600 console into it. When
you're finished with that, find your old Diamond Dave Van Halen record
collection and download BeMAME from BeWare (http://www.be.com/beware/).
</p><p>
Have some fun and write good code!
</p></div><hr class="pagebreak" /><div class="sect1"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="Engineering3-34-2"></a>Be Engineering Insights: Pixel Packing Mama Errata</h2></div><div xmlns:d="http://docbook.org/ns/docbook"><span xmlns="http://www.w3.org/1999/xhtml" class="author">By <span class="firstname">George</span> <span class="surname">Hoffman</span></span></div></div></div><p>
Just dropping in to let y'all know of several errors in last week's
article by William Adams, "Pixel Packing Mama"
</p><p>
<a class="xref" href="Issue3-33.html#Engineering3-33-2" title="Be Engineering Insights: Pixel Packing Mama">Be Engineering Insights: Pixel Packing Mama</a>
</p><p>
If you enjoyed the original article, you'll find it educational to follow
along and learn why each of these things is an error. These are not
uncommon mistakes, so learning about them now is the best way to avoid
them in the future.
</p><p>
Even if you didn't read the article in question, I'll use this as an
opportunity to talk about pixel packing and endianess issues in general.
And while I'm at it, I'll present a little design documentation for the
Application Server and the usage models it encourages.
</p><p>
First, some actual bugs in the code presented with the article.
</p><p>
The code makes the assumption that the <span class="type">rgb_color</span> structure is a 32-bit
pixel value. For the record (as this mistake has been made countless
times by many developers, no doubt due to unclear documentation on our
part) the <span class="type">rgb_color</span> structure is NOT, when cast to a <span class="type">uint32</span>, in any valid
32-bit pixel format, big or little-endian, whether you're executing on
Intel or PowerPC. So the following code snippet, which appears in the
article's included code, is always a no-no:
</p><pre class="programlisting cpp">
<span class="type">colorUnion</span> <code class="varname">aUnion</code>.<code class="varname">value</code> = *((<span class="type">uint32 *</span>)<code class="methodname">GetPointer</code>(<code class="varname">x</code>,<code class="varname">y</code>));
<code class="varname">aColor</code> = <code class="varname">aUnion</code>.<code class="varname">color</code>;
</pre><p>
To find out why, look at the structure definition of the <span class="type">rgb_color</span>
structure. The format in memory is RGBA. A little- endian pixel in memory
is BGRA, and a big-endian pixel is ARGB. (This is also why it is a Bad
Thing to put <code class="constant">B_TRANSPARENT_32_BIT</code> into a 32-bit bitmap to designate
transparency. <code class="constant">B_TRANSPARENT_32_BIT</code> is an <span class="type">rgb_color</span> structure, not a pixel
value.) The proper way to do the above is to assign the components
manually:
</p><pre class="programlisting cpp">
uint32 <code class="varname">value</code> = *((uint32 *)<code class="methodname">GetPointer</code>(<code class="varname">x</code>,<code class="varname">y</code>));
<code class="varname">aColor</code>.<code class="varname">alpha</code> = (<code class="varname">value</code>&gt;&gt;24)&amp;0xff;
<code class="varname">aColor</code>.<code class="varname">red</code> = (<code class="varname">value</code>&gt;&gt;16)&amp;0xff;
<code class="varname">aColor</code>.<code class="varname">green</code> = (<code class="varname">value</code>&gt;&gt;8)&amp;0xff;
<code class="varname">aColor</code>.<code class="varname">blue</code> = <code class="varname">value</code>&amp;0xff;
</pre><p>
The 32-bit <code class="methodname">PutPixel()</code> routine would be changed to work similarly. This
isn't the fastest way of moving a pixel from one place to another, but
William's intention wasn't to show optimized code, but to present a
learning exercise.
</p><p>
Note that the code as given will not show this bug while simply copying
pixels from one 32-bit bitmap to another. The bug turns up when you copy
between color depths, or try to read out individual color components.
Also, the "proper" code above works only for host endianess pixels (i.e.,
little-endian pixels, or <code class="constant">B_RGBA32</code>, on Intel, and big-endian pixels, or
<code class="constant">B_RGBA32_BIG</code>, on PowerPC).
</p><p>
That brings us to the second bug—one of endianess. The BeOS
color_space enum lets you create bitmaps in 1,8,15,16, or
32-bits-per-pixel format, and in either endianess. Looking at
<code class="filename">GraphicsDefs.h</code>,
we can see that the "default" endianess is little:
</p><pre class="screen">
<code class="constant">B_RGB32</code> = 0x0008,
<code class="constant">B_RGBA32</code> = 0x2008,
<code class="constant">B_RGB32_BIG</code> = 0x1008,
<code class="constant">B_RGBA32_BIG</code> = 0x3008,
<code class="constant">B_RGB32_LITTLE</code> = <code class="constant">B_RGB32</code>,
<code class="constant">B_RGBA32_LITTLE</code> = <code class="constant">B_RGBA32</code>,
</pre><p>
Thus, the code in William's article, which specified the un-suffixed
version of these enums, was working exclusively with little-endian
pixels. You may not want this if you're on a big-endian processor, and/or
writing to a big-endian frame buffer. It's faster for the Application
Server to blit between bitmaps of the same endianess than to have to do
the conversion. That's why you may want to take into account the
endianess of the destination frame buffer and the host's endianess when
picking a colorspace for your bitmap.
</p><p>
But let's say you want to use little-endian pixels or die trying. Take a
look at the 2-byte-per-pixel case in the
<code class="classname">PixelBuffer</code>::<code class="methodname">GetPixel()</code> code:
</p><pre class="programlisting cpp">
case <code class="constant">B_RGB15</code>:
case <code class="constant">B_RGBA15</code>:
case <code class="constant">B_RGB16</code>:
{
<span class="type">uint16</span> <code class="varname">indexValue</code> = *((<span class="type">uint16 *</span>)<code class="methodname">GetPointer</code>(<code class="varname">x</code>,<code class="varname">y</code>));
<code class="varname">aColor</code>.<code class="varname">blue</code> = (<code class="varname">indexValue</code> &amp; 0x1f) &lt;&lt; 3; <span class="comment">// low 5 bits</span>
<code class="varname">aColor</code>.<code class="varname">green</code> = ((<code class="varname">indexValue</code> &gt;&gt; 5) &amp;0x1f) &lt;&lt; 3;
<code class="varname">aColor</code>.<code class="varname">red</code> = ((<code class="varname">indexValue</code> &gt;&gt; 10) &amp;0x1f) &lt;&lt; 3;
<code class="varname">aColor</code>.<code class="varname">alpha</code> = 255;
}
break;
</pre><p>
This code is invoked on either PowerPC or Intel, in cases in which you're
using little-endian 15- or 16-bit pixels. The code works correctly for
15-bit pixels, but only if the host processor is little-endian. (Even in
this limited case, the code is still slightly flawed, but I'll get to
that in a minute.) To understand what the bug is, and how to fix the code
to work correctly, we need to look at how the pixel is structured and how
it is laid out in memory.
</p><p>
First, why doesn't the code work for 16-bit pixels? Well, the code
assumes five bits per color component. In a 16-bit pixel, red and blue
are five bits, but the green component is six bits (the eye is
particularly sensitive to green, so this uses the extra bit in a 16-bit
word where it can do the most good). For a 16-bit, little-endian pixel on
a little-endian processor, the code should look like this:
</p><pre class="programlisting cpp">
<span class="type">uint16</span> <code class="varname">indexValue</code> = *((<span class="type">uint16 *</span>)<code class="methodname">GetPointer</code>(<code class="varname">x</code>,<code class="varname">y</code>));
<code class="varname">aColor</code>.<code class="varname">blue</code> = (<code class="varname">indexValue</code> &amp; 0x1f) &lt;&lt; 3; <span class="comment">// low 5 bits</span>
<code class="varname">aColor</code>.<code class="varname">green</code> = ((<code class="varname">indexValue</code> &gt;&gt; 5) &amp;0x3f) &lt;&lt; 2; <span class="comment">// middle 6 bits</span>
<code class="varname">aColor</code>.<code class="varname">red</code> = ((<code class="varname">indexValue</code> &gt;&gt; 11) &amp;0x1f) &lt;&lt; 3; <span class="comment">// high 5 bits</span>
<code class="varname">aColor</code>.<code class="varname">alpha</code> = 255;
</pre><p>
That deals with a simple mistake, so now let's attack the more involved
endianess problem. To review, a little-endian 15-bit pixel in memory
looks like this:
</p><pre class="screen">
[gggBBBBB] [ARRRRRGG]
</pre><p>
Each letter represents a bit. Here we see two bytes: the first holds the
low three bits of the green component (the lowercase "g"s) and the whole
blue component; the second holds an alpha bit (mostly unused so far in
the BeOS) the red component, and the high two bits of the green
component. When read into the low 16 bits of a little-endian machine
register for processing, the first byte is the low-order byte, so the
register looks like this:
</p><pre class="screen">
[ARRRRRGGGGGBBBBB]
</pre><p>
Again, this is a little-endian pixel in a little-endian machine register.
It's easy to see why the code works in this case. To obtain each of the
RGB components, we shift right (by 0, 5, or 10), mask the bits, and shift
those bits up to the high end of the byte, obtaining an 8-bit component.
Consider, however, an instance of this code executing on a big-endian
processor. In this case, the first byte is the high byte, and the
register looks like this:
</p><pre class="screen">
[gggBBBBBARRRRRGG]
</pre><p>
Horrors! Clearly, this code won't work correctly on PowerPC. The
components get all jumbled and everything's a mess.
</p><p>
How do we correct for this? We could split off a different case for
big-endian hosts for use with little-endian pixels. But if we step back
for a moment and think about the problem, it turns out we can kill two
birds with one stone (or, as JLG would say, tractor two birds with one
stone). We can fix this problem and make our code handle big-endian
pixels for free.
</p><p>
Attentive readers will have noticed that it's not the endianess of the
pixel per se that matters, but the endianess of the pixel with respect to
the host endianess. That is, once we're actually playing with the pixel
as a 16-bit word (and not as a bytestream in memory) a little-endian
pixel on a big-endian machine looks like a big-endian pixel on a little
endian machine, and a little- endian pixel on a little-endian machine
looks like a big-endian pixel on a big-endian machine.
</p><p>
Got that?
</p><p>
Simply said, for reading and writing pixels, there are only two endianess
cases: host-endianess and not host-endianess. A host- endianess 15-bit
pixel in a register always looks like
</p><pre class="screen">
[ARRRRRGGGGGBBBBB]
</pre><p>
and an anti-host-endianess 15-bit pixel always looks like
</p><pre class="screen">
[gggBBBBBARRRRRGG]
</pre><p>
This means we need only two code paths per endianess-independent pixel
format to handle both endianesses of processors and both endianesses of
pixels. Here's some code to read a 15-bit pixel that works for all
endianess combinations:
</p><pre class="programlisting cpp">
<span class="type">uint16</span> <code class="varname">value</code> = *((<span class="type">uint16*</span>)<code class="methodname">GetPointer</code>(<code class="varname">x</code>,<code class="varname">y</code>));
if (!<code class="methodname">IsHostEndianess</code>(<code class="methodname">ColorModel</code>()))
<code class="varname">value</code> = (<code class="varname">value</code> &gt;&gt; 8) | ((<code class="varname">value</code> &amp; 0xff) &lt;&lt; 8);
<code class="varname">aColor</code>.<code class="varname">blue</code> = (<code class="varname">value</code> &amp; 0x1f) &lt;&lt; 3;
<code class="varname">aColor</code>.<code class="varname">green</code> = (<code class="varname">value</code> &amp; (0x1f &lt;&lt; 5)) &gt;&gt; 2;
<code class="varname">aColor</code>.<code class="varname">red</code> = (<code class="varname">value</code> &amp; (0x1f &lt;&lt; 10)) &gt;&gt; 7;
<code class="varname">aColor</code>.<code class="varname">alpha</code> = 255;
</pre><p>
Here, we simply byte-swap the register before doing the conversion, to
make sure the value is in a format the conversion code can deal with.
[Aside: Notice that half of the shifting has been moved to operate on
constants; this is faster because the constant is shifted at compile time
rather than run time. The speed gains from this trick would be minimal
for the <code class="methodname">GetPixel()</code>/<code class="methodname">PutPixel()</code>
framework, but very significant in more optimized code.]
</p><p>
I mentioned earlier that the code is still slightly flawed. To see what
the problem is, take a look at what we're doing for each component. We
obtain a 5-bit value and convert to an 8-bit value. To do this, we're
shifting up by three. This appears to work—it gets the values pretty
close to the mark—but the <span class="type">rgb_color</span> struct produced is not as accurate
as it could (and perhaps should) be. The maximum value of 5-bit value is
31. The maximum value of an 8-bit value is 255. We'd like one to map to
another, but (31 &lt;&lt; 3) is only 248. Ack!
</p><p>
To do a fully correct conversion, we need to duplicate the three
high-order bits of the source 5-bit value into the three low-order bits
of the destination 8-bit quantity. This automagically corrects our
mapping. In applications where speed is more important than accuracy, you
might not want to do those extra logical ops. But this learning exercise
is not one of those times.
</p><p>
Is there anything else about this code we can improve? Well, this one is
a bit nit-picky, but worth mentioning: 15-bit pixels have an alpha bit.
Depending on our application, we might want to preserve the alpha
information.
</p><p>
So, the fully correct any-endianess conversion is:
</p><pre class="programlisting cpp">
<span class="type">uint16</span> <code class="varname">value</code> = *((<span class="type">uint16*</span>)<code class="methodname">GetPointer</code>(<code class="varname">x</code>,<code class="varname">y</code>));
if (!<code class="methodname">IsHostEndianess</code>(<code class="methodname">ColorModel</code>()))
<code class="varname">value</code> = (<code class="varname">value</code> &gt;&gt; 8) | ((<code class="varname">value</code> &amp; 0xff) &lt;&lt; 8);
<code class="varname">aColor</code>.<code class="varname">blue</code> = (<code class="varname">value</code> &amp; 0x1f) &lt;&lt; 3;
<code class="varname">aColor</code>.<code class="varname">blue</code> |= <code class="varname">aColor</code>.<code class="varname">blue</code> &gt;&gt; 5;
<code class="varname">aColor</code>.<code class="varname">green</code> = (<code class="varname">value</code> &amp; (0x1f &lt;&lt; 5)) &gt;&gt; 2;
<code class="varname">aColor</code>.<code class="varname">green</code> |= <code class="varname">aColor</code>.<code class="varname">green</code> &gt;&gt; 5;
<code class="varname">aColor</code>.<code class="varname">red</code> = (<code class="varname">value</code> &amp; (0x1f &lt;&lt; 10)) &gt;&gt; 7;
<code class="varname">aColor</code>.<code class="varname">red</code> |= <code class="varname">aColor</code>.<code class="varname">red</code> &gt;&gt; 5;
<code class="varname">aColor</code>.<code class="varname">alpha</code> = 0-(<code class="varname">value</code> &gt;&gt; 15);
</pre><p>
Whew! A lot of work just to get a friggin' pixel, eh? Now I'll clear up
some misleading things that the article either said or implied.
</p><p>
The first is that the Application Server allocates bitmaps only in chunks
of 4K or more, and that smaller bitmaps waste a lot of extra space. This
is actually almost never the case. It's true that some bitmaps reserve an
area for their data, and that areas can consist of no less than one 4K
page, but the only bitmaps that do this are those for which you specify
<code class="constant">B_CONTIGUOUS</code> as a flag.
</p><p>
Under the BeOS, the only way to map a contiguous set of logical pages to
a contiguous set of physical pages is to create a special area for those
pages; thus, an area is needed for any physically contiguous bitmaps. All
other, non-physically- contiguous bitmaps are stored together in a single
large area that the app_server shares with the client. So, while there's
a small amount of overhead per bitmap, and you might want to group
together large numbers of very small bitmaps into one large bitmap anyway
(say, 2000 4x4 bitmaps), don't assume that at least a page is needed for
any given general bitmap. It ain't true.
</p><p>
The second point is more a clarification than a correction. The article
talks about using these
<code class="methodname">GetPixel()</code>/<code class="methodname">PutPixel()</code>
routines and states, "The
second benefit is that you don't actually have to talk to the app_server
in order for your icon to be drawn into your pixel buffer. And why not
talk to the app_server? Because it's a busy team and you would probably
rather not make requests if you really don't have to."
</p><p>
William is absolutely right that there are things you probably shouldn't
use the Application Server for. Putting a pixel into a bitmap, or even
several pixels, is definitely one of them. It's true that the app_server
is a busy team, but it also has threads reserved for your use and ready
to serve you at all times.
</p><p>
The reason is that the app_server is a server. There is context-switching
and memory-copy overhead inherent in talking to a server. But we're
working to minimize and/or eliminate more of that overhead, and optimize
the drawing performance, in every release.
</p><p>
So, while you might not want to use the app_server to put individual
pixels into a bitmap, you probably do want to use it to blit any
decent-sized bitmap (i.e., 32x32 and up) into another or onto the screen.
Just be sure to use <code class="methodname">DrawBitmapAsync()</code> and to not make any unnecessary
synchronous calls. If you're not satisfied with the performance, gimme a
call.
</p><p>
That's about it for now. For those of you who have been holding your
breath waiting for the article in which I promised to divulge all the
Ultra-Secret New R4 Application Server Features, it'll be a couple more
weeks. I'm still coding 'em. But don't worry. It'll be good.
</p></div><hr class="pagebreak" /><div class="sect1"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="DevWorkshop3-34"></a>Developers Workshop: Life in Three Dimensions</h2></div><div xmlns:d="http://docbook.org/ns/docbook"><span xmlns="http://www.w3.org/1999/xhtml" class="author">By <span class="firstname">Michael</span> <span class="surname">Morrissey</span></span></div></div></div><p>
The Game of Life is a classic programming exercise, perhaps second in
popularity only to "Hello World". It's fun to program and it's really fun
to watch the exciting patterns and interactions appear and disappear. But
as exciting as that classic, 2D game is, this is BeOS, and nothing less
than three dimensions will do!
</p><p>
To visualize the 3D version of Life, we're going to use OpenGL. The
beauty of OpenGL is that it allows you to concentrate on what you're
trying to draw rather than on how to draw it. Put another way, it makes
you look pretty impressive with just a few lines of code!
</p><p>
The focus of this article is more on using OpenGL on BeOS, as opposed to
a general OpenGL tutorial. However, only basic OpenGL techniques are used
in the code, so even if you're unfamiliar with OpenGL, you should be able
to understand it. It helps to arm yourself with a good OpenGL book;
personally, I'd recommend OpenGL Programming Guide, 2nd edition by Woo,
Neider, and Davis.
</p><p>
The sample code for this application can be found at:
</p><p>
ftp://ftp.be.com/pub/samples/open_gl/3Dlife.zip
</p><p>
Before we dive into the code, we need a little background. Life in three
dimensions is played in exactly the same manner as Life in two
dimensions. For each cell, a count of the living neighbors is taken. In
two dimensions, there are eight neighbors, while in three dimensions,
there are 26. The number of living neighbors determines the fate of the
cell in question: given enough living neighbors, but not too many, the
cell may continue living. Or, if the cell was not alive, it may spring to
life in the next generation, if conditions are favorable.
</p><p>
Carter Bays, a computer scientist at the University of South Carolina,
has investigated three dimensional analogues of the standard,
two-dimensional game of Life. The trick in extending the game by an extra
dimension is finding rules which properly balance life and death on our
little "planet." He has found two such sets of rules, called Life 4-5-5-5
and Life 5-7-6-6. The first two numbers are the lower and upper bounds
for sustaining life. For example, in Life 4-5-5-5, if a living cell has
less than four living neighbors, it will die of loneliness, but if it has
more than five living neighbors, it will die of overcrowding. Similarly,
the next two numbers are the lower and upper bounds for creating new
life. Again, in Life 4-5-5-5, if an empty cell has exactly five living
neighbors, new life will be born. Otherwise, the cell will remain empty.
</p><p>
Now, onto the code! As always, we want to start with a good, clean
design, but one which will allow flexibility for future improvements.
Most importantly, we will not concern ourselves with optimizing the Life
calculation code, tempting though it might be. As Donald Knuth warns us,
"Premature optimization is the root of all evil."
</p><p>
The major component of our design centers around multithreading, which
gives us better performance and better responsiveness than if we lumped
all the code into a single thread. We'll have three threads: one for the
Life calculations, one for the drawing of the Life board, and one for the
window. (Of course, the window thread is created for us when we
instantiate the <code class="classname">BWindow</code>, while the first two must be explicitly spawned.)
So in <code class="methodname">lifeView::AttachedToWindow()</code>, we start the life calculation thread,
and the drawing thread:
</p><pre class="programlisting cpp">
<span class="comment">// start a thread to calculate the board generations</span>
<code class="varname">lifeTID</code> = <code class="function">spawn_thread</code>((<span class="type">thread_entry</span>)<code class="varname">lifeThread</code>,
"lifeThread", <code class="constant">B_NORMAL_PRIORITY</code>, <code class="varname">this</code>);
<code class="function">resume_thread</code>(<code class="varname">lifeTID</code>);
<span class="comment">// start a thread which does all of the drawing</span>
<code class="varname">drawTID</code> = <code class="function">spawn_thread</code>((<span class="type">thread_entry</span>)<code class="varname">drawThread</code>,
"drawThread", <code class="constant">B_NORMAL_PRIORITY</code>, <code class="varname">this</code>);
<code class="function">resume_thread</code>(<code class="varname">drawTID</code>);
</pre><p>
Now that we've created them, these two threads need to be synchronized.
It seems tempting to let the lifeThread calculate as many generations
ahead as possible, queueing them up for display by drawThread, but it
really won't buy us anything. A little experimenting with BStopWatch
confirms that displaying each generation takes much longer than the
calculation of the next generation, so we only need to stay one step
ahead of the display.
</p><p>
We use two global semaphores, <code class="varname">read_board_sem</code> and <code class="varname">write_board_sem</code>, to let
the lifeThread know when to calculate a new generation, and to let the
<code class="function">drawThread</code> know that there's a new generation to be displayed. In
lifeThread, we acquire the <code class="varname">write_board_sem</code>, calculate the next
generation, and release the <code class="varname">read_board_sem</code>:
</p><pre class="programlisting cpp">
while(!(<code class="varname">mv</code>-&gt;<code class="methodname">QuitPending</code>()))
{
<code class="varname">nextGen</code> = new <span class="type">bool</span>[<code class="constant">BOARD_SIZE</code>][<code class="constant">BOARD_SIZE</code>][<code class="constant">BOARD_SIZE</code>];
<code class="function">acquire_sem</code>(<code class="varname">write_board_sem</code>);
if(<code class="varname">mv</code>-&gt;<code class="methodname">QuitPending</code>())
{
<span class="comment">// semaphore was released from ExitThreads()</span>
break;
}
<span class="comment">// calculate the next generation</span>
<code class="methodname">DoLife</code>(<code class="varname">prevGen</code>, <code class="varname">nextGen</code>);
<span class="comment">// "post" the next generation</span>
<code class="varname">board</code> = <code class="varname">nextGen</code>;
<code class="varname">prevGen</code> = <code class="varname">nextGen</code>;
<code class="function">release_sem</code>(<code class="varname">read_board_sem</code>);
}
</pre><p>
<code class="methodname">QuitPending()</code> is a function defined in
the <code class="classname">lifeView</code> class which returns a
boolean flag. The flag is set to <code class="constant">true</code> in the
<code class="methodname">ExitThread()</code> function if a
<code class="constant">B_QUIT_REQUESTED</code> message has been posted. Checking this flag with each
pass of the loop allows the thread to exit gracefully. It's used in both
the <code class="function">lifeThread()</code> and the <code class="function">drawThread()</code>.
</p><p>
Similarly, drawThread tries to acquire the <code class="varname">read_board_sem</code>, but here we
use <code class="function">acquire_sem_etc()</code>, which allows us to timeout. We may be rotating the
display, and we don't want to have to wait until the next generation is
computed to rotate (as is the case if we're running in the single-step,
rather than continuous, mode of life calculation):
</p><pre class="programlisting cpp">
while(!(<code class="varname">mv</code>-&gt;<code class="methodname">QuitPending</code>()))
{
<code class="varname">mv</code>-&gt;<code class="methodname">SpinCalc</code>();
<code class="varname">mv</code>-&gt;<code class="methodname">Display</code>(<code class="varname">displayBoard</code>, <code class="varname">generations</code>, <code class="varname">steadyFlag</code>);
if(<code class="varname">mv</code>-&gt;<code class="methodname">continuousMode</code>() &amp;&amp; <code class="varname">steadyFlag</code>)
{
<code class="varname">mv</code>-&gt;<code class="methodname">continuousMode</code>(<code class="constant">false</code>);
}
if(<code class="varname">mv</code>-&gt;<code class="methodname">continuousMode</code>() || <code class="varname">mv</code>-&gt;<code class="methodname">singleStepMode</code>())
{
if(<code class="function">acquire_sem_etc</code>(<code class="varname">read_board_sem</code>, 1,
<code class="constant">B_TIMEOUT</code>,100) != <code class="constant">B_TIMED_OUT</code>)
{
if(<code class="varname">displayBoard</code>)
{
delete []<code class="varname">displayBoard</code>;
}
<code class="varname">displayBoard</code> = <code class="varname">board</code>;
<code class="varname">board</code> = <code class="constant">NULL</code>;
<code class="varname">generations</code>++;
<code class="varname">mv</code>-&gt;<code class="methodname">singleStepMode</code>(<code class="constant">false</code>);
<code class="function">release_sem</code>(<code class="varname">write_board_sem</code>);
}
}
else
{
<span class="comment">// if the display isn't rotating and we don't have a</span>
<span class="comment">// new board to display, give the CPU a break!</span>
if(!<code class="varname">mv</code>-&gt;<code class="methodname">spinning</code>())
<code class="function">snooze</code>(500000);
}
}
</pre><p>
It's important to note that we release these semaphores in <code class="methodname">ExitThreads()</code>.
If we didn't do this, the <code class="function">acquire_sem()</code> in <code class="function">lifeThread()</code> would never
return. Note also that once we acquire the <code class="varname">write_board_sem</code>, we make sure
that it wasn't released from <code class="methodname">ExitThreads()</code>, by checking the status of
<code class="methodname">QuitPending()</code>.
</p><p>
The OpenGL portion of the code is very straightforward. First, we
instantiate a class (<code class="classname">lifeView</code>) which is
derived from <code class="classname">BGLView</code>, and pass
the <code class="classname">BGLView</code> constructor the options
<code class="constant">BGL_RGB</code> | <code class="constant">BGL_DEPTH</code> | <code class="constant">BGL_DOUBLE</code>,
indicating that we will enable depth testing and <span class="type">double</span> buffering. Next,
in <code class="methodname">lifeView::AttachToWindow()</code>, we prepare the OpenGL state:
</p><pre class="programlisting cpp">
<code class="methodname">LockGL</code>();
<span class="comment">// turn on backface culling</span>
<code class="function">glEnable</code>(<code class="constant">GL_CULL_FACE</code>);
<code class="function">glCullFace</code>(<code class="constant">GL_BACK</code>);
<code class="function">glEnable</code>(<code class="constant">GL_DEPTH_TEST</code>);
<code class="function">glEnableClientState</code>(<code class="constant">GL_VERTEX_ARRAY</code>);
<code class="function">glShadeModel</code>(<code class="constant">GL_FLAT</code>);
<code class="function">glClearColor</code>(0.0,0.0,0.0,0.0);
<code class="function">glOrtho</code>(-<code class="constant">BOARD_SIZE</code>,<code class="constant">BOARD_SIZE</code>,-<code class="constant">BOARD_SIZE</code>,<code class="constant">BOARD_SIZE</code>,
-<code class="constant">BOARD_SIZE</code>,<code class="constant">BOARD_SIZE</code>);
<code class="methodname">UnlockGL</code>();
</pre><p>
Before you call an OpenGL function, you must <code class="methodname">LockGL()</code>, which assures that
only one thread at a time will be making OpenGL calls. As with all locks,
this one should be held as briefly as possible.
</p><p>
The <code class="methodname">Display()</code> function actually draws the board. We clear the view, make
several <code class="function">glRotatef()</code> calls to get the view we want, translate the board to
be centered about the origin, and draw the living cells as cubes (in the
<code class="methodname">DrawFrame()</code> function). Most importantly, don't forget to call
<code class="methodname">SwapBuffers()</code>! Because we've selected <span class="type">double</span> buffering mode all of our
drawing is done off screen, and we need to call <code class="methodname">SwapBuffers()</code> to be able
to see what we've just drawn.
</p><p>
There's a lot more fun to be had with this code, so we'll return to this
project in future articles. Some of the things we'll be adding include
zooming in and out, the ability to "grab" the board with the mouse and
rotate it, and transparency, so we can see through the cubes. In the
meantime, experiment with this code and your own OpenGL ideas, and have
fun!
</p></div><hr class="pagebreak" /><div class="sect1"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="Gassee3-34"></a>OPH and OPOS</h2></div><div xmlns:d="http://docbook.org/ns/docbook"><span xmlns="http://www.w3.org/1999/xhtml" class="author">By <span class="firstname">Jean-Louis</span> <span class="surname">Gassée</span></span></div></div></div><p>
More acronyms? Yes, but only for the duration of this column, and I'll
make my use of them clear over the course of it. OPH stands for Other
People's Hardware and OPOS for Other People's Operating Systems.
</p><p>
This week's topic arises from suggestions that we look at running the
BeOS on various interesting hardware ranging &gt;from multiprocessor PowerPC
machines to Alpha-based systems. Proponents often refer to our [supply
your own adjective here] decision to port the BeOS to the Power Mac and
Intel-based hardware. Since this is clear evidence of a preference for
running the BeOS on OPH rather than limiting it to our proprietary BeBox,
why not consider more OPH ports?
</p><p>
Going back to our original OPH decision, I confess it wasn't so much
strategic as reactive, borne out of irritation. Or, to be more generous
to ourselves, prompted by an incident that triggered a decision that was
already lurking in the back of our minds.
</p><p>
Arguably, the process started in November 1995 when we saw the multitude
of inexpensive dual and quad processor motherboards displayed at Comdex.
Later, a Friday afternoon phone call "de-registering" us, three days
before the event, &gt;from the May 1996 Apple Developer's Conference,
greatly helped to clarify previously inchoate thoughts.
</p><p>
We had been invited to the conference, confirmed (and reconfirmed at our
request because we weren't sure of our status), our check had cleared...
and then were informed that we had to beg off—because we weren't a Mac
developer.
</p><p>
Legally, this was accurate. But, as one of us—I can't recall exactly
who it was—pointed out, we could volunteer to become one. Just do a
quick port and qualify as yet another developer adding value to Power Mac
hardware.
</p><p>
Since there already was a preexisting, if nebulous, acceptance of OPH
thought, everyone quickly embraced the idea, including our friends at
Apple. The PowerPC version of the BeOS was launched shortly thereafter. A
year later, we started giving demonstrations of an early Intel port. Both
actions unquestionably establish that we are in the business of running
the BeOS on OPH.
</p><p>
In fact, our OPH bias is not so pellucidly clear, and that's where OPOS
comes in. When we looked at our decision to move &gt;from the BeBox to OPH,
our first thought was that we could benefit from the much higher volumes
enjoyed by established hardware platforms. BeOS developers could reach a
wider market and Be shareholders could focus their investment on the
operating system alone.
</p><p>
As to why we built multiprocessor hardware in the first place, see some
of my earlier Newsletter articles:
</p><p>
<a class="xref" href="Issue1-7.html#Gassee1-7" title="Strategy">Strategy</a><br />
<a class="xref" href="Issue1-20.html#Gassee1-20" title="What Business Are We In, Really?">What Business Are We In, Really?</a><br />
<a class="xref" href="Issue1-22.html#Gassee1-22" title="When, Where Can I Buy One?">When, Where Can I Buy One?</a>
</p><p>
Be had inexpensive Symmetric Multi-Processor hardware long before it
became a quasi-commodity in the Intel space. In turn, our SMP hardware
gave us the foundation for the nimble SMP features in the BeOS.
</p><p>
The wider hardware playing field argument, however, is hard to separate
from another factor. That is, the presence or absence of a dominant OS on
the hardware platform we're considering. In our opinion, at this stage of
our game, we think it's a good idea for us to coexist with the dominant
system software life form.
</p><p>
These last two sentences require additional explanation. When I write
"hard to separate," I refer to the old, unsettled, hardware and OS,
chicken and egg argument. The need for a preexisting strong OS refers to
the perceived limitations of a nascent and specialized system software
platform such as the BeOS.
</p><p>
A strong general purpose OS on the targeted OPH provides a reassuring
presence. For a potential customer, the BeOS applications and the BeOS
itself can be seen as adding value to a well-established, safe system --
as opposed to demanding an all-or-nothing bet on a BeOS-only solution.
</p><p>
Besides using the precautionary "in our opinion" above, I also wrote "at
this stage of our game." Time will tell how broad and deep the range of
BeOS uses will become. But for this stage of our life, we're best in a
symbiotic relationship with mature OPOS life forms.
</p><p>
A strange thought, understandably uncomfortable for some. It is, however,
the thought that underlies our decision not to support some hardware
systems in spite of their otherwise impressive performance credentials.
</p></div><hr class="pagebreak" /><div class="sect1"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="BeDevTalk3-34"></a>BeDevTalk Summary</h2></div></div></div><p>
BeDevTalk is an unmonitored discussion group in which technical
information is shared by Be developers and interested parties. In this
column, we summarize some of the active threads, listed by their subject
lines as they appear, verbatim, in the mail.
</p><p>
To subscribe to BeDevTalk, visit the mailing list page on our web site:
http://www.be.com/aboutbe/mailinglists.html.
</p><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id731395"></a>NEW</h3></div></div></div><div class="sect3"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h4 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id731401"></a>Subject: Be Applications in the Future</h4></div></div></div><p>
Mark S. VanderVoord votes "yea" on applications that can act like
add-ons: “<span class="quote">A user's suite of applications becomes a set of modules, all of
which are customizable and/or interchangeable. If a user was to learn a
SpellCheck module, why should they need to learn a new one just because
they purchase a new word processor?</span>
</p><p>
So, what are the chances that application writers will publish add-on
versions, and that other developers will use these add-ons? Osma
Ahvenlampi: “<span class="quote">All it requires is some co-operation and self-discipline on
the parts of the authors, as well as the capability to accept someone
else's work and not fall into the Not Invented Here syndrome. In other
words, especially if we're talking about commercial software, and based
on prior experience of such, not bloody likely.</span>
</p><p>
But does anything stand in the way technically? Not much in the way of
plumbing, but would a service-providing app know which format the
service-requestor wants? Jon Watte: “<span class="quote">This is solved already. Something
initiating a drag can put 'promised' data formats in the drag message,
and the recipient can choose the one that fits best and ask the initiator
to fulfill the promise. It's in R4. Part of the protocol is the ability
to say 'please hand me a disk location, and I can write data to there' so
you can drag clippings to the Tracker.</span>
</p></div><div class="sect3"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h4 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id731453"></a>Subject: BListView with many items</h4></div></div></div><p>
How many items is too many? Jim Menard has noticed that a <code class="classname">BListView</code> with,
oh, a thousand items can take a very long time to display. “<span class="quote">The
construction of the list items is not the time-waster. Adding the items
to the list is taking all of the time. Any suggestions, or do I have to
roll my own scrollable list widget?</span>
</p><p>
A number of folks wrote in to say that "thousands of items" is asking for
too much, and that the mechanics of scrolling through that many items
isn't a great interface. Jon Watte summed it up: “<span class="quote">If you already have the
data in some indexable storage, I don't think you should use a <code class="classname">BListView</code>.
The problem with <code class="classname">BListView</code> ... does not fit well into a framework where
you have alternate storage. Instead, write your own array view. It's not
hard; especially if each item has the same height.</span>
</p><p>
So what's a good 10,000 item displaying GUI paradigm? Some suggestions
were offered, including a background task that reads items in the
direction of the scroll.
</p></div><div class="sect3"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h4 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id731506"></a>Subject: [Q] submenu speed</h4></div></div></div><p>
How do you set the submenu delay, i.e. the times it takes for a submenu
to appear after the mouse touches its initiating menu item?
</p><p>
THE BE LINE: (From Pavel Cisler) You can't in R3—but R4 implements a
whole new callback mechanism to do this properly, stay tuned.
</p></div><div class="sect3"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h4 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id731525"></a>Subject: Joystick feedback</h4></div></div></div><p>
Joystick talk led to a semi-rhetorical poll from Jon Watte: “<span class="quote">What level
of cooked-ness of data should the <code class="classname">BJoystick</code> class provide to your
application? Should it implement things such as scaling of input data,
dead-zone centering, and filtering to eliminate jitter? It seems to me
that you'd be better off with raw-er data, because then your game can
decide what the right thing to do is, but maybe games developers expect
this from the OS?</span>
</p><p>
Sean Gies answers practically: “<span class="quote">There could be a Joystick preferences app
to take the user through the standard calibration routines. That way, I
can always expect to receive a <span class="type">float</span> between -1.0 and 1.0 when I query a
<code class="classname">BJoystick</code> object...Filtering, however, should be left up to us game
developers so that we can determine how 'soft' or 'hard' our game will
feel.</span>
</p><p>
"And I as well think this," spake others.
</p></div><div class="sect3"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h4 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id731576"></a>Subject: Setting the mouse position</h4></div></div></div><p>
Is there anyway to set the mouse position? Will it eat your soul? Most
folks think that setting the mouse position should be rare, but it
shouldn't be prohibited.
</p><p>
THE BE LINE: Use <code class="function">set_mouse_position()</code> and link against
<code class="filename">libgame</code>. It works
even if you aren't displaying a <code class="classname">BWindowScreen</code>.
</p></div></div></div></div><div id="footer"><hr /><div id="footerT">Prev: <a href="Issue3-33.html">Issue 3-33, August 19, 1998</a>  Up: <a href="volume3.html">Volume 3: 1998</a>  Next: <a href="Issue3-35.html">Issue 3-35, September 2, 1998</a> </div><div id="footerB"><div id="footerBL"><a href="Issue3-33.html" title="Issue 3-33, August 19, 1998"><img src="./images/navigation/prev.png" alt="Prev" /></a> <a href="volume3.html" title="Volume 3: 1998"><img src="./images/navigation/up.png" alt="Up" /></a> <a href="Issue3-35.html" title="Issue 3-35, September 2, 1998"><img src="./images/navigation/next.png" alt="Next" /></a></div><div id="footerBR"><div><a href="http://www.haiku-os.org"><img src="./images/People_24.png" alt="haiku-os.org" title="Visit The Haiku Website" /></a></div><div class="navighome" title="Home"><a accesskey="h" href="index.html"><img src="./images/navigation/home.png" alt="Home" /></a></div></div><div id="footerBC"><a href="http://www.access-company.com/home.html" title="ACCESS Co."><img alt="Access Company" src="./images/access_logo.png" /></a></div></div></div><div id="licenseFooter"><div id="licenseFooterBL"><a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/3.0/" title="Creative Commons License"><img alt="Creative Commons License" style="border-width:0" src="https://licensebuttons.net/l/by-nc-nd/3.0/88x31.png" /></a></div><div id="licenseFooterBR"><a href="./LegalNotice.html">Legal Notice</a></div><div id="licenseFooterBC"><span id="licenseText">This work is licensed under a
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/3.0/">Creative
Commons Attribution-Non commercial-No Derivative Works 3.0 License</a>.</span></div></div></body></html>