Tuesday, October 22, 2019

Horror Vacui

Aristotélēs postulated that Nature abhors a vacuum. There is probably an equivalent postulate for the Internet Age about unused computing resources.

I've been dumbfounded many times about the ridiculous amount of bloat in software.

Be it the 18,000 classes in an iOS app. That is 18,000 classes, mind you! Not 18,000 objects.

Or the enormous big ball of mud that imgur uses to serve an image.

Or the 1.2M lines of code to simply boot an OS. Not the OS it self, mind you, just the boot system that lives on top of the OS.

I think these things grew out of control purely by the grace of fast computers. Moore's law makes them faster, more powerful, and immediately sloppy engineers fill it with a plethora of layered frameworks.

It would be better if all software engineers were forced to use a decade old computer, so that teraflop machines will not be brought to their knees trying to load a webpage or something. Leave the modern and fast machinery to the users and clients, but develop the software on Raspberry Pi's maybe?

So, are you a software engineer? Please surrender your dual GPU, 16 core machine to the bureau of software simplification, and compile, link, test your software on the Raspberry Pi that will be provided to you. No ifs and buts... that Raspberry Pi is vastly more capable than the Apollo 11 Guidance Computer. It will do.

Nature abhors unused RAM and CPU cycles.

Thursday, October 10, 2019

Itch.io achievements.

I got a couple of requests for Hexa Trains to be available from itch.io and not just Steam. At first glance, that should be easy enough, I've published stuff there before.

But with Hexa Trains there is a complication in the fact that the end-goal of the game, is to unlock the Steam Achievements. And without achievements, that goal would be missing.

Itch.io has no Achievement/Leader-board API. More than that, it does not even have the concept of User IDs, nor logging in via the game. So what would achievements on an Itch.io version look like?

I thought "Can you do leader-boards without a log-in system? And then I quickly remembered the 1980s. Getting listed in the leader-boards by writing your three letter initials.

So at the very least, something like this is possible. But in modern times, there is more to record than just a single number for the high score. All sorts of statistics are tracked between sessions. For instance, in my game, the number of rail sections laid down.

So, we still have the problem of tying a player to the statistics. But do so without adding the hurdle of a log in step. And tying the player to a name should be optional, like it was on a PacMan Arcade Cabinet.

Thinking about this problem made me want to see if I could write my own Open Sourced Achievement/Leader-board system. So let's start with the requirements.

  • No log-in stage.
  • No user id.
  • No email address.
  • Name/Initials optional.
  • API similar to existing frameworks (Game Center, Steam, Google Play.)
  • A server to keep track of progress and scores.
  • Don't track between different game titles.

I think something like that could work. Step 1 would be to generate a random number to be used as a unique ID to identify a player for this specific game. All subsequent launches of the game title, should use this ID when reporting scores and achievement progress. There will be no name/initials associated with this ID, until the player chooses to do so.

To preempt any pranksters wanting to be obscene, racist, or otherwise, the optional name should come in the form as 3-letter initials. It is too hard to be obscene in English, using just three letters. (You can be in Dutch, though. But meh.) This removes the need for moderation by humans. (Duplicate initials is not much of a problem. Stats are tracked separately via the ID.) Also, Latin-alphabet only would be a safe choice with respect to obscenities as well. Less expressive means safer.

I also think it would be a good idea to be as lightweight as is conceivably possible. And it doesn't get much lighter-weight than single-UDP-packet fire-and-forget communication. Stat updates could conceivably get lost, as could scores. I think we should just ignore that. If stats are reported in absolute values, it will get corrected in a future report. If it is reported as deltas, meh.

Note that achievements could be a purely local thing. No need for a server. Only high-scores would need to be shared. But for sake of symmetry and versatility, both via a server could have benefits.

There is still the issue of presentation. The player needs to be notified of unlocking an achievement. How? Put that burden 100% on the game developer? A nice thing about Steam/iOS/GooglePlay is that you don't have to think about that as a game developer. You report the stats, the system will tell the player if it is appropriate. But how would you do this without having control over the game's window?

Let me conclude with: what are your thoughts on this? Leave a comment!

Monday, September 30, 2019

Early Results from Google Play Experiment.

I have some early results to share about my Google Play Experiment. To recap: I released a game that requires Android 9+, hence is available for a mere 10% of the devices that use Google Play. So, is store-traffic reduced with -90% as well?

I was baffled to see that store traffic was flat out zero. Worse than that, the title will not show up at all, no matter how you search for it! I have created an un-searchable title!

First order of business, let's verify whether the title actually exists on the store, using the link from the Google Play Developer Console, here:

Ah, phew... it is actually there!

Now, let's search for it in Google Play using it's exact (longish) title:

I guess I should be happy that the first hit is actually on one of my own apps, and not a competitor. But still... the game is not in the search results. It returns 249 results in total, by the way.

Ok, maybe the fans of my older titles will find it by checking out the store page on my company? Let's go to my other game, and click on the "Game Studio Abraham Stolk Inc." link.

Even here, it is not present. Ok, a title that has zero links pointing at it from the Google Play store it self. This is going to be tricky. I need to leverage the existing customer base. So I put an advertisement for Little Crane 2 in my free game 'Six Wheels and a Gun.'

And yes! A big thank you to my first customer from Switzerland. I appreciate your support. So the store is working: you can buy Little Crane 2: Mud Play.

I celebrate every sale. That customer rocks. But hey... wait a minute.... what is that?

Der Nachfolger von "The Little Crane That Could", mit dem Sie im Schlamm graben können.

A German translation of the store page? I never added any translations. The developer console also lets you purchase translations, but I never did that. Who translated this? To me, it seems good enough to be a human translation, not a machine one, by the way.

So there we have it, ladies and gentlemen... A title on Google Play that you cannot search for and cannot find without a direct link taken from the developer console. But somehow, it has been translated.

UPDATE:
I complained to Google. It took them a few weeks, but they have half-way fixed it now. It is now possible to get "Little Crane 2: Mud Play" back as a search result. Sadly, only as the 5th title, even when searching the exact name. But hey, at least it is returned, and listed on my portfolio page.

Wednesday, September 25, 2019

Google Play Experiment.

In 2015, I released a successor to The Little Crane That Could on Steam for Windows, Linux, MacOS.

In the sequel, I wanted to do something special, and decided to make the terrain deformable. This means, you can dig into the terrain, anywhere, and deposit soil anywhere. And not just constrained to height-maps, but a real full 3D terrain that includes caves, overhangs, and such.

An ambitious undertaking, but I think it got there. Being on desktop, instead of mobile, meant that there was more CPU power available. The collision of all those dirt particles against a dynamic terrain is far from cheap.

After publishing the Windows/Linux/Mac versions, I decided to push the iOS hardware and see if the latest iOS devices were up to the task. To my surprise, iOS CPUs were surprisingly powerful, and able to pull it off (although often below 60fps, or even 30fps, but the 2015 devices were able to run it.)

Back then I also tried a quick Android port, but the average Android device was trailing so far behind Apple's performance (floating point calculations), that I could not publish it there. An Android tablet would just be bogged down, unable to compute the simulation at interactive rates.

Now, skip to 2019. Four years later, the Android devices are roughly where iOS devices were in 2015, when it comes to running my (OpenDE based) soil simulation. It is time to revisit the Android port!

For the Android port, I decided to do an experimental release. Since I can only afford to run on fast devices, I thought I would cut down massively on supported devices. For starters, 64 bit ARM only. And I will publish for Android version 9 and newer (which is just a 10% marketshare.) To top it off, only for devices with ES3.1 and higher.

One of the questions I want to answer with this experiment is how Google Play would treat such a title. Let's say Google Play decides to promote your title in the "Similar Titles" section on the store. And let's say, the Google Play store algorithm decides to grant you 1000 slots, or views. Will it simply drop 900 of those slots for customers on incompatible devices? Or will it keep trying to find customers with compatible devices to show your app to?

Or in short.... by reducing your compatibility to 10% of the devices, will you reduce the in-store visibility to 10% of its potential, as well? I honestly do not know. And I don't even know if I can reliably measure it? I guess compare store-stats with my other apps? Or after a while, add Android-8 support and see if store hits increase?

UPDATE: It has been released. But somehow, it is impossible to search for it? It will not even show up under the publisher page of Game Studio Abraham Stolk Inc. This is weird.

Tuesday, September 17, 2019

Sanitizing addresses of your Android NDK application.

In my attempt to squash all bugs, I ran my Android app with clang's address sanitizer. Because the steps are not trivial, I thought I would documents the process here.

This recipe makes some assumptions, though:

  • I'm on Linux.
  • I use AndroidStudio with CMake.
  • I run on the Android emulator. (A real device shouldn't be much different.)

First, we need to pass the -fsanitize=address flag to both the compiler and the linker. To compile your C/C++ code with this flag, use this in your CMakefile

target_compile_options(foo PRIVATE
        "$<$<CONFIG:DEBUG>:-fsanitize=address>"
)

And to link, add "-fsanitize=address" to your target_link_libraries.

Ok, now your shared-object has been built with the address sanitizer. But if you try to run it, you will see: dlopen failed: library "libclang_rt.asan-i686-android.so" not found.

Luckily, the Android NDK comes with the correct libraries, you just need to package them with your build. First locate where this library is on your system, doing:

$ locate libclang_rt.asan-i686-android.so
/home/bram/Android/Sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/8.0.7/lib/linux/libclang_rt.asan-i686-android.so

You simply need to copy this alongside your own shared object file, as generated by cmake. In my case, it goes in: ./app/build/intermediates/cmake/debug/obj/x86/libclang_rt.asan-i686-android.so

If you now build and run your app on the emulator, your addresses will get sanitized. In my first run, it found a free() of a pointer that was never malloc()-ed in the Google Play Games SDK.

Note that if you want to do this on a ARM device, you would need libclang_rt.asan-arm-android.so and on a 64-bit ARM device, look for libclang_rt.asan-aarch64-android.so instead.

Thursday, September 12, 2019

Keeping user directory names out of your binary.

So, if you use ASSERT macros a lot, you typically want to point to the offending line of code, and use __LINE__ and __BASE_FILE__ macros for that.

Now, where it gets tricky: VisualC does not have __BASE_FILE__ so what I did was use __FILE__ instead. But that has the nasty side effect of putting your home directory in your build, like so: C:\Users\bram\src\.. which is bad practice.

Stackoverflow.com to the rescue. I found this neat solution for it.



UPDATE

Gregory warned about this affecting parallel builds. Personally, I do see all cores used for my single project solution, but your mileage may vary. An alternate approach would be to ditch VisualC compiler, and use clang instead.

Monday, September 2, 2019

High volume, low friction art work for video games.

I am a one-man indie game studio (I like to use the term INDIEvidual.) The lack of employees means that I need to shoulder all the tasks, including art work. With so many tasks, I can not afford to spend a lot of time on it. Here are some tricks to make it manageable.

So many pictograms!

One tedious type of Art related tasks, is creating pictograms for Achievements, or Leader-boards. Here you see some (very minimal) pictograms for four such achievements. To illustrate the volume of work involved, imagine that the game has 100 achievements. How is a part-time artists supposed to generate the 200 images required for this?

It starts with the right Art style. Steam wants these in 64x64 pixels, and believe me, you do not want to do this art as pixel-art in Photoshop. Instead, it will have to be vector art, which is rendered at the required 64x64 resolution. Additionally, this will have to be low-poly vector art. I also chose a two-colour palette to reduce the work even more.

Ok, fine, now it comes down to generate vector images in low-poly style, and just two colours. Massively easier, but still a crazy amount of work.

This requires a ruthlessly reduced work-flow.

Open Source to the rescue, and more specifically, Inkscape to the rescue.

With Inkscape, we can put all our pictograms in just one SVG file, each pictogram in a separate layer. Never opening/closing/copying files, just work in that one achievements.svg file, and create layers.

With everything in one file, Inkscape will let you invoke an export to PNG from the command-line, and pick each layer in isolation for each export command. This is achieved with the --export-id= flag and the --export-id-only flag.

To identify each layer, we need to name the layer object, and set its ID. Unfortunately, renaming the layer is not enough, you need to use the XML-edit widget in Inkscape to actually set the ID of the layer object, as shown below.

Command Line Conversions

For the actual generation of the PNG files, we can leverage a UNIX-style Makefile like so:
IMAGES = \
 a_pentdrive.png \
 a_coaster.png \
 a_millennialbuilder.png \
 a_eagerlearner.png \

all: $(IMAGES)


%.png: achievements.svg
 inkscape --export-id=$(basename $@) --export-id-only --export-png=$@ --export-area-page --export-width=64 --export-height=64 $<
 convert -colorspace Gray $@ lo-$@

In this example, a single SVG file with 4 layers is used as input, and the Makefile will generate 4 pictograms as PNG, and 4 additional grey-scale pictograms to serve as the images for uncompleted achievements. Note that to generate the grey-scale, the Makefile uses the Imagemagick convert command.

Before adopting this workflow, I would export the PNGs using the Inkscape GUI. Sure, doable if you have 4 achievements, but now, with this streamlined workflow, I can pump out a 100 achievements, as 200 images, in a single day, if I have to. Just draw them all in a crude Art style, list them in the Makefile, and run a single make command. And now, the bottleneck (apart from the drawing of the pictograms) is the actual uploading to the steam portal, as I still have to do that as one image at a time.

Future Work

It would make sense to auto-generate the IMAGES list in the Makefile from the SVG itself. Just search the SVG for layer objects. I should also look into the Steam portal, if it allows me to batch-upload the pictograms somehow, without going through the WEB interface.

Wednesday, July 17, 2019

OpenGL debugging under GNU/Linux.

For debugging OpenGL under GNU/Linux, there are three tools that are very similar to each other. They can capture the stream of OpenGL commands, and let you examine the state at each command. These are the tools:

Renderdoc

Renderdoc probably has the easiest interface, and is quite powerful in its capabilities. It lets you view both input and output meshes from your vertex shader, for instance. I do hit on what seems to be a bug in renderdoc if I use uniform buffers, though.

Intel Graphics Performance Analyzer

Intel GPA comes with a graphics frame analyzer. The capturing and analysis of a frame is done with two different commands, though. To capture, run gpa-monitor to launch your app and press Ctrl-Shift-C to capture a frame. Then quit the app and monitor, and run frame-analyzer.

NVidia Nsight Graphics

To capture with NVidia Nsight Graphics, do:

$ cd NVIDIA-Nsight-Graphics-2019.2/host/linux-desktop-nomad-x64/
$ ./nv-nsight-gfx

Choose "Quick Launch" and select "Capture for Live Analysis" in the nsight UI when your app is running.

Thursday, May 23, 2019

Do Space Aliens have Fiscal Quarters?

So, my Hexa Trains indie game features some outlandish planets with freaky colours. Obviously, they cannot represent Earth, as the continents are all wrong too. So, railroad building on Alien Worlds, it is then.

So, when the time came to implement loan interest for the game, it made me realize I need some time-accounting for my game. An extra-solar planet will still have years (as it orbits a star) and days (as it will likely spin as it orbits.) So years, and days. But what about monthly interest payments?

Well... there it breaks down. A random alien planet does not have months, and even if it does, there wouldn't be 12 of them. For starters, a random planet may or may not have moons. And if it does, the moon orbit will not be an integral number of times faster than the planet's orbit.

So no moon then. But what about seasons? Yes... our Alien Planet will most likely have seasons, because the spin axis is probably not aligned with the orbit axis. This means that there will be a spring, summer, autumn and winter. Nice!

And because our Alien Planet has four seasons, which are perfectly in sync with the orbit (exactly 4 seasons per year) we can reasonably assume that our aliens are familiar with dividing their year into four equal parts.

And there you have it, ladies and gentlemen... Our Aliens will probably track corporate performance on a quarterly basis. And hence, will have fiscal quarters.

So, this means that I will implement my economic simulation in such a way that interest on debts is charged four times a year, and revenue/investments/operational-costs/assets are tracked on a quarterly basis.

Wednesday, May 1, 2019

My magic bullet for writing multi-threaded code.

When doing my game development there is often a task or two that are just a little too computationally intensive for a smooth framerate. And I put my framerate targets very ambitious: typically 120Hz for proper operation on an iPad PRO. At 120Hz, there is only 8.33ms to do everything: rendering, simulation, ai, physics, etc. So the larger computations that threaten to exceed this, need to be moved off the main thread.

Examples off things that I have computed on their own threads, in my previous projects include: AI action planning, iso surface generation to simulate deformable terrain, crowd flow and path finding.

The biggest hurdle to take when doing multi threaded code, is to avoid race conditions. If two threads are writing to something, which write will persist? Or if a thread reads and another writes, will the reader see the old or the new value? Tricky stuff.

I've found a neat trick to make this whole MT programming thing a lot more manageable. And frankly, after repeatedly using it, I've come to regard it as some sort of magic bullet. In this blog post, I will explain my approach in the hope that it will be useful to other (game) developers.

So the mechanism of choice for me can be summed up in one sentence: concentrate all the synchronization + semaphores + condition variables in just one place: a thread-safe work queue.

This thread-safe queue is then used to set up a producer / consumer system for units of work. The producers live on the main thread and spec well defined pieces of work that need to be performed. Those work specs are put in the queue.

The consumers of these jobs live on worker threads, with one consumer for each thread, and often one thread per CPU core. The job is consumed and the worker thread goes to work, thereby performing the outsourced service that the producer on the main thread did not want to do itself.

Note that the task-queue consumers are the actual service-providers, and that the task-queue producers are the clients of these services that want the computation out-sourced to another thread.

To communicate back the results of the computations, I use nothing fancy. All I do is have the worker write a boolean in memory to signal that the specific work was done. This is not protected by any construct, because there is a well defined order in which things happen: The worker (and only the worker) writes the boolean. The client on the main thread (and only that client) reads the boolean to see if work is done. I do this polling once every simulation frame. Every 'entity' in the simulation that has work outstanding, checks the boolean for completion once per frame, and if it is set, it can safely, use the results that have been stored in main memory. If a client just misses the completion, no biggie! The next simulation frame, it will be picked up.

Note that the magic bullet comes with one big drawback, which may or may not be a big deal in your personal case: You lose determinism. But frankly, deterministic code is elusive anyway. For instance, there is hidden precision in the FPU registers that may be set randomly, so in practice deterministic floating point code is not possible anyway.

The actual implementation of a thread safe queue is beyond the scope of this article, but it involves one mutex, and two condition variables. One condvar to signal that the queue is not empty (wakes up consumers) and one condvar to signal that the queue is not full (wakes up producers.) Of course the queue depth needs to be large enough so that it is never full, because a full queue would temporarily freeze your main thread.

Finally, I want to stress that this approach does not absolve you from being careful. You still need to make sure that a thread is not overwriting nilly willy in the main memory. But the fact that you can safely communicate the work that is req'd and the work that is completed, is at least half the battle.

Tuesday, February 12, 2019

Unreal

After a successful exploration of unity earlier, I though I would test-drive Unreal Engine 4 to see what that is all about. Here are some random observations I made:

  • For linux, the engine and editor have to be built from source. Which is fine, but it does take an hour, and 60Gbyte of disk space.
  • First time starting the editor takes a very long time, and comes with scary warnings to boot.
  • Once you actually try to launch a sample, everything is very slow. Thousands of shaders need to be compiled.
  • Be careful with that quality slider. If you change it from "High" to "Medium" be prepared for another lengthy shader recompilation step.
  • The vulkan renderer would crash at startup with an out-of-memory error. But switching to OpenGL renderer, helped.
  • Out of box performance when launching the "Advanced Vehicle" template was dreadful. I estimate the FPS well below 10 for that. I need to figure out if this is a GPU or CPU bottleneck. Although performance is a lot better if I first quit the editor, and then start the demo application by itself.

Wednesday, February 6, 2019

Crashing apps.

I'm on a mission to reduce my Android game's crashes as much as possible. And it turns out it is incredibly hard to approach a zero crash rate.

First off, I would like you to consider this: If 1% of today's users of your app experienced a crash today, is that bad?

Well, according to Google's metrics system, it gets labelled 'bad' as soon as you exceed 1.09% of users. Which is kind of a weirdly specific number. I don't know where that number comes from.

I love the Google Play Developer Console. It's so much better than any other developer console I've used. These include Valve, Apple and Amazon's developer sites. Google Play defines the crash rate as follows: Crash Rate: Percentage of daily sessions during which your users experienced at least one crash. A daily session refers to a day during which your app was used.

So here you can see how my quest for zero crashes is progressing. The trend looks good:

Now the "Benchmark" is interesting... they compare the crash rate of your app, to a game-category. And the Google Play Developer Console interface let's you change it to a different benchmark. By comparing my game to all the different benchmarks, I could do a comparative study of crash rates in different categories! With out further ado, here's how likely a game of a given genre is to crash:

One thing that stands out to me, is how high these levels are. Crashes in Android apps are actually quite common. If I had to guess, a lot of it could be attributed to fragmentation of the Android devices. So much diversity, and limited means of testing them. Although I have to give a shout-out to the incredibly useful "Pre Launch Reports" that Google offers. The team that does the Developer Console is one hell of a team.

Another source of frustration is the dubious quality of Android's frameworks. Especially the NDK native frameworks. If your app crashes beyond your control in a buggy In-App-Billing framework, or Google-Play-Games framework, there is little you can do.

Still, it is an interesting experiment to see how low I can get my crash rate. Stay tuned for updates on my progress towards my goal of zero crashes.

Thursday, January 31, 2019

Callstack

This call stack, captured from Google Play for one of my apps, shows what's fundamentally wrong with Android.

The app didn't even finish launching, and this is how deep the complexity runs?

backtrace:
  #00  pc 00000000000264f4  /system/lib/libharfbuzz_ng.so (hb_ot_layout_has_glyph_classes+28)
  #01  pc 0000000000049af0  /system/lib/libharfbuzz_ng.so (_hb_ot_shape+192)
  #02  pc 000000000001e008  /system/lib/libharfbuzz_ng.so (hb_shape_plan_execute+140)
  #03  pc 000000000001db30  /system/lib/libharfbuzz_ng.so (hb_shape+84)
  #04  pc 0000000000011b05  /system/lib/libminikin.so (minikin::Layout::doLayoutRun(unsigned short const*, unsigned int, unsigned int, unsigned int, bool, minikin::MinikinPaint const&, minikin::StartHyphenEdit, minikin::EndHyphenEdit)+3780)
  #05  pc 0000000000013991  /system/lib/libminikin.so (minikin::LayoutCacheKey::doLayout(minikin::Layout*, minikin::MinikinPaint const&) const+148)
  #06  pc 0000000000010bf5  /system/lib/libminikin.so (void minikin::LayoutCache::getOrCreate(minikin::U16StringPiece const&, minikin::Range const&, minikin::MinikinPaint const&, bool, minikin::StartHyphenEdit, minikin::EndHyphenEdit, minikin::LayoutAppendFunctor&)+468)
  #07  pc 0000000000010717  /system/lib/libminikin.so (minikin::Layout::doLayoutWord(unsigned short const*, unsigned int, unsigned int, unsigned int, bool, minikin::MinikinPaint const&, unsigned int, minikin::StartHyphenEdit, minikin::EndHyphenEdit, minikin::LayoutPieces const*, minikin::Layout*, float*, minikin::MinikinExtent*, minikin::MinikinRect*, minikin::LayoutPieces*)+234)
  #08  pc 000000000000fcd5  /system/lib/libminikin.so (minikin::Layout::doLayoutRunCached(minikin::U16StringPiece const&, minikin::Range const&, bool, minikin::MinikinPaint const&, unsigned int, minikin::StartHyphenEdit, minikin::EndHyphenEdit, minikin::LayoutPieces const*, minikin::Layout*, float*, minikin::MinikinExtent*, minikin::MinikinRect*, minikin::LayoutPieces*)+1160)
  #09  pc 0000000000010609  /system/lib/libminikin.so (minikin::Layout::measureText(minikin::U16StringPiece const&, minikin::Range const&, minikin::Bidi, minikin::MinikinPaint const&, minikin::StartHyphenEdit, minikin::EndHyphenEdit, float*, minikin::MinikinExtent*, minikin::LayoutPieces*)+1292)
  #10  pc 000000000022ca17  /system/lib/libhwui.so (android::MinikinUtils::measureText(android::Paint const*, minikin::Bidi, android::Typeface const*, unsigned short const*, unsigned int, unsigned int, unsigned int, float*)+82)
  #11  pc 00000000000e1cd5  /system/lib/libandroid_runtime.so (android::PaintGlue::getRunAdvance___CIIIIZI_F(_JNIEnv*, _jclass*, long long, _jcharArray*, int, int, int, int, unsigned char, int)+204)
  #12  pc 0000000000a4758b  /system/framework/arm/boot-framework.oat (android.graphics.Paint.nGetRunAdvance+178)
  #13  pc 0000000000a48e0d  /system/framework/arm/boot-framework.oat (android.graphics.Paint.getRunAdvance+172)
  #14  pc 0000000000a48cab  /system/framework/arm/boot-framework.oat (android.graphics.Paint.getRunAdvance+250)
  #15  pc 0000000001482e99  /system/framework/arm/boot-framework.oat (android.text.TextLine.getRunAdvance+128)
  #16  pc 0000000001483d89  /system/framework/arm/boot-framework.oat (android.text.TextLine.handleText+312)
  #17  pc 0000000001483367  /system/framework/arm/boot-framework.oat (android.text.TextLine.handleRun+566)
  #18  pc 0000000001484f49  /system/framework/arm/boot-framework.oat (android.text.TextLine.measure+216)
  #19  pc 0000000001485bcd  /system/framework/arm/boot-framework.oat (android.text.TextLine.metrics+44)
  #20  pc 00000000017ac2c9  /system/framework/arm/boot-framework.oat (android.text.BoringLayout.isBoring+384)
  #21  pc 0000000001af2d8f  /system/framework/arm/boot-framework.oat (android.widget.TextView.onMeasure+430)
  #22  pc 000000000180ff23  /system/framework/arm/boot-framework.oat (android.view.View.measure+826)
  #23  pc 0000000001a7c3bb  /system/framework/arm/boot-framework.oat (android.view.ViewGroup.measureChildWithMargins+178)
  #24  pc 0000000001c900a1  /system/framework/arm/boot-framework.oat (android.widget.LinearLayout.measureChildBeforeLayout+64)
  #25  pc 0000000001c904ed  /system/framework/arm/boot-framework.oat (android.widget.LinearLayout.measureHorizontal+1060)
  #26  pc 0000000001c91f41  /system/framework/arm/boot-framework.oat (android.widget.LinearLayout.onMeasure+64)
  #27  pc 0000000001f4f553  /system/framework/arm/boot-framework.oat (com.android.internal.widget.ButtonBarLayout.onMeasure+226)
  #28  pc 000000000180ff23  /system/framework/arm/boot-framework.oat (android.view.View.measure+826)
  #29  pc 0000000001d3747d  /system/framework/arm/boot-framework.oat (android.widget.ScrollView.measureChildWithMargins+228)
  #30  pc 0000000001c87141  /system/framework/arm/boot-framework.oat (android.widget.FrameLayout.onMeasure+296)
  #31  pc 0000000001d384f7  /system/framework/arm/boot-framework.oat (android.widget.ScrollView.onMeasure+46)
  #32  pc 000000000180ff23  /system/framework/arm/boot-framework.oat (android.view.View.measure+826)
  #33  pc 0000000001f4eaf3  /system/framework/arm/boot-framework.oat (com.android.internal.widget.AlertDialogLayout.tryOnMeasure+434)
  #34  pc 0000000001f4f13f  /system/framework/arm/boot-framework.oat (com.android.internal.widget.AlertDialogLayout.onMeasure+46)
  #35  pc 000000000180ff23  /system/framework/arm/boot-framework.oat (android.view.View.measure+826)
  #36  pc 0000000001a7c3bb  /system/framework/arm/boot-framework.oat (android.view.ViewGroup.measureChildWithMargins+178)
  #37  pc 0000000001c87141  /system/framework/arm/boot-framework.oat (android.widget.FrameLayout.onMeasure+296)
  #38  pc 000000000180ff23  /system/framework/arm/boot-framework.oat (android.view.View.measure+826)
  #39  pc 0000000001a7c3bb  /system/framework/arm/boot-framework.oat (android.view.ViewGroup.measureChildWithMargins+178)
  #40  pc 0000000001c87141  /system/framework/arm/boot-framework.oat (android.widget.FrameLayout.onMeasure+296)
  #41  pc 000000000180ff23  /system/framework/arm/boot-framework.oat (android.view.View.measure+826)
  #42  pc 0000000001a7c3bb  /system/framework/arm/boot-framework.oat (android.view.ViewGroup.measureChildWithMargins+178)
  #43  pc 0000000001c87141  /system/framework/arm/boot-framework.oat (android.widget.FrameLayout.onMeasure+296)
  #44  pc 0000000001d65865  /system/framework/arm/boot-framework.oat (com.android.internal.policy.DecorView.onMeasure+1252)
  #45  pc 000000000180ff23  /system/framework/arm/boot-framework.oat (android.view.View.measure+826)
  #46  pc 00000000018368f7  /system/framework/arm/boot-framework.oat (android.view.ViewRootImpl.performMeasure+246)
  #47  pc 00000000018353ed  /system/framework/arm/boot-framework.oat (android.view.ViewRootImpl.measureHierarchy+500)
  #48  pc 000000000183733b  /system/framework/arm/boot-framework.oat (android.view.ViewRootImpl.performTraversals+2330)
  #49  pc 000000000183dbb5  /system/framework/arm/boot-framework.oat (android.view.ViewRootImpl.doTraversal+188)
  #50  pc 0000000000d5e83d  /system/framework/arm/boot-framework.oat (android.content.ContextWrapper.getBasePackageName [DEDUPED]+52)
  #51  pc 0000000001515df7  /system/framework/arm/boot-framework.oat (android.view.Choreographer.doCallbacks+966)
  #52  pc 0000000001516675  /system/framework/arm/boot-framework.oat (android.view.Choreographer.doFrame+1396)
  #53  pc 00000000017d6413  /system/framework/arm/boot-framework.oat (android.view.Choreographer$FrameDisplayEventReceiver.run+66)
  #54  pc 000000000130e659  /system/framework/arm/boot-framework.oat (android.os.Handler.dispatchMessage+64)
  #55  pc 0000000001313e8b  /system/framework/arm/boot-framework.oat (android.os.Looper.loop+1162)
  #56  pc 0000000000c905d3  /system/framework/arm/boot-framework.oat (android.app.ActivityThread.main+674)
  #57  pc 0000000000417575  /system/lib/libart.so (art_quick_invoke_stub_internal+68)
  #58  pc 00000000003f125f  /system/lib/libart.so (art_quick_invoke_static_stub+222)
  #59  pc 00000000000a1043  /system/lib/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+154)
  #60  pc 000000000035093d  /system/lib/libart.so (art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*)+52)
  #61  pc 0000000000351d85  /system/lib/libart.so (art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned int)+960)
  #62  pc 0000000000302b15  /system/lib/libart.so (art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*)+40)
  #63  pc 00000000006ab577  /system/framework/arm/boot-core-oj.oat (java.lang.Class.getDeclaredMethodInternal [DEDUPED]+110)
  #64  pc 000000000165417b  /system/framework/arm/boot-framework.oat (com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run+114)
  #65  pc 000000000165da49  /system/framework/arm/boot-framework.oat (com.android.internal.os.ZygoteInit.main+2896)
  #66  pc 0000000000417575  /system/lib/libart.so (art_quick_invoke_stub_internal+68)
  #67  pc 00000000003f125f  /system/lib/libart.so (art_quick_invoke_static_stub+222)
  #68  pc 00000000000a1043  /system/lib/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+154)
  #69  pc 000000000035093d  /system/lib/libart.so (art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*)+52)
  #70  pc 0000000000350759  /system/lib/libart.so (art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)+304)
  #71  pc 000000000029493d  /system/lib/libart.so (art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+476)
  #72  pc 000000000006d819  /system/lib/libandroid_runtime.so (_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+28)
  #73  pc 000000000006f9cf  /system/lib/libandroid_runtime.so (android::AndroidRuntime::start(char const*, android::Vector const&, bool)+466)
  #74  pc 0000000000001b21  /system/bin/app_process32 (main+880)
  #75  pc 00000000000a2b3d  /system/lib/libc.so (__libc_init+48)
  #76  pc 000000000000176f  /system/bin/app_process32 (_start_main+46)
  #77  pc 0000000000019da9  /system/bin/linker (__dl__ZN6soinfoD1Ev+16)
  #78  pc 00000000007fea64  [stack:ff3b8000]

Wednesday, January 16, 2019

Unity

Two days ago, I started using the Unity3D game development tool. Interestingly, Unity3D is the most pervasive tool in the Game Dev industry... it is everywhere. I've been making games since 1982, but have never used it before.

I'm one of those dinosaurs keeping to the old way of doing things, and making my own game engines. Think of a manual 3-gear transmission Willys Jeep instead of heated seats, 7 cup-holder, navsat SUV.

So far, I like it better than I thought I would. In this blog article, I will collect some gotchas, insights, surprises that I will undoubtedly encounter.

1. Project loads are slow. Once inside the editor, make sure you stop "Playback" before editing anything... all edits during playback will be lost.

2. Unity uses degrees, not radians for angles.

3. You can only use const for compile-time constants. Not for run-time constants, like C and C++.

4. Unity physics is a wrapper over PhysX. Neither uses ERP and CFM like OpenDE and Bullet Physics do.

5. Unity debug output prints vectors with low precision. So (0.0, 0.3, 1.0) can actually be unit length!

6. My bicycle wheel would not spin faster than 7 radians per second, no matter how much torque I applied to it. It turns out that there is a rigid body property that caps it.

7. There is no include directive in the C# language. Instead, files that using the same namespace will automagically find each other's definitions.

8. Centre of mass and inertia tensor are automatically computed from the collision geometry! Beware of this: simulation results will be different just by changing collision geometry, unless you set c.o.m. and inertia explicitly.

9. Monobehaviour.Awake() is called in random order for the Gameobjects. Ugh.

Saturday, January 12, 2019

Sprinkle, Sprinkle, Little Star.

I released a new software application. It is named "Sprinkle, Sprinkle, Little Star" and is available for free for Windows and Linux.

It is a 2D simulation of Gravity acting on a galaxy of stars. It is completely interactive, and you can paint your own galaxy onto a grid, and see the galaxy evolve under Newton's law for gravity.

I recorded a video of myself with a play-though.

I started it during the holiday break, and had fun implementing it. The so called N-Body problem is pretty hard to solve interactively for large numbers of stars, because of its O(N*N) nature: Each star is influenced by each other stars. This means calculating 100M distances if you have 10,000 stars. To do it efficiently, I had to aggregate stars at larger distances. In addition to this, I vectorized my code with AVX intrinsics, that calculates 8 forces in a single go using SIMD. Because my compiler did such a bad job on the scalar code, the speed up was even better than 8, I saw a x18 increase in framerate (which also included rendering, so the computation speed up was even a little more than 18x.) Below is the spatial structure I use to aggregate stars at large distances.