handling large bitmaps in OpenGL ES and Android - android

I created a map app that uses a very large image as my map. It shows high resolution tiles when the user zooms in to a certain degree and a lower-res bitmap of the whole image when zoomed out past 50%. It works OK, on my origional droid sometimes I can see a very slight lag due to the new tiles being drawn into and from the garbage collector.
Im now thinking about how OpenGL ES would be a better way to render. Ive never touched it before, but from what ive been reading in different turoials, it seems i could create a quad that has 8 faces or so, and enable culling so it only draws the images that are currently visible on the screen.
Would this eliminate the lag completely? In my test app now I have tiles being loaded on app startup but i can tell the lag is due to those tiles being drawn for the first time. Thanks.

I have written an OpenGL-based map view and it's bloody awesome if I say so myself. Sadly it's for a commercial project so I can't offer code. I can however tell you that it has 1 rendering thread, a pool of 8 tile downloading threads, and (most pertinent to your question) 1 storage thread that loads and saves tiles to the NAND flash or the SD card. Rendering is done one tile at a time, each tile being 2 triangles (there are no quads in ES). It's still blisteringly quick.

Related

Optimizing performance in android graphics

I'm creating a maps application that needs to display a very large bitmap of a world map which is bigger than most screens and consumes a lot of memory.
To solve the problem, I'm using a similar idea to google maps and its app by splitting the map into smaller 256x256 pieces and then calculating the pieces that fall in the view area which on my device is 12 pieces at any one time.
The pieces are all stored in the assets folder which get loaded using the assetmanager and then decoded into a bitmap using bitmapfactory and then drawn onto my views canvas.
This is very slow and even after going further in the literature and having it run in a seperate thread, the graphics are jerky when scrolling around to new locations.
How do other games and apps (like google maps) dispaly graphics with such smooth scrolling?
After many attempts it seems that the problem was with the reading from the assets folder. I solved the problem by calculating the maximum number of map pieces visible at one time from the users screen size. The Draw procedure first searches for pieces in the array that are now off the screen and nulls the slots. It then looks for pieces that are now visible and loads them into an empty slot.
The app then uses the array as a source of data and it has reduced the assets folder reads down to 6 as opposed to the direct method loading 20 pieces per Draw.
The game now runs with the speed of popular mobile games!

Displaying and navigating large custom maps offline using Phonegap

My question is how to efficiently display large custom maps in an offline Phonegap application, allowing them to be panned and zoomed smoothly while still supporting older mobile devices?
I’m developing a mobile application that involves using geolocation to navigate walking routes in remote areas where it’s likely the user won’t have a signal and therefore an internet connection. It’s important that the app works well with Android 2.2+ (so SVG is not an option) as well as iOS4+.
I’ve drawn custom vector maps using Adobe Illustrator at resolutions appropriate to each route, the average being about 2000x2000 pixels and the largest of which so far results in an image 4000x2400 pixels.
I’ve chosen to go with Phonegap/JQM rather than native simply because I come from a web programming background and it seemed the fastest way to get a user interface up and running without needing to delve into native code too much, although I’ve written a couple of Phonegap plugins using native code for the purposes of power and screen management.
The application needs to allow the user to pan around the map (by dragging) and zoom in/out (by pinching) between about 25% to 200% of the original image size.
Most of the testing I’ve done has been on an HTC Desire running Android 2.3.3 and an HTC Wildfire running Android 2.2 since these are likely to be some of the lowest spec devices the app is going to have to run on.
I’ve tried out various approaches to display the map (detailed below), but so far each has proved unfit for purpose either because the memory usage of the app is too great, the storage space required makes the app too large to download or the CPU usage is too intensive causing lag when panning/zooming.
Any suggestions much appreciated. Thanks in advance.
Approaches I’ve tried:
1. Display map as raster PNG using tag
This was the first approach I tried. Exporting the 4000x2400 pixel image from Illustrator as a 128 colour PNG-8 resulted in a 746Kb file. I panned the image by absolutely positioning it relative to the viewport and zoomed the image by scaling the width/height attributes of the tag.
The problem with this approach was that even at a 1:1 zoom level, the Android application used 60Mb of RAM for the image and zooming in to 200% caused this to increase 120Mb, causing the app to crash on the HTC Wildfire.
2. Display portions of raster PNG using HTML5 canvas
To avoid the problem of zooming-in causing a proportional increase in memory usage, I tried loading the image via JS then copying the portion of the image to be displayed to a canvas the size of the viewport, something like:
var canvas = $(‘canvas#mycanvas’);
canvas.width = $(window).width;
canvas.height = $(window).height;
...
var img = new Image();
img.src = “map.png”;
...
var context = canvas[0].getContext("2d");
context.drawImage(img, x, y, w, h, 0, 0, canvas.width, canvas.height);
where x,y is the top-left corner within the source image defined by panning
and w,h is the area size within the source image determined by zoom level
The problem here was that large map images were somehow losing quality while in memory (I can only assume there’s some upper memory limit which is resulting in dithering), causing the maps to look distorted in the app: see here for an example screenshot
3. Display map as vector using HTML5 canvas
A bit of Googling led me to discover ai2canvas, an Illustrator plugin that enables you to export artwork as vectors displayed in an HTML5 canvas. The result of the export is an html file containing a chunk of JS which represents all the paths in illustrator as bezier curves. Exporting my 4000x2400 map resulted in a 550Kb html file containing the vector paths.
In my test app, I rendered the entire map to an in-memory canvas (not attached to the DOM) of 4000x2400 pixels, then copied the relevant portions of it to a viewport-sized canvas using context.drawImage() with the in-memory canvas as the source.
On the HTC Wildfire, although the initial render of all the bezier curves to the in-memory canvas took around 2000ms, copying between canvases was fast enough to allow smooth panning and zooming. The problem was when I looked at the memory usage of the app, it was using 120Mb for the in-memory canvas once all the vectors had rendered.
I tried a second approach using the vector map; instead of rendering all the vectors to a large in-memory canvas, I made the app calculate which vector paths were visible within the viewport at the current pan position/zoom level during each drag/pinch event and only draw the visible vectors to the viewport-sized canvas. While this reduced the required memory usage to 10Mb, the CPU cycles required to perform these calculations on every drag/pinch cycle made the app lag so much on the old android phones it was unusable.
4. Display map using offline tiling
Using map tiler, I created PNG tiles for my maps at zoom levels from 25% to 100%. In my test app, I was then able to lazy load the tiles on demand reducing memory usage and producing a smooth pan/zoom experience even on the HTC Wildfire. I thought I’d found the solution until I looked at the size of the APK produced: for my 4000x2400 map, map tiler produced 4Mb of tile images. Most of my maps are around 2000x2000 pixels, resulting in about 2Mb of tiles. The code of my proper application plus the Phonegap overhead is another 2Mb.
My intention is to release a series of apps available on the Android/Apple markets, each with a set of around 10 maps, but with tiling each map weighs in at between 1-4Mb so the resulting app becomes a very large download.
In case this is of interest to anyone else, I solved this by using map tiling in the end, using a tool called pnqnq to create 8-bit PNGs constrained to 256 colours. The resulting set of tiles for my 4000x2000 map was about 800K in size as opposed to 4Mb for PNG-24, which was an acceptable size for assets in my Android and iOS applications.

Loading large textures in AndEngine

In a game we're developing we need to animate a large sprite, of size 485x485 and the animation has about 30 frames. We have a problem animating such artifacts. I have tried some solutions as listed below, but unfortunately we haven't been able to come up with a solution yet.
Tiling
It looks like putting every frame in one big tile is not an option because:
The texture size needs to be a power of two, so it shows up as black on most devices
When I make the texture size a power of two, it becomes too big for most devices too handle
Recommended maximum texture size of AndEngine seems to be 1024x1024.
Seperate sprites
The other option is loading each texture, and thus each frame, seperately and putting it in a Sprite (as described here). This works quite well and toggling the visibility of each sprite at the right time causes the user to see the animation.
The problem with this method is that loading the whole animation takes quite some time. This is not visible when initially loading the game because of the loading screen, but later in the game the animation needs to be changed and the game needs then about 2-3 seconds to load. Putting a loading screen up is not an option.
Loading on seperate thread
I tried to put loading the textures in a seperate, newly created thread, but even while the thread loads the textures the drawing of the game seems to be paused.
Other options?
I don't know any option, and it appears no one else tried to animate a texture greater than 50x50 pixels because it is very difficult to find anyone with a similar case.
My question is: Is it even properly possible to animate large textures in AndEngine?
I think your problem is going to run up against device limitiations, not andengine limitations. Designing for mobile, there are few android devices that could run that.
However, you may be able to come up with an alternative solution using VertexShaders and FramentShaders. This si an important features of Andengine GLES2
Here is an article describing that approach:
http://code.zynga.com/2011/11/mesh-compression-in-dream-zoo/

Hardware Acceleration pre-Honeycomb

I'm playing around with the Android API with a long term prospect of developing a 2D game. Normally, I could live with just using the Canvas for drawing sprites as the graphics for my game. I'd like to be able to perform lots of drawing for visual effects, but it seems that the Android versions prior to Honeycomb (3.0, API level 11) don't support hardware acceleration.
I'm not sure what that exactly means though. I can't get myself round to believe that the drawing is done by the CPU, pixel by pixel !?! If I end up having effects like glow, lens effects etc... I'll be drawing over each pixel quite a few times. Am I right to believe that a typical smartphone CPU will not be able to cope with that at ~30 FPS?
I don't have the luxury to target Android versions >=3.0 as they constitute 8% of the already not SO big Android market. Should I take my time to go the OpenGL way (I'm a beginner at OpenGL)? If I do so, do you think I'll gain anything by overlaying a GLSurfaceView taking care of the effects on top of a custom android view using a Canvas to do the drawing otherwise. Or is it for any reason a bad idea to mix the two?
Oh God yes. Esepecially if you're targetting pre Android 3 devices, going from SurfaceView (with Canvas.drawXxx() calls) to OpenGlSurface works great. Not only do you have faster frames (updates) per second but memory consumption is A LOT better.
Here's some of the points I've noticed:
If you want to do (multi) sprite animations, doing them with the images loaded as OpenGL textures and displayed on an OpenGL quad gives you a lot more memory space. This is because while regular Bitmap objects are capped by the Android-per-process memory limit (which is something like 16-48 Mb, depending on device and Android version), creating OpenGL textures out of those images (and clearing the iamges right after) doesn't have this limitation. You're only limited by the total memory on the device which is A LOT more then 16-48 megs.
Secondly, but still related to this, with Android 2 and below tracking how much memory a Bitmap instance takes is a lot trickier since those intances are not reported against the Java heap memory. They are allocated in some other memory space. In short, another hassle less if you use OpenGL.
Simple animations such as rotating an image become a breeze with OpenGL. You just texture a quad, then roate it any way you want. Equivalent with Sprite animation is to sequentially display different (rotated versions ) of the image. This is better for memory consumption and speed.
If you're doing a 2D-like game, using OpenGL's orthogonal projection not only simplifies a lot of the (useless, in this case) hassle you'd have with a regular OpenGL perspective projection, but it actually alleviates A LOT of the issues you'd get with regular SurfaceView when needing to scale all your graphical elements so they'd look the same size on different screen resolutions/proportions. With OpenGL's ortho projection you effectifelly create a fixed area of desired widht and height and then have OpenGL project it on the device screen area automatically.
It comes without saying that making simple effects such as a pulsating light affecting some graphic element is a lot easier to do with OpenGL (where you just make the light and have it pulsate, and everything is lit accordingly) rather than simulate this with SurfaceView and baked in sprites.
I've actually started a small asteriod defence-like game with SurfaceView and Canvas and then quickly switched to OpenGL for the above mentioned reasons. Long story short, the game now looks better, and while it initially ran at 16 UPS on a Samsung TEOS and 30 UPS on an Optimus LG 2x it now runs at 33 UPS on the Teos and about 80 UPS on the LG 2x.

Android opengles glBindTexture() calls

My game already consists of an atlas the size of 1024x1024 as I am aware that is the max size for a texture. The problem is I need about 1-2 more of these 1024x1024 textures. That should give me 3 glBindTexture() calls which I know is huge performance drag. I have not tested it yet because I am having other problems currently but would this slow my application by a considerable amount? Is there another solution?
If you are worried about any performance loss, I'd recommend you to profile your application so you really can see where your application's bottleneck is, and then come back if it turns out to be horrible.
Three big atlas textures are still better than many small textures that you bind over and over again.
Currently my game has around 6 512x512 texture atlases and I still dont require any loading screens on any phone so far. Although if you are going to do a lot of binding you may want to look into making a Batcher class that will submit all of your vertices to the GPU at one, it will speed up your application considerably

Categories

Resources