Pango/Android -vs- Pango/NaCl
At the end of my Sugar/Android week, I had a simple
Pango-on-Cairo demo running. This was built on a stack
of ported libraries, including gettext, pixman,
freetype, libxml2, fontconfig, and glib,
as well as cairo and pango. You can run the demo
yourself by sideloading pango-demo.apk onto your Android
device (tested on a Motorola Xoom), and you can browse the
source code to see what it entailed (here's the scariest
part). (I was inspired by Akita Noek's android-cairo project,
but I ended up reworking the build scheme and redoing most of the
ports.)
It made sense to start my Sugar/NaCl investigation by porting
the same demo application to Native Client. The same stack of ported
libraries was involved, although it was easy to include more
functionality in the Native Client ports, including threading and
PNG/PS/PDF support in cairo. The source code is a fork from
the upstream naclports project, and the process was generally much cleaner.
(But see my previous post for some caveats regarding naclports.)
If you're using Chrome 10 or 11, you can run the demo in your
browser (follow the instructions on that page). The
Wesnoth team has a parallel project which ported some of these libraries as well, but
not in an upstreamable manner.
The demo app uses cairo to draw the background, an animated X, and
some basic text in the center; it uses Pango's advanced international
text support to draw properly-shaped Persian text in a circle
around it. The center text is the "proper" bilingual Greek/Japanese
written form of "pango"; the text around the edges is the Persian name of
the internationalization library, "harfbuzz". Note that the Persian
text is written right-to-left—and that I didn't put a full CJK
font in the NaCl app, so the Japanese "go" character is missing.
The Android port rebuilds the font cache at each startup, so it loads
rather slowly; the NaCl port contains a prebuilt font cache so it starts
more quickly.
Both ports took about two weeks. I blew my original schedule,
partly due to the Patriot's day holiday, and partly because I'd given
Android about a week's head start by tinkering on it before my
original schedule post. The framerate of the demo is much better on
NaCl (so fast that the edges of the animated X look choppy in the
screenshot), but the hardware isn't easily comparable, so the
comparison doesn't really tell us much. The porting effort was
certainly more pleasant on NaCl, since newlib is a much more complete
libc than Android's "Bionic"—but having gdb available made
debugging on Android easier. (There is an unintegrated NaCl branch that
integrates NaCl gdb in the browser, though!)
Much of the GNOME/POSIX library stack assumes access to a filesystem
tree and does file-based configuration. In our demo application,
fontconfig was the most culpable party: it wanted to load a
configuration file describing font locations and naming, then to load
the fonts themselves from the file system, and finally to write a
cache file describing what it found back to the file system. Most
ported software is going to want similar access—even if you store
the user's own documents in a Journal, software still expects
to find configuration, caches, and other data in a filesystem.
Android provides the POSIX filesystem APIs, but the filesystem an app can
touch is segmented and sandboxed. As discussed previously, Android's
Opaque Binary Blob feature may allow you to create a app-specific
filesystem, but this doesn't let you share (for example) fonts and
font configuration between activities. NaCl might eventually provide
a similar unshared mechanism based on the HTML5 AppCache.
The preferred solution is more limited, but more flexible: no built-in
filesystem APIs are used (or in NaCl's case, provided!) at all.
Instead, you provide your own implementation of the POSIX file APIs
(either via the --wrap linker indirection or through an appropriate
backend to newlib/glibc/glib). For the NaCl demo app, I wrote a
rather-elaborate in-memory filesystem --- only to find that an
even-more-elaborate one already existed in naclports. But the
longer-term solution uses message-passing (SRPC in NaCl, intents in
Android) to implement these POSIX APIs. In Native Client, the
implementation would be in browser-side JavaScript, which would then
allow you to share parts of the filesystem tree between activities
and/or map it into (cached) web-addressed resources. In either case,
your application still sees the bog-standard POSIX API it expects.
More problematic are the networking APIs. Here Android provides
a pretty standard socket library, while Native Client provides
nothing at all. Using a browser-based implementation, as for
the file APIs, will work fine for HTTP, WebSockets and even P2P via the
HTML5 P2P APIs. But it's not clear that (for example) glib's
elaborate asynchronous DNS name resolver implementation can (or
should!) be implemented in a NaCl port.
In the end, the porting effort and abstraction shifts needed for
Native Client and Android are roughly comparable. I expect
Native Client will hold a strong edge in allowing close
integration with web standards and web technologies. Android will
probably continue to hold an edge in third-party application support and
platform maturity.
Syndicated 2011-04-29 15:08:56 from C. Scott Ananian